From 2443d477941b6189906882772307e15e5471ba43 Mon Sep 17 00:00:00 2001 From: weolar Date: Wed, 25 Sep 2024 16:28:10 +0800 Subject: [PATCH] =?UTF-8?q?*=20=E6=A0=BC=E5=BC=8F=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- node/lib/fs.js | 54 +- node/lib/internal/bootstrap_node.js | 2 +- node/lib/timers.js | 15 +- node/nodeblink.cpp | 618 +- node/nodeblink.h | 13 + node/src/async-wrap-inl.h | 46 +- node/src/async-wrap.cc | 571 +- node/src/async-wrap.h | 131 +- node/src/backtrace_posix.cc | 53 +- node/src/backtrace_win32.cc | 5 +- node/src/base-object-inl.h | 82 +- node/src/base-object.h | 81 +- node/src/base64.h | 280 +- node/src/bootstrap_node_js.h | 1984 ++-- node/src/cares_wrap.cc | 2556 ++--- node/src/connect_wrap.cc | 9 +- node/src/connect_wrap.h | 16 +- node/src/connection_wrap.cc | 167 +- node/src/connection_wrap.h | 43 +- node/src/debug-agent.cc | 526 +- node/src/debug-agent.h | 156 +- node/src/env-inl.h | 861 +- node/src/env.cc | 122 +- node/src/env.h | 1188 +- node/src/fs_event_wrap.cc | 266 +- node/src/handle_wrap.cc | 159 +- node/src/handle_wrap.h | 69 +- node/src/http_parser.c | 3654 +++---- node/src/http_parser.h | 359 +- node/src/inspector_agent.cc | 1758 +-- node/src/inspector_agent.h | 47 +- node/src/inspector_socket.cc | 1226 ++- node/src/inspector_socket.h | 182 +- node/src/js_stream.cc | 341 +- node/src/js_stream.h | 60 +- node/src/node.h | 577 +- node/src/node1.cc | 6303 ++++++----- node/src/node_api.cc | 3957 +++---- node/src/node_api.h | 787 +- node/src/node_api_types.h | 166 +- node/src/node_buffer.cc | 2252 ++-- node/src/node_buffer.h | 115 +- node/src/node_config.cc | 41 +- node/src/node_constants.cc | 605 +- node/src/node_constants.h | 49 +- node/src/node_contextify.cc | 1503 ++- node/src/node_counters.cc | 144 +- node/src/node_counters.h | 54 +- node/src/node_crypto.cc | 9731 +++++++++-------- node/src/node_crypto.h | 1346 +-- node/src/node_crypto_bio.cc | 717 +- node/src/node_crypto_bio.h | 246 +- node/src/node_crypto_clienthello-inl.h | 82 +- node/src/node_crypto_clienthello.cc | 336 +- node/src/node_crypto_clienthello.h | 207 +- node/src/node_crypto_groups.h | 722 +- node/src/node_dtrace.cc | 364 +- node/src/node_dtrace.h | 39 +- node/src/node_etw_provider.h | 28 +- node/src/node_file.cc | 2750 ++--- node/src/node_file.h | 6 +- node/src/node_http_parser.cc | 1172 +- node/src/node_http_parser.h | 6 +- node/src/node_i18n.cc | 279 +- node/src/node_i18n.h | 12 +- node/src/node_internals.h | 409 +- node/src/node_javascript.cc | 69 +- node/src/node_javascript.h | 6 +- node/src/node_lttng.cc | 353 +- node/src/node_lttng.h | 36 +- node/src/node_lttng_provider.h | 124 +- node/src/node_lttng_tp.h | 4 +- node/src/node_main.cc | 86 +- node/src/node_mutex.h | 264 +- node/src/node_natives.h | 354 +- node/src/node_node.cc | 3957 +++++++ node/src/node_object_wrap.h | 184 +- node/src/node_os.cc | 704 +- node/src/node_perfctr_provider.h | 24 +- node/src/node_revert.cc | 72 +- node/src/node_revert.h | 13 +- node/src/node_root_certs.h | 7706 ++++++------- node/src/node_stat_watcher.cc | 141 +- node/src/node_stat_watcher.h | 41 +- node/src/node_url.cc | 4389 ++++---- node/src/node_url.h | 370 +- node/src/node_util.cc | 226 +- node/src/node_v8.cc | 219 +- node/src/node_version.h | 38 +- node/src/node_watchdog.cc | 402 +- node/src/node_watchdog.h | 123 +- node/src/node_win32_etw_provider-inl.h | 383 +- node/src/node_win32_etw_provider.cc | 232 +- node/src/node_win32_etw_provider.h | 70 +- node/src/node_win32_perfctr_provider.cc | 415 +- node/src/node_win32_perfctr_provider.h | 10 +- node/src/node_win32_perfctr_provider_empty.cc | 69 + node/src/node_wrap.h | 55 +- node/src/node_zlib.cc | 1155 +- node/src/pipe_wrap.cc | 230 +- node/src/pipe_wrap.h | 52 +- node/src/process_wrap.cc | 440 +- node/src/req-wrap-inl.h | 41 +- node/src/req-wrap.h | 44 +- node/src/signal_wrap.cc | 159 +- node/src/spawn_sync.cc | 1535 +-- node/src/spawn_sync.h | 316 +- node/src/stream_base-inl.h | 214 +- node/src/stream_base.cc | 701 +- node/src/stream_base.h | 519 +- node/src/stream_wrap.cc | 521 +- node/src/stream_wrap.h | 182 +- node/src/string_bytes.cc | 1033 +- node/src/string_bytes.h | 194 +- node/src/string_search.cc | 10 +- node/src/string_search.h | 1210 +- node/src/tcp_wrap.cc | 544 +- node/src/tcp_wrap.h | 68 +- node/src/timer_wrap.cc | 185 +- node/src/tls_wrap.cc | 1331 ++- node/src/tls_wrap.h | 162 +- node/src/tree.h | 1375 +-- node/src/tty_wrap.cc | 156 +- node/src/tty_wrap.h | 40 +- node/src/udp_wrap.cc | 679 +- node/src/udp_wrap.h | 117 +- node/src/util-inl.h | 440 +- node/src/util.h | 414 +- node/src/util1.cc | 103 +- node/src/uv.cc | 66 +- node/src/v8abbr.h | 92 +- node/uv/include/android-ifaddrs.h | 26 +- node/uv/include/pthread-barrier.h | 36 +- node/uv/include/pthread-fixes.h | 19 +- node/uv/include/stdint-msvc2008.h | 243 +- node/uv/include/tree.h | 1375 +-- node/uv/include/uv-aix.h | 10 +- node/uv/include/uv-bsd.h | 10 +- node/uv/include/uv-darwin.h | 60 +- node/uv/include/uv-errno.h | 280 +- node/uv/include/uv-linux.h | 14 +- node/uv/include/uv-sunos.h | 12 +- node/uv/include/uv-threadpool.h | 8 +- node/uv/include/uv-unix.h | 460 +- node/uv/include/uv-version.h | 6 +- node/uv/include/uv-win.h | 1010 +- node/uv/include/uv.h | 1369 ++- node/uv/src/fs-poll.c | 324 +- node/uv/src/heap-inl.h | 328 +- node/uv/src/inet.c | 462 +- node/uv/src/queue.h | 149 +- node/uv/src/threadpool.c | 387 +- node/uv/src/unix/aix.c | 1586 +-- node/uv/src/unix/android-ifaddrs.c | 528 +- node/uv/src/unix/async.c | 357 +- node/uv/src/unix/atomic-ops.h | 63 +- node/uv/src/unix/core.c | 1656 +-- node/uv/src/unix/darwin-proctitle.c | 304 +- node/uv/src/unix/darwin.c | 431 +- node/uv/src/unix/dl.c | 71 +- node/uv/src/unix/freebsd.c | 551 +- node/uv/src/unix/fs.c | 1929 ++-- node/uv/src/unix/fsevents.c | 1303 ++- node/uv/src/unix/getaddrinfo.c | 264 +- node/uv/src/unix/getnameinfo.c | 162 +- node/uv/src/unix/internal.h | 164 +- node/uv/src/unix/kqueue.c | 658 +- node/uv/src/unix/linux-core.c | 1302 ++- node/uv/src/unix/linux-inotify.c | 373 +- node/uv/src/unix/linux-syscalls.c | 527 +- node/uv/src/unix/linux-syscalls.h | 128 +- node/uv/src/unix/loop-watcher.c | 88 +- node/uv/src/unix/loop.c | 187 +- node/uv/src/unix/netbsd.c | 472 +- node/uv/src/unix/openbsd.c | 520 +- node/uv/src/unix/pipe.c | 382 +- node/uv/src/unix/poll.c | 144 +- node/uv/src/unix/process.c | 748 +- node/uv/src/unix/proctitle.c | 118 +- node/uv/src/unix/pthread-barrier.c | 143 +- node/uv/src/unix/pthread-fixes.c | 33 +- node/uv/src/unix/signal.c | 553 +- node/uv/src/unix/spinlock.h | 34 +- node/uv/src/unix/stream.c | 2258 ++-- node/uv/src/unix/sunos.c | 1097 +- node/uv/src/unix/tcp.c | 471 +- node/uv/src/unix/thread.c | 618 +- node/uv/src/unix/timer.c | 202 +- node/uv/src/unix/tty.c | 355 +- node/uv/src/unix/udp.c | 1269 ++- node/uv/src/uv-common.c | 829 +- node/uv/src/uv-common.h | 256 +- node/uv/src/version.c | 21 +- node/uv/src/win/async.c | 109 +- node/uv/src/win/atomicops-inl.h | 23 +- node/uv/src/win/core.c | 602 +- node/uv/src/win/dl.c | 135 +- node/uv/src/win/error.c | 352 +- node/uv/src/win/fs-event.c | 801 +- node/uv/src/win/fs.c | 3892 ++++--- node/uv/src/win/getaddrinfo.c | 580 +- node/uv/src/win/getnameinfo.c | 208 +- node/uv/src/win/handle-inl.h | 268 +- node/uv/src/win/handle.c | 165 +- node/uv/src/win/internal.h | 113 +- node/uv/src/win/loop-watcher.c | 178 +- node/uv/src/win/pipe.c | 3282 +++--- node/uv/src/win/poll.c | 1006 +- node/uv/src/win/process-stdio.c | 773 +- node/uv/src/win/process.c | 2007 ++-- node/uv/src/win/req-inl.h | 312 +- node/uv/src/win/signal.c | 424 +- node/uv/src/win/snprintf.c | 17 +- node/uv/src/win/stream-inl.h | 33 +- node/uv/src/win/stream1.c | 314 +- node/uv/src/win/tcp.c | 2350 ++-- node/uv/src/win/thread-win.c | 693 ++ node/uv/src/win/timer-win.c | 193 + node/uv/src/win/tty.c | 3452 +++--- node/uv/src/win/udp.c | 1485 +-- node/uv/src/win/util.c | 2110 ++-- node/uv/src/win/winapi.c | 192 +- node/uv/src/win/winapi.h | 2732 +++-- node/uv/src/win/winsock.c | 716 +- node/uv/src/win/winsock.h | 171 +- 225 files changed, 76677 insertions(+), 70926 deletions(-) create mode 100644 node/src/node_node.cc create mode 100644 node/src/node_win32_perfctr_provider_empty.cc create mode 100644 node/uv/src/win/thread-win.c create mode 100644 node/uv/src/win/timer-win.c diff --git a/node/lib/fs.js b/node/lib/fs.js index 7446491071..23d7e026d6 100644 --- a/node/lib/fs.js +++ b/node/lib/fs.js @@ -701,7 +701,6 @@ var readSyncWarned = false; fs.readSync = function(fd, buffer, offset, length, position) { var legacy = false; var encoding; - if (!(buffer instanceof Buffer)) { // legacy string interface (fd, length, position, encoding, callback) readSyncWarned = printDeprecation('fs.readSync\'s legacy String interface' + @@ -1827,6 +1826,23 @@ fs.realpath = function realpath(p, options, callback) { } }; +function getOptions(options, defaultOptions) { + if (options === null || options === undefined || + typeof options === 'function') { + return defaultOptions; + } + return options; +} + +fs.realpath.native = (path, options, callback) => { + callback = makeCallback(callback || options); + options = getOptions(options, {}); + //path = /getValidatedPath(path); + const req = new FSReqCallback(); + req.oncomplete = callback; + return binding.realpath(path, options.encoding, req); +}; + fs.mkdtemp = function(prefix, options, callback) { if (!prefix || typeof prefix !== 'string') throw new TypeError('filename prefix is required'); @@ -1867,6 +1883,42 @@ fs.mkdtempSync = function(prefix, options) { return binding.mkdtemp(prefix + 'XXXXXX', options.encoding); }; +fs.copyFile = function(src, dest, flags, callback) { + if (typeof flags === 'function') { + callback = flags; + flags = 0; + } else if (typeof callback !== 'function') { + throw new ERR_INVALID_CALLBACK(); + } + + //src = toPathIfFileURL(src); + //dest = toPathIfFileURL(dest); + //validatePath(src, 'src'); + //validatePath(dest, 'dest'); + + src = pathModule._makeLong(src); + dest = pathModule._makeLong(dest); + flags = flags | 0; + const req = new FSReqCallback(); + req.oncomplete = makeCallback(callback); + binding.copyFile(src, dest, flags, req); +} + + +fs.copyFileSync = function(src, dest, flags) { + //src = toPathIfFileURL(src); + //dest = toPathIfFileURL(dest); + //validatePath(src, 'src'); + //validatePath(dest, 'dest'); + + const ctx = { path: src, dest }; // non-prefixed + + src = pathModule._makeLong(src); + dest = pathModule._makeLong(dest); + flags = flags | 0; + binding.copyFile(src, dest, flags, undefined, ctx); + //handleErrorFromBinding(ctx); +} var pool; diff --git a/node/lib/internal/bootstrap_node.js b/node/lib/internal/bootstrap_node.js index 9071ce9939..cdcd69151d 100644 --- a/node/lib/internal/bootstrap_node.js +++ b/node/lib/internal/bootstrap_node.js @@ -44,7 +44,7 @@ setupGlobalConsole(); } - setupAsarSupport(); + setupAsarSupport(); // weolar const _process = NativeModule.require('internal/process'); diff --git a/node/lib/timers.js b/node/lib/timers.js index 78953f25d5..fc59a70cc0 100644 --- a/node/lib/timers.js +++ b/node/lib/timers.js @@ -106,7 +106,7 @@ const active = exports.active = function(item) { // Internal APIs that need timeouts should use `_unrefActive()` instead of // `active()` so that they do not unnecessarily keep the process open. -exports._unrefActive = function(item) { +const _unrefActive = exports._unrefActive = function(item) { insert(item, true); }; @@ -459,6 +459,7 @@ function Timeout(after, callback, args) { this._onTimeout = callback; this._timerArgs = args; this._repeat = null; + this.kRefed = null; } @@ -468,8 +469,18 @@ function unrefdHandle() { this.owner.close(); } +Timeout.prototype.refresh = function() { + if (this.kRefed) + active(this); + else + _unrefActive(this); + + return this; +} Timeout.prototype.unref = function() { + this.kRefed = false; + if (this._handle) { this._handle.unref(); } else if (typeof this._onTimeout === 'function') { @@ -497,6 +508,8 @@ Timeout.prototype.unref = function() { }; Timeout.prototype.ref = function() { + this[kRefed] = true; + if (this._handle) this._handle.ref(); return this; diff --git a/node/nodeblink.cpp b/node/nodeblink.cpp index f46f72bcbc..4ead83d058 100644 --- a/node/nodeblink.cpp +++ b/node/nodeblink.cpp @@ -1,218 +1,406 @@ - -#define USING_UV_SHARED 1 - -#include "nodeblink.h" - + +#if 1 // ENABLE_NODEJS +#define USING_UV_SHARED 1 + +#include "node/nodeblink.h" + #include "node/src/node.h" #include "node/src/env.h" #include "node/src/env-inl.h" -#include "node/uv/include/uv.h" - -#include "gin/v8_initializer.h" -#include "libplatform/libplatform.h" - -#include "third_party/WebKit/Source/config.h" -#include "third_party/WebKit/Source/bindings/core/v8/V8RecursionScope.h" - -#include -#include -#include - -#pragma comment(lib,"openssl.lib") -#pragma comment(lib, "IPHLPAPI.lib") -#pragma comment(lib, "Userenv.lib") -#pragma comment(lib, "Psapi.lib") - -namespace node { - -// static void childSignalCallback(uv_async_t* signal) { -// -// } -// -// static void workerRun(NodeArgc* nodeArgc) { -// int err = uv_loop_init(nodeArgc->childLoop); -// if (err != 0) -// goto loop_init_failed; -// -// // Interruption signal handler -// err = uv_async_init(nodeArgc->childLoop, &nodeArgc->async, childSignalCallback); -// if (err != 0) -// goto async_init_failed; -// -// //uv_unref(reinterpret_cast(&nodeArgc->async)); //zero 不屏蔽此句导致loop循环退出 -// nodeArgc->initType = true; -// ::SetEvent(nodeArgc->initEvent); -// -// { -// if (nodeArgc->preInitcall) -// nodeArgc->preInitcall(nodeArgc); -// -// v8::Isolate::CreateParams params; -// node::ArrayBufferAllocator array_buffer_allocator; -// params.array_buffer_allocator = &array_buffer_allocator; -// v8::Isolate *isolate = v8::Isolate::New(params); -// v8::Isolate::Scope isolate_scope(isolate); -// { -// v8::HandleScope handle_scope(isolate); -// v8::Local context = v8::Context::New(isolate); -// -// v8::Context::Scope context_scope(context); -// nodeArgc->childEnv = node::CreateEnvironment(isolate, nodeArgc->childLoop, context, nodeArgc->argc, nodeArgc->argv, nodeArgc->argc, nodeArgc->argv); -// -// // Expose API -// LoadEnvironment(nodeArgc->childEnv); -// -// if (nodeArgc->initcall) -// nodeArgc->initcall(nodeArgc); -// CHECK_EQ(nodeArgc->childLoop, nodeArgc->childEnv->event_loop()); -// //uv_run(nodeArgc->childLoop, UV_RUN_DEFAULT); // 由nodeArgc->initcall里负责消息循环 -// } -// // Clean-up all running handles -// nodeArgc->childEnv->CleanupHandles(); -// -// nodeArgc->childEnv->Dispose(); -// nodeArgc->childEnv = nullptr; -// -// isolate->Dispose(); -// -// delete nodeArgc->v8platform; -// nodeArgc->v8platform = nullptr; -// } -// return; -// -// async_init_failed: -// err = uv_loop_close(nodeArgc->childLoop); -// CHECK_EQ(err, 0); -// loop_init_failed: -// free(nodeArgc); -// nodeArgc->initType = false; -// ::SetEvent(nodeArgc->initEvent); -// } -// -// extern "C" NODE_EXTERN NodeArgc* nodeRunThread(int argc, const wchar_t *wargv[], NodeInitCallBack initcall, NodeInitCallBack preInitcall, void *data) { -// NodeArgc* nodeArgc = (NodeArgc *)malloc(sizeof(NodeArgc)); -// memset(nodeArgc, 0, sizeof(NodeArgc)); -// nodeArgc->initcall = initcall; -// nodeArgc->preInitcall = preInitcall; -// nodeArgc->data = data; -// nodeArgc->childLoop = (uv_loop_t *)malloc(sizeof(uv_loop_t)); -// nodeArgc->argv = new char*[argc + 1]; -// for (int i = 0; i < argc; i++) { -// // Compute the size of the required buffer -// DWORD size = WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1, nullptr, 0, nullptr, nullptr); -// if (size == 0) -// ::DebugBreak(); -// -// // Do the actual conversion -// nodeArgc->argv[i] = new char[size]; -// DWORD result = WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1, nodeArgc->argv[i], size, nullptr, nullptr); -// if (result == 0) -// ::DebugBreak(); -// } -// nodeArgc->argv[argc] = nullptr; -// nodeArgc->argc = argc; -// -// nodeArgc->v8platform = v8::platform::CreateDefaultPlatform(4); -// gin::V8Initializer::SetV8Platform(nodeArgc->v8platform); -// -// nodeArgc->initEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL); // 创建一个对象,用来等待node环境基础环境创建成功 -// int err = uv_thread_create(&nodeArgc->thread, reinterpret_cast(workerRun), nodeArgc); -// if (err != 0) -// goto thread_create_failed; -// ::WaitForSingleObject(nodeArgc->initEvent, INFINITE); -// CloseHandle(nodeArgc->initEvent); -// nodeArgc->initEvent = NULL; -// if (!nodeArgc->initType) -// goto thread_init_failed; -// return nodeArgc; -// -// thread_init_failed: -// -// thread_create_failed: -// free(nodeArgc); -// return nullptr; -// } -// -// extern "C" NODE_EXTERN Environment* nodeGetEnvironment(NodeArgc* nodeArgc) { -// if (nodeArgc) -// return nodeArgc->childEnv; -// return nullptr; -// } - -} // node - -extern "C" NODE_EXTERN void* nodeCreateDefaultPlatform() { - v8::Platform* v8platform = v8::platform::CreateDefaultPlatform(1); - gin::V8Initializer::SetV8Platform(v8platform); - return v8platform; -} - -static void waitForEnvironmentHandleWrapQueue(node::Environment* env) { - while (true) { - uv_run(env->event_loop(), UV_RUN_NOWAIT); - - int emptyCount = 0; - int allCount = 0; - node::Environment::HandleWrapQueue::Iterator it = env->handle_wrap_queue()->begin(); - for (; it != env->handle_wrap_queue()->end(); ++it, ++allCount) { - node::HandleWrap* wrap = (*it); - v8::Local obj = wrap->object(); - bool b = obj.IsEmpty(); - if (b) - ++emptyCount; - } - if (emptyCount == allCount) - break; - } - if (!env->handle_wrap_queue()->IsEmpty()) - DebugBreak(); -} - -static void handleCloseCb(uv_handle_t* handle) { - int* closingHandleCount = (int*)handle->data; - ++(*closingHandleCount); -} - -static void closeWalkCB(uv_handle_t* handle, void* arg) { - int* closeHandleCount = (int*)arg; - - if (!uv_is_closing(handle)) { - ++(*closeHandleCount); - handle->data = closeHandleCount + 1; - uv_close(handle, handleCloseCb); - } -} - -static void waitForAllHandleWrapQueue(node::Environment* env) { - int closeHandleCount[2] = { 0, 0 }; - uv_walk(env->event_loop(), closeWalkCB, &closeHandleCount); - - while (closeHandleCount[0] != closeHandleCount[1]) - uv_run(env->event_loop(), UV_RUN_NOWAIT); -} - -extern "C" NODE_EXTERN void nodeDeleteNodeEnvironment(node::Environment* env) { - env->CleanupHandles(); - waitForEnvironmentHandleWrapQueue(env); - //waitForAllHandleWrapQueue(env); - - env->Dispose(); -} - -extern "C" NODE_EXTERN BlinkMicrotaskSuppressionHandle nodeBlinkMicrotaskSuppressionEnter(v8::Isolate* isolate) { - return new blink::V8RecursionScope::MicrotaskSuppression(isolate); -} - -extern "C" NODE_EXTERN void nodeBlinkMicrotaskSuppressionLeave(BlinkMicrotaskSuppressionHandle handle) { - blink::V8RecursionScope::MicrotaskSuppression* suppression = (blink::V8RecursionScope::MicrotaskSuppression*)handle; - delete suppression; -} - -extern "C" NODE_EXTERN void* nodeBlinkAllocateUninitialized(size_t length) { - return node::Malloc(length); -} - -extern "C" NODE_EXTERN void nodeBlinkFree(void* data, size_t length) { - node::Realloc(data, 0); -} - +#include "node/src/node_natives.h" +#include "node/src/node_buffer.h" +#include "node/uv/include/uv.h" + +#include "gin/v8_initializer.h" +#include "libplatform/libplatform.h" + +#if V8_MAJOR_VERSION >= 7 +#include "v8_7_5/src/libplatform/default_platform_wrap.h" +#endif + +#include "third_party/WebKit/Source/config.h" +#include "third_party/WebKit/Source/bindings/core/v8/V8RecursionScope.h" + +#include "base/strings/string_util.h" +#include "wke/wkedefine.h" + +#include +#include +#include + +#pragma comment(lib,"openssl.lib") +#pragma comment(lib, "IPHLPAPI.lib") +#pragma comment(lib, "Userenv.lib") +#pragma comment(lib, "Psapi.lib") + +namespace node { + +// static void childSignalCallback(uv_async_t* signal) { +// +// } +// +// static void workerRun(NodeArgc* nodeArgc) { +// int err = uv_loop_init(nodeArgc->childLoop); +// if (err != 0) +// goto loop_init_failed; +// +// // Interruption signal handler +// err = uv_async_init(nodeArgc->childLoop, &nodeArgc->async, childSignalCallback); +// if (err != 0) +// goto async_init_failed; +// +// //uv_unref(reinterpret_cast(&nodeArgc->async)); //zero 不屏蔽此句导致loop循环退出 +// nodeArgc->initType = true; +// ::SetEvent(nodeArgc->initEvent); +// +// { +// if (nodeArgc->preInitcall) +// nodeArgc->preInitcall(nodeArgc); +// +// v8::Isolate::CreateParams params; +// node::ArrayBufferAllocator array_buffer_allocator; +// params.array_buffer_allocator = &array_buffer_allocator; +// v8::Isolate *isolate = v8::Isolate::New(params); +// v8::Isolate::Scope isolate_scope(isolate); +// { +// v8::HandleScope handle_scope(isolate); +// v8::Local context = v8::Context::New(isolate); +// +// v8::Context::Scope context_scope(context); +// nodeArgc->childEnv = node::CreateEnvironment(isolate, nodeArgc->childLoop, context, nodeArgc->argc, nodeArgc->argv, nodeArgc->argc, nodeArgc->argv); +// +// // Expose API +// LoadEnvironment(nodeArgc->childEnv); +// +// if (nodeArgc->initcall) +// nodeArgc->initcall(nodeArgc); +// CHECK_EQ(nodeArgc->childLoop, nodeArgc->childEnv->event_loop()); +// //uv_run(nodeArgc->childLoop, UV_RUN_DEFAULT); // 由nodeArgc->initcall里负责消息循环 +// } +// // Clean-up all running handles +// nodeArgc->childEnv->CleanupHandles(); +// +// nodeArgc->childEnv->Dispose(); +// nodeArgc->childEnv = nullptr; +// +// isolate->Dispose(); +// +// delete nodeArgc->v8platform; +// nodeArgc->v8platform = nullptr; +// } +// return; +// +// async_init_failed: +// err = uv_loop_close(nodeArgc->childLoop); +// CHECK_EQ(err, 0); +// loop_init_failed: +// free(nodeArgc); +// nodeArgc->initType = false; +// ::SetEvent(nodeArgc->initEvent); +// } +// +// extern "C" NODE_EXTERN NodeArgc* nodeRunThread(int argc, const wchar_t *wargv[], NodeInitCallBack initcall, NodeInitCallBack preInitcall, void *data) { +// NodeArgc* nodeArgc = (NodeArgc *)malloc(sizeof(NodeArgc)); +// memset(nodeArgc, 0, sizeof(NodeArgc)); +// nodeArgc->initcall = initcall; +// nodeArgc->preInitcall = preInitcall; +// nodeArgc->data = data; +// nodeArgc->childLoop = (uv_loop_t *)malloc(sizeof(uv_loop_t)); +// nodeArgc->argv = new char*[argc + 1]; +// for (int i = 0; i < argc; i++) { +// // Compute the size of the required buffer +// DWORD size = WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1, nullptr, 0, nullptr, nullptr); +// if (size == 0) +// ::DebugBreak(); +// +// // Do the actual conversion +// nodeArgc->argv[i] = new char[size]; +// DWORD result = WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1, nodeArgc->argv[i], size, nullptr, nullptr); +// if (result == 0) +// ::DebugBreak(); +// } +// nodeArgc->argv[argc] = nullptr; +// nodeArgc->argc = argc; +// +// nodeArgc->v8platform = v8::platform::CreateDefaultPlatform(4); +// gin::V8Initializer::SetV8Platform(nodeArgc->v8platform); +// +// nodeArgc->initEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL); // 创建一个对象,用来等待node环境基础环境创建成功 +// int err = uv_thread_create(&nodeArgc->thread, reinterpret_cast(workerRun), nodeArgc); +// if (err != 0) +// goto thread_create_failed; +// ::WaitForSingleObject(nodeArgc->initEvent, INFINITE); +// CloseHandle(nodeArgc->initEvent); +// nodeArgc->initEvent = NULL; +// if (!nodeArgc->initType) +// goto thread_init_failed; +// return nodeArgc; +// +// thread_init_failed: +// +// thread_create_failed: +// free(nodeArgc); +// return nullptr; +// } +// +// extern "C" NODE_EXTERN Environment* nodeGetEnvironment(NodeArgc* nodeArgc) { +// if (nodeArgc) +// return nodeArgc->childEnv; +// return nullptr; +// } + +} // node + +extern "C" NODE_EXTERN void* nodeCreateDefaultPlatform() +{ +#if V8_MAJOR_VERSION >= 7 + gin::DefaultPlatformWrap* defaultPlatform = new gin::DefaultPlatformWrap(); + v8::Platform* v8platform = defaultPlatform->GetPlatform(); +#else + v8::Platform* v8platform = v8::platform::CreateDefaultPlatform(1); +#endif + gin::V8Initializer::SetV8Platform(v8platform); + return v8platform; +} + +static void waitForEnvironmentHandleWrapQueue(node::Environment* env) +{ + while (true) { + uv_run(env->event_loop(), UV_RUN_NOWAIT); + + int emptyCount = 0; + int allCount = 0; + node::Environment::HandleWrapQueue::Iterator it = env->handle_wrap_queue()->begin(); + for (; it != env->handle_wrap_queue()->end(); ++it, ++allCount) { + node::HandleWrap* wrap = (*it); + v8::Local obj = wrap->object(); + bool b = obj.IsEmpty(); + if (b) + ++emptyCount; + } + if (emptyCount == allCount) + break; + } + if (!env->handle_wrap_queue()->IsEmpty()) + DebugBreak(); +} + +static void handleCloseCb(uv_handle_t* handle) +{ + int* closingHandleCount = (int*)handle->data; + ++(*closingHandleCount); +} + +static void closeWalkCB(uv_handle_t* handle, void* arg) +{ + int* closeHandleCount = (int*)arg; + + if (!uv_is_closing(handle)) { + ++(*closeHandleCount); + handle->data = closeHandleCount + 1; + uv_close(handle, handleCloseCb); + } +} + +static void waitForAllHandleWrapQueue(node::Environment* env) +{ + int closeHandleCount[2] = { 0, 0 }; + uv_walk(env->event_loop(), closeWalkCB, &closeHandleCount); + + while (closeHandleCount[0] != closeHandleCount[1]) + uv_run(env->event_loop(), UV_RUN_NOWAIT); +} + +extern "C" NODE_EXTERN void nodeDeleteNodeEnvironment(node::Environment* env) +{ + env->CleanupHandles(); + waitForEnvironmentHandleWrapQueue(env); + //waitForAllHandleWrapQueue(env); + + env->Dispose(); +} + +extern "C" NODE_EXTERN BlinkMicrotaskSuppressionHandle nodeBlinkMicrotaskSuppressionEnter(v8::Isolate* isolate) +{ + return new blink::V8RecursionScope::MicrotaskSuppression(isolate); +} + +extern "C" NODE_EXTERN void nodeBlinkMicrotaskSuppressionLeave(BlinkMicrotaskSuppressionHandle handle) +{ + blink::V8RecursionScope::MicrotaskSuppression* suppression = (blink::V8RecursionScope::MicrotaskSuppression*)handle; + delete suppression; +} + +struct MemoryHead { + size_t magicNum; + size_t size; +}; + +static const size_t magicNum0 = 0x99223321; +static const size_t magicNum1 = 0x89223321; + +extern "C" NODE_EXTERN void* nodeBlinkAllocateUninitialized(size_t length) +{ + return node::Malloc(length); +// MemoryHead* head = (MemoryHead*)malloc(length + sizeof(MemoryHead)); +// head->magicNum = magicNum0; +// head->size = length; +// return head + 1; +} + +extern "C" NODE_EXTERN void nodeBlinkFree(void* data, size_t length) +{ + node::Realloc(data, 0); +// MemoryHead* head = (MemoryHead*)data; +// --head; +// if (head->magicNum != magicNum0 || head->size != length) +// DebugBreak(); +// head->magicNum = magicNum1; +// free(head); +} + +extern "C" void node_module_init_register(); + +extern "C" NODE_EXTERN void nodeModuleInitRegister() +{ + node_module_init_register(); +} + +////////////////////////////////////////////////////////////////////////// +void testReadFile(const wchar_t* path, std::vector* buffer); + +static uv_loop_t* s_uvLoop = nullptr; +static v8::Platform* s_v8platform = nullptr; + +bool isNodejsEnable() +{ + return !!s_uvLoop; +} + +void enableNodejs() +{ + if (s_uvLoop) + return; + + nodeModuleInitRegister(); + + s_uvLoop = (uv_loop_t*)malloc(sizeof(uv_loop_t)); + uv_loop_init(s_uvLoop); + + s_v8platform = (v8::Platform*)nodeCreateDefaultPlatform(); +} + +static void isInElectronEnv(const v8::FunctionCallbackInfo& info) +{ + v8::Isolate* isolate = info.GetIsolate(); + info.GetReturnValue().Set(v8::Boolean::New(isolate, false).As()); +} + +static void getPreloadScript(const v8::FunctionCallbackInfo& info) +{ +// std::vector buffer; +// testReadFile(L"G:\\mycode\\mb\\node\\lib\\renderinit.js", &buffer); +// buffer.push_back('\0'); + + const char* data = (const char*)node::render_init_native; + size_t size = sizeof(node::render_init_native); + + v8::Isolate* isolate = info.GetIsolate(); + const v8::NewStringType type = v8::NewStringType::kInternalized; + v8::Local nameString = v8::String::NewFromUtf8(isolate, data, type, size).ToLocalChecked(); + info.GetReturnValue().Set(nameString); +} + +static void bindFuncToProcessObj(v8::Isolate* isolate, node::Environment* env, const char* name, v8::FunctionCallback callback) +{ + v8::Local func = v8::FunctionTemplate::New(isolate, callback)->GetFunction(); + const v8::NewStringType type = v8::NewStringType::kInternalized; + v8::Local nameString = v8::String::NewFromUtf8(isolate, name, type).ToLocalChecked(); + env->process_object()->Set(nameString, func); + func->SetName(nameString); +} + +void nodeWillReleaseScriptContext(NodeBindingInMbCore* nodebinding) +{ + node::FreeEnvironment((node::Environment*)nodebinding->env); + delete nodebinding; +} + +// 给mb自己用的,不导出dll。可以让mb的js环境带上nodejs的api +NodeBindingInMbCore* nodeBindNodejsOnDidCreateScriptContext(void* webView, void* frameId, void* ctx) +{ + v8::Local* context = (v8::Local*)ctx; + + BlinkMicrotaskSuppressionHandle handle = nodeBlinkMicrotaskSuppressionEnter((*context)->GetIsolate()); + NodeBindingInMbCore* binding = new NodeBindingInMbCore(); + + int argc = 0; + int argsArrayNum = 0; + wchar_t** argvW = ::CommandLineToArgvW(::GetCommandLineW(), &argc); + char** argsArray = (char**)malloc((argc + 1) * sizeof(char**)); + memset(argsArray, 0, argc * sizeof(char**)); + + for (int i = 0; i < argc; ++i) { + std::wstring arg = argvW[i]; + if (arg.length() > 0 && arg[0] >= L'a' && arg[0] <= L'z') + arg[0] += L'A' - L'a'; + + std::string argUtf8 = base::WideToUTF8(arg); + if (std::string::npos != argUtf8.find("use_res_url")) // 测试demo会有这个命令行。 + continue; + + argsArray[argsArrayNum] = strdup(argUtf8.c_str()); + ++argsArrayNum; + } +// argsArray[argsArrayNum] = strdup("G:\\mycode\\mb\\node\\lib\\renderinit.js"); +// ++argsArrayNum; + + v8::Isolate* isolate = (*context)->GetIsolate(); + + binding->env = node::CreateEnvironment(isolate, s_uvLoop, *context, argsArrayNum, argsArray, 0, nullptr); + node::Environment* env = (node::Environment*)binding->env; + + bindFuncToProcessObj(isolate, env, "_isInElectronEnv", isInElectronEnv); + bindFuncToProcessObj(isolate, env, "_getPreloadScript", getPreloadScript); + + node::LoadEnvironment(env); + + nodeBlinkMicrotaskSuppressionLeave(handle); + +// std::vector buffer; +// readFile(L"G:\\mycode\\mb\\node\\lib\\renderinit.js", &buffer); +// buffer.push_back('\0'); +// wkeRunJsByFrame((wkeWebView)webView, (wkeWebFrameHandle)frameId, buffer.data(), false); + + for (int i = 0; i < argsArrayNum; ++i) { + if (argsArray[i]) + free(argsArray[i]); + } + free(argsArray); + + return binding; +} + +void nodeRunNoWait() +{ + if (!s_uvLoop) + return; + + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + BlinkMicrotaskSuppressionHandle handle = nodeBlinkMicrotaskSuppressionEnter(isolate); + bool more = (0 != uv_run(s_uvLoop, UV_RUN_NOWAIT)); + nodeBlinkMicrotaskSuppressionLeave(handle); + + if (s_v8platform && isolate) + v8::platform::PumpMessageLoop(s_v8platform, isolate); +} + +char* nodeBufferGetData(void* buf, size_t* len) +{ + v8::Local* val = (v8::Local*)buf; + char* data = node::Buffer::Data(*val); + *len = node::Buffer::Length(*val); + return data; +} + +#endif // ENABLE_NODEJS \ No newline at end of file diff --git a/node/nodeblink.h b/node/nodeblink.h index 33f1f5690d..e109827202 100644 --- a/node/nodeblink.h +++ b/node/nodeblink.h @@ -1,6 +1,8 @@ #ifndef _NODEBLINK_H_ #define _NODEBLINK_H_ +#if 1 // ENABLE_NODEJS + #define NODE_ARCH "ia32" #define NODE_PLATFORM "win32" #define NODE_WANT_INTERNALS 1 @@ -53,4 +55,15 @@ extern "C" NODE_EXTERN void nodeBlinkMicrotaskSuppressionLeave(BlinkMicrotaskSup extern "C" NODE_EXTERN void* nodeBlinkAllocateUninitialized(size_t length); extern "C" NODE_EXTERN void nodeBlinkFree(void* data, size_t length); +extern "C" NODE_EXTERN void nodeModuleInitRegister(); +extern "C" NODE_EXTERN char* nodeBufferGetData(void* buf, size_t* len); + +struct NodeBindingInMbCore { + void* env; // node::Environment* m_env +}; +NodeBindingInMbCore* nodeBindNodejsOnDidCreateScriptContext(void* webView, void* frameId, void* ctx); +void nodeWillReleaseScriptContext(NodeBindingInMbCore* nodebinding); +bool isNodejsEnable(); + +#endif // ENABLE_NODEJS #endif //_NODEBLINK_H_ \ No newline at end of file diff --git a/node/src/async-wrap-inl.h b/node/src/async-wrap-inl.h index 64b5f09161..abfc9cfc9d 100644 --- a/node/src/async-wrap-inl.h +++ b/node/src/async-wrap-inl.h @@ -1,3 +1,6 @@ +//Copyright Joyent, Inc. and other Node contributors. +//The MIT License (MIT) + #ifndef SRC_ASYNC_WRAP_INL_H_ #define SRC_ASYNC_WRAP_INL_H_ @@ -15,42 +18,43 @@ namespace node { -inline bool AsyncWrap::ran_init_callback() const { - return static_cast(bits_ & 1); +inline bool AsyncWrap::ran_init_callback() const +{ + return static_cast(bits_ & 1); } - -inline AsyncWrap::ProviderType AsyncWrap::provider_type() const { - return static_cast(bits_ >> 1); +inline AsyncWrap::ProviderType AsyncWrap::provider_type() const +{ + return static_cast(bits_ >> 1); } - -inline int64_t AsyncWrap::get_uid() const { - return uid_; +inline int64_t AsyncWrap::get_uid() const +{ + return uid_; } - inline v8::Local AsyncWrap::MakeCallback( const v8::Local symbol, int argc, - v8::Local* argv) { - v8::Local cb_v = object()->Get(symbol); - CHECK(cb_v->IsFunction()); - return MakeCallback(cb_v.As(), argc, argv); + v8::Local* argv) +{ + v8::Local cb_v = object()->Get(symbol); + NODE_CHECK(cb_v->IsFunction()); + return MakeCallback(cb_v.As(), argc, argv); } - inline v8::Local AsyncWrap::MakeCallback( uint32_t index, int argc, - v8::Local* argv) { - v8::Local cb_v = object()->Get(index); - CHECK(cb_v->IsFunction()); - return MakeCallback(cb_v.As(), argc, argv); + v8::Local* argv) +{ + v8::Local cb_v = object()->Get(index); + NODE_CHECK(cb_v->IsFunction()); + return MakeCallback(cb_v.As(), argc, argv); } -} // namespace node +} // namespace node -#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS +#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS -#endif // SRC_ASYNC_WRAP_INL_H_ +#endif // SRC_ASYNC_WRAP_INL_H_ diff --git a/node/src/async-wrap.cc b/node/src/async-wrap.cc index 42463bd22b..732508efc1 100644 --- a/node/src/async-wrap.cc +++ b/node/src/async-wrap.cc @@ -29,362 +29,361 @@ using v8::Value; namespace node { static const char* const provider_names[] = { -#define V(PROVIDER) \ - #PROVIDER, - NODE_ASYNC_PROVIDER_TYPES(V) +#define V(PROVIDER) \ +#PROVIDER, + NODE_ASYNC_PROVIDER_TYPES(V) #undef V }; +class RetainedAsyncInfo : public RetainedObjectInfo { +public: + explicit RetainedAsyncInfo(uint16_t class_id, AsyncWrap* wrap); -class RetainedAsyncInfo: public RetainedObjectInfo { - public: - explicit RetainedAsyncInfo(uint16_t class_id, AsyncWrap* wrap); + void Dispose() override; + bool IsEquivalent(RetainedObjectInfo* other) override; + intptr_t GetHash() override; + const char* GetLabel() override; + intptr_t GetSizeInBytes() override; - void Dispose() override; - bool IsEquivalent(RetainedObjectInfo* other) override; - intptr_t GetHash() override; - const char* GetLabel() override; - intptr_t GetSizeInBytes() override; - - private: - const char* label_; - const AsyncWrap* wrap_; - const int length_; +private: + const char* label_; + const AsyncWrap* wrap_; + const int length_; }; - RetainedAsyncInfo::RetainedAsyncInfo(uint16_t class_id, AsyncWrap* wrap) - : label_(provider_names[class_id - NODE_ASYNC_ID_OFFSET]), - wrap_(wrap), - length_(wrap->self_size()) { + : label_(provider_names[class_id - NODE_ASYNC_ID_OFFSET]) + , wrap_(wrap) + , length_(wrap->self_size()) +{ } - -void RetainedAsyncInfo::Dispose() { - delete this; +void RetainedAsyncInfo::Dispose() +{ + delete this; } - -bool RetainedAsyncInfo::IsEquivalent(RetainedObjectInfo* other) { - return label_ == other->GetLabel() && - wrap_ == static_cast(other)->wrap_; +bool RetainedAsyncInfo::IsEquivalent(RetainedObjectInfo* other) +{ + return label_ == other->GetLabel() && wrap_ == static_cast(other)->wrap_; } - -intptr_t RetainedAsyncInfo::GetHash() { - return reinterpret_cast(wrap_); +intptr_t RetainedAsyncInfo::GetHash() +{ + return reinterpret_cast(wrap_); } - -const char* RetainedAsyncInfo::GetLabel() { - return label_; +const char* RetainedAsyncInfo::GetLabel() +{ + return label_; } - -intptr_t RetainedAsyncInfo::GetSizeInBytes() { - return length_; +intptr_t RetainedAsyncInfo::GetSizeInBytes() +{ + return length_; } +RetainedObjectInfo* WrapperInfo(uint16_t class_id, Local wrapper) +{ + // No class_id should be the provider type of NONE. + NODE_CHECK_NE(NODE_ASYNC_ID_OFFSET, class_id); + NODE_CHECK(wrapper->IsObject()); + NODE_CHECK(!wrapper.IsEmpty()); -RetainedObjectInfo* WrapperInfo(uint16_t class_id, Local wrapper) { - // No class_id should be the provider type of NONE. - CHECK_NE(NODE_ASYNC_ID_OFFSET, class_id); - CHECK(wrapper->IsObject()); - CHECK(!wrapper.IsEmpty()); - - Local object = wrapper.As(); - CHECK_GT(object->InternalFieldCount(), 0); + Local object = wrapper.As(); + NODE_CHECK_GT(object->InternalFieldCount(), 0); - AsyncWrap* wrap = Unwrap(object); - CHECK_NE(nullptr, wrap); + AsyncWrap* wrap = Unwrap(object); + NODE_CHECK_NE(nullptr, wrap); - return new RetainedAsyncInfo(class_id, wrap); + return new RetainedAsyncInfo(class_id, wrap); } - // end RetainedAsyncInfo - -static void EnableHooksJS(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - Local init_fn = env->async_hooks_init_function(); - if (init_fn.IsEmpty() || !init_fn->IsFunction()) - return env->ThrowTypeError("init callback is not assigned to a function"); - env->async_hooks()->set_enable_callbacks(1); +static void EnableHooksJS(const FunctionCallbackInfo& args) +{ + Environment* env = Environment::GetCurrent(args); + Local init_fn = env->async_hooks_init_function(); + if (init_fn.IsEmpty() || !init_fn->IsFunction()) + return env->ThrowTypeError("init callback is not assigned to a function"); + env->async_hooks()->set_enable_callbacks(1); } - -static void DisableHooksJS(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - env->async_hooks()->set_enable_callbacks(0); +static void DisableHooksJS(const FunctionCallbackInfo& args) +{ + Environment* env = Environment::GetCurrent(args); + env->async_hooks()->set_enable_callbacks(0); } - -static void SetupHooks(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - - if (env->async_hooks()->callbacks_enabled()) - return env->ThrowError("hooks should not be set while also enabled"); - if (!args[0]->IsObject()) - return env->ThrowTypeError("first argument must be an object"); - - Local fn_obj = args[0].As(); - - Local init_v = fn_obj->Get( - env->context(), - FIXED_ONE_BYTE_STRING(env->isolate(), "init")).ToLocalChecked(); - Local pre_v = fn_obj->Get( - env->context(), - FIXED_ONE_BYTE_STRING(env->isolate(), "pre")).ToLocalChecked(); - Local post_v = fn_obj->Get( - env->context(), - FIXED_ONE_BYTE_STRING(env->isolate(), "post")).ToLocalChecked(); - Local destroy_v = fn_obj->Get( - env->context(), - FIXED_ONE_BYTE_STRING(env->isolate(), "destroy")).ToLocalChecked(); - - if (!init_v->IsFunction()) - return env->ThrowTypeError("init callback must be a function"); - - env->set_async_hooks_init_function(init_v.As()); - - if (pre_v->IsFunction()) - env->set_async_hooks_pre_function(pre_v.As()); - if (post_v->IsFunction()) - env->set_async_hooks_post_function(post_v.As()); - if (destroy_v->IsFunction()) - env->set_async_hooks_destroy_function(destroy_v.As()); +static void SetupHooks(const FunctionCallbackInfo& args) +{ + Environment* env = Environment::GetCurrent(args); + + if (env->async_hooks()->callbacks_enabled()) + return env->ThrowError("hooks should not be set while also enabled"); + if (!args[0]->IsObject()) + return env->ThrowTypeError("first argument must be an object"); + + Local fn_obj = args[0].As(); + + Local init_v = fn_obj->Get( + env->context(), + FIXED_ONE_BYTE_STRING(env->isolate(), "init")) + .ToLocalChecked(); + Local pre_v = fn_obj->Get( + env->context(), + FIXED_ONE_BYTE_STRING(env->isolate(), "pre")) + .ToLocalChecked(); + Local post_v = fn_obj->Get( + env->context(), + FIXED_ONE_BYTE_STRING(env->isolate(), "post")) + .ToLocalChecked(); + Local destroy_v = fn_obj->Get( + env->context(), + FIXED_ONE_BYTE_STRING(env->isolate(), "destroy")) + .ToLocalChecked(); + + if (!init_v->IsFunction()) + return env->ThrowTypeError("init callback must be a function"); + + env->set_async_hooks_init_function(init_v.As()); + + if (pre_v->IsFunction()) + env->set_async_hooks_pre_function(pre_v.As()); + if (post_v->IsFunction()) + env->set_async_hooks_post_function(post_v.As()); + if (destroy_v->IsFunction()) + env->set_async_hooks_destroy_function(destroy_v.As()); } - void AsyncWrap::Initialize(Local target, - Local unused, - Local context) { - Environment* env = Environment::GetCurrent(context); - Isolate* isolate = env->isolate(); - HandleScope scope(isolate); - - env->SetMethod(target, "setupHooks", SetupHooks); - env->SetMethod(target, "disable", DisableHooksJS); - env->SetMethod(target, "enable", EnableHooksJS); - - Local async_providers = Object::New(isolate); -#define V(PROVIDER) \ - async_providers->Set(FIXED_ONE_BYTE_STRING(isolate, #PROVIDER), \ - Integer::New(isolate, AsyncWrap::PROVIDER_ ## PROVIDER)); - NODE_ASYNC_PROVIDER_TYPES(V) + Local unused, + Local context) +{ + Environment* env = Environment::GetCurrent(context); + Isolate* isolate = env->isolate(); + HandleScope scope(isolate); + + env->SetMethod(target, "setupHooks", SetupHooks); + env->SetMethod(target, "disable", DisableHooksJS); + env->SetMethod(target, "enable", EnableHooksJS); + + Local async_providers = Object::New(isolate); +#define V(PROVIDER) \ + async_providers->Set(FIXED_ONE_BYTE_STRING(isolate, #PROVIDER), \ + Integer::New(isolate, AsyncWrap::PROVIDER_##PROVIDER)); + NODE_ASYNC_PROVIDER_TYPES(V) #undef V - target->Set(FIXED_ONE_BYTE_STRING(isolate, "Providers"), async_providers); + target->Set(FIXED_ONE_BYTE_STRING(isolate, "Providers"), async_providers); - env->set_async_hooks_init_function(Local()); - env->set_async_hooks_pre_function(Local()); - env->set_async_hooks_post_function(Local()); - env->set_async_hooks_destroy_function(Local()); + env->set_async_hooks_init_function(Local()); + env->set_async_hooks_pre_function(Local()); + env->set_async_hooks_post_function(Local()); + env->set_async_hooks_destroy_function(Local()); } +void AsyncWrap::DestroyIdsCb(uv_idle_t* handle) +{ + uv_idle_stop(handle); + + Environment* env = Environment::from_destroy_ids_idle_handle(handle); + // None of the V8 calls done outside the HandleScope leak a handle. If this + // changes in the future then the SealHandleScope wrapping the uv_run() + // will catch this can cause the process to abort. + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); + Local fn = env->async_hooks_destroy_function(); + + if (fn.IsEmpty()) + return env->destroy_ids_list()->clear(); + + TryCatch try_catch(env->isolate()); + + for (auto current_id : *env->destroy_ids_list()) { + // Want each callback to be cleaned up after itself, instead of cleaning + // them all up after the while() loop completes. + HandleScope scope(env->isolate()); + Local argv = Number::New(env->isolate(), current_id); + MaybeLocal ret = fn->Call( + env->context(), Undefined(env->isolate()), 1, &argv); + + if (ret.IsEmpty()) { + ClearFatalExceptionHandlers(env); + FatalException(env->isolate(), try_catch); + } + } +} -void AsyncWrap::DestroyIdsCb(uv_idle_t* handle) { - uv_idle_stop(handle); - - Environment* env = Environment::from_destroy_ids_idle_handle(handle); - // None of the V8 calls done outside the HandleScope leak a handle. If this - // changes in the future then the SealHandleScope wrapping the uv_run() - // will catch this can cause the process to abort. - HandleScope handle_scope(env->isolate()); - Context::Scope context_scope(env->context()); - Local fn = env->async_hooks_destroy_function(); - - if (fn.IsEmpty()) - return env->destroy_ids_list()->clear(); +void LoadAsyncWrapperInfo(Environment* env) +{ + HeapProfiler* heap_profiler = env->isolate()->GetHeapProfiler(); +#define V(PROVIDER) \ + heap_profiler->SetWrapperClassInfoProvider( \ + (NODE_ASYNC_ID_OFFSET + AsyncWrap::PROVIDER_##PROVIDER), WrapperInfo); + NODE_ASYNC_PROVIDER_TYPES(V) +#undef V +} - TryCatch try_catch(env->isolate()); +AsyncWrap::AsyncWrap(Environment* env, + Local object, + ProviderType provider, + AsyncWrap* parent) + : BaseObject(env, object) + , bits_(static_cast(provider) << 1) + , uid_(env->get_async_wrap_uid()) +{ + NODE_CHECK_NE(provider, PROVIDER_NONE); + NODE_CHECK_GE(object->InternalFieldCount(), 1); + + // Shift provider value over to prevent id collision. + persistent().SetWrapperClassId(NODE_ASYNC_ID_OFFSET + provider); + + Local init_fn = env->async_hooks_init_function(); + + // No init callback exists, no reason to go on. + if (init_fn.IsEmpty()) + return; + + // If async wrap callbacks are disabled and no parent was passed that has + // run the init callback then return. + if (!env->async_wrap_callbacks_enabled() && (parent == nullptr || !parent->ran_init_callback())) + return; - for (auto current_id : *env->destroy_ids_list()) { - // Want each callback to be cleaned up after itself, instead of cleaning - // them all up after the while() loop completes. HandleScope scope(env->isolate()); - Local argv = Number::New(env->isolate(), current_id); - MaybeLocal ret = fn->Call( - env->context(), Undefined(env->isolate()), 1, &argv); - if (ret.IsEmpty()) { - ClearFatalExceptionHandlers(env); - FatalException(env->isolate(), try_catch); + Local argv[] = { + Number::New(env->isolate(), get_uid()), + Int32::New(env->isolate(), provider), + Null(env->isolate()), + Null(env->isolate()) + }; + + if (parent != nullptr) { + argv[2] = Number::New(env->isolate(), parent->get_uid()); + argv[3] = parent->object(); } - } -} + TryCatch try_catch(env->isolate()); -void LoadAsyncWrapperInfo(Environment* env) { - HeapProfiler* heap_profiler = env->isolate()->GetHeapProfiler(); -#define V(PROVIDER) \ - heap_profiler->SetWrapperClassInfoProvider( \ - (NODE_ASYNC_ID_OFFSET + AsyncWrap::PROVIDER_ ## PROVIDER), WrapperInfo); - NODE_ASYNC_PROVIDER_TYPES(V) -#undef V -} + MaybeLocal ret = init_fn->Call(env->context(), object, arraysize(argv), argv); + if (ret.IsEmpty()) { + ClearFatalExceptionHandlers(env); + FatalException(env->isolate(), try_catch); + } -AsyncWrap::AsyncWrap(Environment* env, - Local object, - ProviderType provider, - AsyncWrap* parent) - : BaseObject(env, object), bits_(static_cast(provider) << 1), - uid_(env->get_async_wrap_uid()) { - CHECK_NE(provider, PROVIDER_NONE); - CHECK_GE(object->InternalFieldCount(), 1); - - // Shift provider value over to prevent id collision. - persistent().SetWrapperClassId(NODE_ASYNC_ID_OFFSET + provider); - - Local init_fn = env->async_hooks_init_function(); - - // No init callback exists, no reason to go on. - if (init_fn.IsEmpty()) - return; - - // If async wrap callbacks are disabled and no parent was passed that has - // run the init callback then return. - if (!env->async_wrap_callbacks_enabled() && - (parent == nullptr || !parent->ran_init_callback())) - return; - - HandleScope scope(env->isolate()); - - Local argv[] = { - Number::New(env->isolate(), get_uid()), - Int32::New(env->isolate(), provider), - Null(env->isolate()), - Null(env->isolate()) - }; - - if (parent != nullptr) { - argv[2] = Number::New(env->isolate(), parent->get_uid()); - argv[3] = parent->object(); - } - - TryCatch try_catch(env->isolate()); - - MaybeLocal ret = - init_fn->Call(env->context(), object, arraysize(argv), argv); - - if (ret.IsEmpty()) { - ClearFatalExceptionHandlers(env); - FatalException(env->isolate(), try_catch); - } - - bits_ |= 1; // ran_init_callback() is true now. + bits_ |= 1; // ran_init_callback() is true now. } +AsyncWrap::~AsyncWrap() +{ + if (!ran_init_callback()) + return; -AsyncWrap::~AsyncWrap() { - if (!ran_init_callback()) - return; - - if (env()->destroy_ids_list()->empty()) - uv_idle_start(env()->destroy_ids_idle_handle(), DestroyIdsCb); + if (env()->destroy_ids_list()->empty()) + uv_idle_start(env()->destroy_ids_idle_handle(), DestroyIdsCb); - env()->destroy_ids_list()->push_back(get_uid()); + env()->destroy_ids_list()->push_back(get_uid()); } - Local AsyncWrap::MakeCallback(const Local cb, - int argc, - Local* argv) { - CHECK(env()->context() == env()->isolate()->GetCurrentContext()); - - Local pre_fn = env()->async_hooks_pre_function(); - Local post_fn = env()->async_hooks_post_function(); - Local uid = Number::New(env()->isolate(), get_uid()); - Local context = object(); - Local domain; - bool has_domain = false; - - Environment::AsyncCallbackScope callback_scope(env()); - - if (env()->using_domains()) { - Local domain_v = context->Get(env()->domain_string()); - has_domain = domain_v->IsObject(); + int argc, + Local* argv) +{ + NODE_CHECK(env()->context() == env()->isolate()->GetCurrentContext()); + + Local pre_fn = env()->async_hooks_pre_function(); + Local post_fn = env()->async_hooks_post_function(); + Local uid = Number::New(env()->isolate(), get_uid()); + Local context = object(); + Local domain; + bool has_domain = false; + + Environment::AsyncCallbackScope callback_scope(env()); + + if (env()->using_domains()) { + Local domain_v = context->Get(env()->domain_string()); + has_domain = domain_v->IsObject(); + if (has_domain) { + domain = domain_v.As(); + if (domain->Get(env()->disposed_string())->IsTrue()) + return Local(); + } + } + if (has_domain) { - domain = domain_v.As(); - if (domain->Get(env()->disposed_string())->IsTrue()) - return Local(); + Local enter_v = domain->Get(env()->enter_string()); + if (enter_v->IsFunction()) { + if (enter_v.As()->Call(domain, 0, nullptr).IsEmpty()) { + FatalError("node::AsyncWrap::MakeCallback", + "domain enter callback threw, please report this"); + } + } } - } - - if (has_domain) { - Local enter_v = domain->Get(env()->enter_string()); - if (enter_v->IsFunction()) { - if (enter_v.As()->Call(domain, 0, nullptr).IsEmpty()) { - FatalError("node::AsyncWrap::MakeCallback", - "domain enter callback threw, please report this"); - } + + if (ran_init_callback() && !pre_fn.IsEmpty()) { + TryCatch try_catch(env()->isolate()); + MaybeLocal ar = pre_fn->Call(env()->context(), context, 1, &uid); + if (ar.IsEmpty()) { + ClearFatalExceptionHandlers(env()); + FatalException(env()->isolate(), try_catch); + return Local(); + } } - } - - if (ran_init_callback() && !pre_fn.IsEmpty()) { - TryCatch try_catch(env()->isolate()); - MaybeLocal ar = pre_fn->Call(env()->context(), context, 1, &uid); - if (ar.IsEmpty()) { - ClearFatalExceptionHandlers(env()); - FatalException(env()->isolate(), try_catch); - return Local(); + + Local ret = cb->Call(context, argc, argv); + + if (ran_init_callback() && !post_fn.IsEmpty()) { + Local did_throw = Boolean::New(env()->isolate(), ret.IsEmpty()); + Local vals[] = { uid, did_throw }; + TryCatch try_catch(env()->isolate()); + MaybeLocal ar = post_fn->Call(env()->context(), context, arraysize(vals), vals); + if (ar.IsEmpty()) { + ClearFatalExceptionHandlers(env()); + FatalException(env()->isolate(), try_catch); + return Local(); + } } - } - - Local ret = cb->Call(context, argc, argv); - - if (ran_init_callback() && !post_fn.IsEmpty()) { - Local did_throw = Boolean::New(env()->isolate(), ret.IsEmpty()); - Local vals[] = { uid, did_throw }; - TryCatch try_catch(env()->isolate()); - MaybeLocal ar = - post_fn->Call(env()->context(), context, arraysize(vals), vals); - if (ar.IsEmpty()) { - ClearFatalExceptionHandlers(env()); - FatalException(env()->isolate(), try_catch); - return Local(); + + if (ret.IsEmpty()) { + return ret; } - } - if (ret.IsEmpty()) { - return ret; - } - - if (has_domain) { - Local exit_v = domain->Get(env()->exit_string()); - if (exit_v->IsFunction()) { - if (exit_v.As()->Call(domain, 0, nullptr).IsEmpty()) { - FatalError("node::AsyncWrap::MakeCallback", - "domain exit callback threw, please report this"); - } + if (has_domain) { + Local exit_v = domain->Get(env()->exit_string()); + if (exit_v->IsFunction()) { + if (exit_v.As()->Call(domain, 0, nullptr).IsEmpty()) { + FatalError("node::AsyncWrap::MakeCallback", + "domain exit callback threw, please report this"); + } + } } - } - if (callback_scope.in_makecallback()) { - return ret; - } + if (callback_scope.in_makecallback()) { + return ret; + } - Environment::TickInfo* tick_info = env()->tick_info(); + Environment::TickInfo* tick_info = env()->tick_info(); - if (tick_info->length() == 0) { - env()->isolate()->RunMicrotasks(); - } + if (tick_info->length() == 0) { + env()->isolate()->RunMicrotasks(); + } - Local process = env()->process_object(); + Local process = env()->process_object(); - if (tick_info->length() == 0) { - tick_info->set_index(0); - return ret; - } + if (tick_info->length() == 0) { + tick_info->set_index(0); + return ret; + } - if (env()->tick_callback_function()->Call(process, 0, nullptr).IsEmpty()) { - return Local(); - } + if (env()->tick_callback_function()->Call(process, 0, nullptr).IsEmpty()) { + return Local(); + } - return ret; + return ret; } -} // namespace node +} // namespace node NODE_MODULE_CONTEXT_AWARE_BUILTIN(async_wrap, node::AsyncWrap::Initialize) diff --git a/node/src/async-wrap.h b/node/src/async-wrap.h index d01c6ce9f9..33ca3a7dfa 100644 --- a/node/src/async-wrap.h +++ b/node/src/async-wrap.h @@ -1,3 +1,6 @@ +//Copyright Joyent, Inc. and other Node contributors. +//The MIT License (MIT) + #ifndef SRC_ASYNC_WRAP_H_ #define SRC_ASYNC_WRAP_H_ @@ -13,88 +16,88 @@ namespace node { #define NODE_ASYNC_ID_OFFSET 0xA1C -#define NODE_ASYNC_PROVIDER_TYPES(V) \ - V(NONE) \ - V(CRYPTO) \ - V(FSEVENTWRAP) \ - V(FSREQWRAP) \ - V(GETADDRINFOREQWRAP) \ - V(GETNAMEINFOREQWRAP) \ - V(HTTPPARSER) \ - V(JSSTREAM) \ - V(PIPEWRAP) \ - V(PIPECONNECTWRAP) \ - V(PROCESSWRAP) \ - V(QUERYWRAP) \ - V(SHUTDOWNWRAP) \ - V(SIGNALWRAP) \ - V(STATWATCHER) \ - V(TCPWRAP) \ - V(TCPCONNECTWRAP) \ - V(TIMERWRAP) \ - V(TLSWRAP) \ - V(TTYWRAP) \ - V(UDPWRAP) \ - V(UDPSENDWRAP) \ - V(WRITEWRAP) \ - V(ZLIB) +#define NODE_ASYNC_PROVIDER_TYPES(V) \ + V(NONE) \ + V(CRYPTO) \ + V(FSEVENTWRAP) \ + V(FSREQWRAP) \ + V(GETADDRINFOREQWRAP) \ + V(GETNAMEINFOREQWRAP) \ + V(HTTPPARSER) \ + V(JSSTREAM) \ + V(PIPEWRAP) \ + V(PIPECONNECTWRAP) \ + V(PROCESSWRAP) \ + V(QUERYWRAP) \ + V(SHUTDOWNWRAP) \ + V(SIGNALWRAP) \ + V(STATWATCHER) \ + V(TCPWRAP) \ + V(TCPCONNECTWRAP) \ + V(TIMERWRAP) \ + V(TLSWRAP) \ + V(TTYWRAP) \ + V(UDPWRAP) \ + V(UDPSENDWRAP) \ + V(WRITEWRAP) \ + V(ZLIB) class Environment; class AsyncWrap : public BaseObject { - public: - enum ProviderType { -#define V(PROVIDER) \ - PROVIDER_ ## PROVIDER, - NODE_ASYNC_PROVIDER_TYPES(V) +public: + enum ProviderType { +#define V(PROVIDER) \ + PROVIDER_##PROVIDER, + NODE_ASYNC_PROVIDER_TYPES(V) #undef V - }; + }; - AsyncWrap(Environment* env, - v8::Local object, - ProviderType provider, - AsyncWrap* parent = nullptr); + AsyncWrap(Environment* env, + v8::Local object, + ProviderType provider, + AsyncWrap* parent = nullptr); - virtual ~AsyncWrap(); + virtual ~AsyncWrap(); - static void Initialize(v8::Local target, - v8::Local unused, - v8::Local context); + static void Initialize(v8::Local target, + v8::Local unused, + v8::Local context); - static void DestroyIdsCb(uv_idle_t* handle); + static void DestroyIdsCb(uv_idle_t* handle); - inline ProviderType provider_type() const; + inline ProviderType provider_type() const; - inline int64_t get_uid() const; + inline int64_t get_uid() const; - // Only call these within a valid HandleScope. - v8::Local MakeCallback(const v8::Local cb, - int argc, - v8::Local* argv); - inline v8::Local MakeCallback(const v8::Local symbol, - int argc, - v8::Local* argv); - inline v8::Local MakeCallback(uint32_t index, - int argc, - v8::Local* argv); + // Only call these within a valid HandleScope. + v8::Local MakeCallback(const v8::Local cb, + int argc, + v8::Local* argv); + inline v8::Local MakeCallback(const v8::Local symbol, + int argc, + v8::Local* argv); + inline v8::Local MakeCallback(uint32_t index, + int argc, + v8::Local* argv); - virtual size_t self_size() const = 0; + virtual size_t self_size() const = 0; - private: - inline AsyncWrap(); - inline bool ran_init_callback() const; +private: + inline AsyncWrap(); + inline bool ran_init_callback() const; - // When the async hooks init JS function is called from the constructor it is - // expected the context object will receive a _asyncQueue object property - // that will be used to call pre/post in MakeCallback. - uint32_t bits_; - const int64_t uid_; + // When the async hooks init JS function is called from the constructor it is + // expected the context object will receive a _asyncQueue object property + // that will be used to call pre/post in MakeCallback. + uint32_t bits_; + const int64_t uid_; }; void LoadAsyncWrapperInfo(Environment* env); -} // namespace node +} // namespace node -#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS +#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS -#endif // SRC_ASYNC_WRAP_H_ +#endif // SRC_ASYNC_WRAP_H_ diff --git a/node/src/backtrace_posix.cc b/node/src/backtrace_posix.cc index 8fd798757a..6b2c8c72f6 100644 --- a/node/src/backtrace_posix.cc +++ b/node/src/backtrace_posix.cc @@ -4,9 +4,7 @@ #include #endif -#if defined(__linux__) && !defined(__GLIBC__) || \ - defined(__UCLIBC__) || \ - defined(_AIX) +#if defined(__linux__) && !defined(__GLIBC__) || defined(__UCLIBC__) || defined(_AIX) #define HAVE_EXECINFO_H 0 #else #define HAVE_EXECINFO_H 1 @@ -21,32 +19,33 @@ namespace node { -void DumpBacktrace(FILE* fp) { +void DumpBacktrace(FILE* fp) +{ #if HAVE_EXECINFO_H - void* frames[256]; - const int size = backtrace(frames, arraysize(frames)); - if (size <= 0) { - return; - } - for (int i = 1; i < size; i += 1) { - void* frame = frames[i]; - fprintf(fp, "%2d: ", i); - Dl_info info; - const bool have_info = dladdr(frame, &info); - if (!have_info || info.dli_sname == nullptr) { - fprintf(fp, "%p", frame); - } else if (char* demangled = abi::__cxa_demangle(info.dli_sname, 0, 0, 0)) { - fprintf(fp, "%s", demangled); - free(demangled); - } else { - fprintf(fp, "%s", info.dli_sname); + void* frames[256]; + const int size = backtrace(frames, arraysize(frames)); + if (size <= 0) { + return; } - if (have_info && info.dli_fname != nullptr) { - fprintf(fp, " [%s]", info.dli_fname); + for (int i = 1; i < size; i += 1) { + void* frame = frames[i]; + fprintf(fp, "%2d: ", i); + Dl_info info; + const bool have_info = dladdr(frame, &info); + if (!have_info || info.dli_sname == nullptr) { + fprintf(fp, "%p", frame); + } else if (char* demangled = abi::__cxa_demangle(info.dli_sname, 0, 0, 0)) { + fprintf(fp, "%s", demangled); + free(demangled); + } else { + fprintf(fp, "%s", info.dli_sname); + } + if (have_info && info.dli_fname != nullptr) { + fprintf(fp, " [%s]", info.dli_fname); + } + fprintf(fp, "\n"); } - fprintf(fp, "\n"); - } -#endif // HAVE_EXECINFO_H +#endif // HAVE_EXECINFO_H } -} // namespace node +} // namespace node diff --git a/node/src/backtrace_win32.cc b/node/src/backtrace_win32.cc index 71610fd663..665a42535a 100644 --- a/node/src/backtrace_win32.cc +++ b/node/src/backtrace_win32.cc @@ -2,7 +2,8 @@ namespace node { -void DumpBacktrace(FILE* fp) { +void DumpBacktrace(FILE* fp) +{ } -} // namespace node +} // namespace node diff --git a/node/src/base-object-inl.h b/node/src/base-object-inl.h index 4ddc5e1349..308ff28ca5 100644 --- a/node/src/base-object-inl.h +++ b/node/src/base-object-inl.h @@ -1,3 +1,6 @@ +//Copyright Joyent, Inc. and other Node contributors. +//The MIT License (MIT) + #ifndef SRC_BASE_OBJECT_INL_H_ #define SRC_BASE_OBJECT_INL_H_ @@ -13,63 +16,64 @@ namespace node { inline BaseObject::BaseObject(Environment* env, v8::Local handle) - : persistent_handle_(env->isolate(), handle), - env_(env) { - CHECK_EQ(false, handle.IsEmpty()); - // The zero field holds a pointer to the handle. Immediately set it to - // nullptr in case it's accessed by the user before construction is complete. - if (handle->InternalFieldCount() > 0) - handle->SetAlignedPointerInInternalField(0, nullptr); + : persistent_handle_(env->isolate(), handle) + , env_(env) +{ + NODE_CHECK_EQ(false, handle.IsEmpty()); + // The zero field holds a pointer to the handle. Immediately set it to + // nullptr in case it's accessed by the user before construction is complete. + if (handle->InternalFieldCount() > 0) + handle->SetAlignedPointerInInternalField(0, nullptr); } - -inline BaseObject::~BaseObject() { - CHECK(persistent_handle_.IsEmpty()); +inline BaseObject::~BaseObject() +{ + NODE_CHECK(persistent_handle_.IsEmpty()); } - -inline v8::Persistent& BaseObject::persistent() { - return persistent_handle_; +inline v8::Persistent& BaseObject::persistent() +{ + return persistent_handle_; } - -inline v8::Local BaseObject::object() { - return PersistentToLocal(env_->isolate(), persistent_handle_); +inline v8::Local BaseObject::object() +{ + return PersistentToLocal(env_->isolate(), persistent_handle_); } - -inline Environment* BaseObject::env() const { - return env_; +inline Environment* BaseObject::env() const +{ + return env_; } - template inline void BaseObject::WeakCallback( - const v8::WeakCallbackInfo& data) { - Type* self = data.GetParameter(); - self->persistent().Reset(); - delete self; + const v8::WeakCallbackInfo& data) +{ + Type* self = data.GetParameter(); + self->persistent().Reset(); + delete self; } - template -inline void BaseObject::MakeWeak(Type* ptr) { - v8::HandleScope scope(env_->isolate()); - v8::Local handle = object(); - CHECK_GT(handle->InternalFieldCount(), 0); - Wrap(handle, ptr); - persistent_handle_.MarkIndependent(); - persistent_handle_.SetWeak(ptr, WeakCallback, - v8::WeakCallbackType::kParameter); +inline void BaseObject::MakeWeak(Type* ptr) +{ + v8::HandleScope scope(env_->isolate()); + v8::Local handle = object(); + NODE_CHECK_GT(handle->InternalFieldCount(), 0); + Wrap(handle, ptr); + persistent_handle_.MarkIndependent(); + persistent_handle_.SetWeak(ptr, WeakCallback, + v8::WeakCallbackType::kParameter); } - -inline void BaseObject::ClearWeak() { - persistent_handle_.ClearWeak(); +inline void BaseObject::ClearWeak() +{ + persistent_handle_.ClearWeak(); } -} // namespace node +} // namespace node -#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS +#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS -#endif // SRC_BASE_OBJECT_INL_H_ +#endif // SRC_BASE_OBJECT_INL_H_ diff --git a/node/src/base-object.h b/node/src/base-object.h index 2725137977..3ba8a7c4ed 100644 --- a/node/src/base-object.h +++ b/node/src/base-object.h @@ -1,3 +1,6 @@ +//Copyright Joyent, Inc. and other Node contributors. +//The MIT License (MIT) + #ifndef SRC_BASE_OBJECT_H_ #define SRC_BASE_OBJECT_H_ @@ -10,46 +13,46 @@ namespace node { class Environment; class BaseObject { - public: - inline BaseObject(Environment* env, v8::Local handle); - inline virtual ~BaseObject(); - - // Returns the wrapped object. Returns an empty handle when - // persistent.IsEmpty() is true. - inline v8::Local object(); - - // The parent class is responsible for calling .Reset() on destruction - // when the persistent handle is strong because there is no way for - // BaseObject to know when the handle goes out of scope. - // Weak handles have been reset by the time the destructor runs but - // calling .Reset() again is harmless. - inline v8::Persistent& persistent(); - - inline Environment* env() const; - - // The handle_ must have an internal field count > 0, and the first - // index is reserved for a pointer to this class. This is an - // implicit requirement, but Node does not have a case where it's - // required that MakeWeak() be called and the internal field not - // be set. - template - inline void MakeWeak(Type* ptr); - - inline void ClearWeak(); - - private: - BaseObject(); - - template - static inline void WeakCallback( - const v8::WeakCallbackInfo& data); - - v8::Persistent persistent_handle_; - Environment* env_; +public: + inline BaseObject(Environment* env, v8::Local handle); + inline virtual ~BaseObject(); + + // Returns the wrapped object. Returns an empty handle when + // persistent.IsEmpty() is true. + inline v8::Local object(); + + // The parent class is responsible for calling .Reset() on destruction + // when the persistent handle is strong because there is no way for + // BaseObject to know when the handle goes out of scope. + // Weak handles have been reset by the time the destructor runs but + // calling .Reset() again is harmless. + inline v8::Persistent& persistent(); + + inline Environment* env() const; + + // The handle_ must have an internal field count > 0, and the first + // index is reserved for a pointer to this class. This is an + // implicit requirement, but Node does not have a case where it's + // required that MakeWeak() be called and the internal field not + // be set. + template + inline void MakeWeak(Type* ptr); + + inline void ClearWeak(); + +private: + BaseObject(); + + template + static inline void WeakCallback( + const v8::WeakCallbackInfo& data); + + v8::Persistent persistent_handle_; + Environment* env_; }; -} // namespace node +} // namespace node -#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS +#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS -#endif // SRC_BASE_OBJECT_H_ +#endif // SRC_BASE_OBJECT_H_ diff --git a/node/src/base64.h b/node/src/base64.h index fef3364f0a..9ae1df9524 100644 --- a/node/src/base64.h +++ b/node/src/base64.h @@ -11,184 +11,178 @@ namespace node { //// Base 64 //// #define base64_encoded_size(size) ((size + 2 - ((size + 2) % 3)) / 3 * 4) - // Doesn't check for padding at the end. Can be 1-2 bytes over. -static inline size_t base64_decoded_size_fast(size_t size) { - size_t remainder = size % 4; - - size = (size / 4) * 3; - if (remainder) { - if (size == 0 && remainder == 1) { - // special case: 1-byte input cannot be decoded - size = 0; - } else { - // non-padded input, add 1 or 2 extra bytes - size += 1 + (remainder == 3); +static inline size_t base64_decoded_size_fast(size_t size) +{ + size_t remainder = size % 4; + + size = (size / 4) * 3; + if (remainder) { + if (size == 0 && remainder == 1) { + // special case: 1-byte input cannot be decoded + size = 0; + } else { + // non-padded input, add 1 or 2 extra bytes + size += 1 + (remainder == 3); + } } - } - return size; + return size; } template -size_t base64_decoded_size(const TypeName* src, size_t size) { - if (size == 0) - return 0; +size_t base64_decoded_size(const TypeName* src, size_t size) +{ + if (size == 0) + return 0; - if (src[size - 1] == '=') - size--; - if (size > 0 && src[size - 1] == '=') - size--; + if (src[size - 1] == '=') + size--; + if (size > 0 && src[size - 1] == '=') + size--; - return base64_decoded_size_fast(size); + return base64_decoded_size_fast(size); } - extern const int8_t unbase64_table[256]; - -#define unbase64(x) \ - static_cast(unbase64_table[static_cast(x)]) - +#define unbase64(x) \ + static_cast(unbase64_table[static_cast(x)]) template size_t base64_decode_slow(char* dst, size_t dstlen, - const TypeName* src, size_t srclen) { - uint8_t hi; - uint8_t lo; - size_t i = 0; - size_t k = 0; - for (;;) { -#define V(expr) \ - while (i < srclen) { \ - const uint8_t c = src[i]; \ - lo = unbase64(c); \ - i += 1; \ - if (lo < 64) \ - break; /* Legal character. */ \ - if (c == '=') \ - return k; \ - } \ - expr; \ - if (i >= srclen) \ - return k; \ - if (k >= dstlen) \ - return k; \ + const TypeName* src, size_t srclen) +{ + uint8_t hi; + uint8_t lo; + size_t i = 0; + size_t k = 0; + for (;;) { +#define V(expr) \ + while (i < srclen) { \ + const uint8_t c = src[i]; \ + lo = unbase64(c); \ + i += 1; \ + if (lo < 64) \ + break; /* Legal character. */ \ + if (c == '=') \ + return k; \ + } \ + expr; \ + if (i >= srclen) \ + return k; \ + if (k >= dstlen) \ + return k; \ hi = lo; - V(void/* Nothing. */); - V(dst[k++] = ((hi & 0x3F) << 2) | ((lo & 0x30) >> 4)); - V(dst[k++] = ((hi & 0x0F) << 4) | ((lo & 0x3C) >> 2)); - V(dst[k++] = ((hi & 0x03) << 6) | ((lo & 0x3F) >> 0)); + V(void /* Nothing. */); + V(dst[k++] = ((hi & 0x3F) << 2) | ((lo & 0x30) >> 4)); + V(dst[k++] = ((hi & 0x0F) << 4) | ((lo & 0x3C) >> 2)); + V(dst[k++] = ((hi & 0x03) << 6) | ((lo & 0x3F) >> 0)); #undef V - } - UNREACHABLE(); + } + UNREACHABLE(); } - template size_t base64_decode_fast(char* const dst, const size_t dstlen, - const TypeName* const src, const size_t srclen, - const size_t decoded_size) { - const size_t available = dstlen < decoded_size ? dstlen : decoded_size; - const size_t max_i = srclen / 4 * 4; - const size_t max_k = available / 3 * 3; - size_t i = 0; - size_t k = 0; - while (i < max_i && k < max_k) { - const uint32_t v = - unbase64(src[i + 0]) << 24 | - unbase64(src[i + 1]) << 16 | - unbase64(src[i + 2]) << 8 | - unbase64(src[i + 3]); - // If MSB is set, input contains whitespace or is not valid base64. - if (v & 0x80808080) { - break; + const TypeName* const src, const size_t srclen, + const size_t decoded_size) +{ + const size_t available = dstlen < decoded_size ? dstlen : decoded_size; + const size_t max_i = srclen / 4 * 4; + const size_t max_k = available / 3 * 3; + size_t i = 0; + size_t k = 0; + while (i < max_i && k < max_k) { + const uint32_t v = unbase64(src[i + 0]) << 24 | unbase64(src[i + 1]) << 16 | unbase64(src[i + 2]) << 8 | unbase64(src[i + 3]); + // If MSB is set, input contains whitespace or is not valid base64. + if (v & 0x80808080) { + break; + } + dst[k + 0] = ((v >> 22) & 0xFC) | ((v >> 20) & 0x03); + dst[k + 1] = ((v >> 12) & 0xF0) | ((v >> 10) & 0x0F); + dst[k + 2] = ((v >> 2) & 0xC0) | ((v >> 0) & 0x3F); + i += 4; + k += 3; } - dst[k + 0] = ((v >> 22) & 0xFC) | ((v >> 20) & 0x03); - dst[k + 1] = ((v >> 12) & 0xF0) | ((v >> 10) & 0x0F); - dst[k + 2] = ((v >> 2) & 0xC0) | ((v >> 0) & 0x3F); - i += 4; - k += 3; - } - if (i < srclen && k < dstlen) { - return k + base64_decode_slow(dst + k, dstlen - k, src + i, srclen - i); - } - return k; + if (i < srclen && k < dstlen) { + return k + base64_decode_slow(dst + k, dstlen - k, src + i, srclen - i); + } + return k; } - template size_t base64_decode(char* const dst, const size_t dstlen, - const TypeName* const src, const size_t srclen) { - const size_t decoded_size = base64_decoded_size(src, srclen); - return base64_decode_fast(dst, dstlen, src, srclen, decoded_size); + const TypeName* const src, const size_t srclen) +{ + const size_t decoded_size = base64_decoded_size(src, srclen); + return base64_decode_fast(dst, dstlen, src, srclen, decoded_size); } static size_t base64_encode(const char* src, - size_t slen, - char* dst, - size_t dlen) { - // We know how much we'll write, just make sure that there's space. - CHECK(dlen >= base64_encoded_size(slen) && - "not enough space provided for base64 encode"); - - dlen = base64_encoded_size(slen); - - unsigned a; - unsigned b; - unsigned c; - unsigned i; - unsigned k; - unsigned n; - - static const char table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz" - "0123456789+/"; - - i = 0; - k = 0; - n = slen / 3 * 3; - - while (i < n) { - a = src[i + 0] & 0xff; - b = src[i + 1] & 0xff; - c = src[i + 2] & 0xff; - - dst[k + 0] = table[a >> 2]; - dst[k + 1] = table[((a & 3) << 4) | (b >> 4)]; - dst[k + 2] = table[((b & 0x0f) << 2) | (c >> 6)]; - dst[k + 3] = table[c & 0x3f]; - - i += 3; - k += 4; - } - - if (n != slen) { - switch (slen - n) { - case 1: - a = src[i + 0] & 0xff; - dst[k + 0] = table[a >> 2]; - dst[k + 1] = table[(a & 3) << 4]; - dst[k + 2] = '='; - dst[k + 3] = '='; - break; - - case 2: + size_t slen, + char* dst, + size_t dlen) +{ + // We know how much we'll write, just make sure that there's space. + NODE_CHECK(dlen >= base64_encoded_size(slen) && "not enough space provided for base64 encode"); + + dlen = base64_encoded_size(slen); + + unsigned a; + unsigned b; + unsigned c; + unsigned i; + unsigned k; + unsigned n; + + static const char table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + + i = 0; + k = 0; + n = slen / 3 * 3; + + while (i < n) { a = src[i + 0] & 0xff; b = src[i + 1] & 0xff; + c = src[i + 2] & 0xff; + dst[k + 0] = table[a >> 2]; dst[k + 1] = table[((a & 3) << 4) | (b >> 4)]; - dst[k + 2] = table[(b & 0x0f) << 2]; - dst[k + 3] = '='; - break; + dst[k + 2] = table[((b & 0x0f) << 2) | (c >> 6)]; + dst[k + 3] = table[c & 0x3f]; + + i += 3; + k += 4; } - } - return dlen; -} -} // namespace node + if (n != slen) { + switch (slen - n) { + case 1: + a = src[i + 0] & 0xff; + dst[k + 0] = table[a >> 2]; + dst[k + 1] = table[(a & 3) << 4]; + dst[k + 2] = '='; + dst[k + 3] = '='; + break; + + case 2: + a = src[i + 0] & 0xff; + b = src[i + 1] & 0xff; + dst[k + 0] = table[a >> 2]; + dst[k + 1] = table[((a & 3) << 4) | (b >> 4)]; + dst[k + 2] = table[(b & 0x0f) << 2]; + dst[k + 3] = '='; + break; + } + } + return dlen; +} +} // namespace node -#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS +#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS -#endif // SRC_BASE64_H_ +#endif // SRC_BASE64_H_ diff --git a/node/src/bootstrap_node_js.h b/node/src/bootstrap_node_js.h index ad883135cf..9ef976bb07 100644 --- a/node/src/bootstrap_node_js.h +++ b/node/src/bootstrap_node_js.h @@ -1,998 +1,998 @@ namespace node { char internal_bootstrap_node_native_hook[15869] = { - 0x2F, 0x2F, 0x20, 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x2C, 0x20, 0x61, 0x6E, 0x64, 0x20, 0x77, 0x65, - 0x6C, 0x63, 0x6F, 0x6D, 0x65, 0x20, 0x74, 0x6F, 0x20, 0x68, 0x61, 0x63, 0x6B, 0x69, 0x6E, 0x67, - 0x20, 0x6E, 0x6F, 0x64, 0x65, 0x2E, 0x6A, 0x73, 0x21, 0x0A, 0x2F, 0x2F, 0x0A, 0x2F, 0x2F, 0x20, - 0x54, 0x68, 0x69, 0x73, 0x20, 0x66, 0x69, 0x6C, 0x65, 0x20, 0x69, 0x73, 0x20, 0x69, 0x6E, 0x76, - 0x6F, 0x6B, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x6E, 0x6F, 0x64, 0x65, 0x3A, 0x3A, 0x4C, 0x6F, - 0x61, 0x64, 0x45, 0x6E, 0x76, 0x69, 0x72, 0x6F, 0x6E, 0x6D, 0x65, 0x6E, 0x74, 0x20, 0x69, 0x6E, - 0x20, 0x73, 0x72, 0x63, 0x2F, 0x6E, 0x6F, 0x64, 0x65, 0x2E, 0x63, 0x63, 0x2C, 0x20, 0x61, 0x6E, - 0x64, 0x20, 0x69, 0x73, 0x0A, 0x2F, 0x2F, 0x20, 0x72, 0x65, 0x73, 0x70, 0x6F, 0x6E, 0x73, 0x69, - 0x62, 0x6C, 0x65, 0x20, 0x66, 0x6F, 0x72, 0x20, 0x62, 0x6F, 0x6F, 0x74, 0x73, 0x74, 0x72, 0x61, - 0x70, 0x70, 0x69, 0x6E, 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6E, 0x6F, 0x64, 0x65, 0x2E, 0x6A, - 0x73, 0x20, 0x63, 0x6F, 0x72, 0x65, 0x2E, 0x20, 0x41, 0x73, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, - 0x61, 0x6C, 0x20, 0x63, 0x61, 0x75, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x69, 0x73, 0x20, 0x67, 0x69, - 0x76, 0x65, 0x6E, 0x0A, 0x2F, 0x2F, 0x20, 0x74, 0x6F, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x65, - 0x72, 0x66, 0x6F, 0x72, 0x6D, 0x61, 0x6E, 0x63, 0x65, 0x20, 0x6F, 0x66, 0x20, 0x74, 0x68, 0x65, - 0x20, 0x73, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x20, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, - 0x2C, 0x20, 0x6D, 0x61, 0x6E, 0x79, 0x20, 0x64, 0x65, 0x70, 0x65, 0x6E, 0x64, 0x65, 0x6E, 0x63, - 0x69, 0x65, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x69, 0x6E, 0x76, 0x6F, 0x6B, 0x65, 0x64, 0x0A, - 0x2F, 0x2F, 0x20, 0x6C, 0x61, 0x7A, 0x69, 0x6C, 0x79, 0x2E, 0x0A, 0x0A, 0x27, 0x75, 0x73, 0x65, - 0x20, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x27, 0x3B, 0x0A, 0x0A, 0x28, 0x66, 0x75, 0x6E, 0x63, - 0x74, 0x69, 0x6F, 0x6E, 0x28, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x29, 0x20, 0x7B, 0x0A, - 0x0A, 0x20, 0x20, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x73, 0x74, 0x61, 0x72, - 0x74, 0x75, 0x70, 0x28, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, 0x6E, 0x73, - 0x74, 0x20, 0x45, 0x76, 0x65, 0x6E, 0x74, 0x45, 0x6D, 0x69, 0x74, 0x74, 0x65, 0x72, 0x20, 0x3D, - 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x72, 0x65, - 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x27, 0x65, 0x76, 0x65, 0x6E, 0x74, 0x73, 0x27, 0x29, 0x3B, - 0x0A, 0x20, 0x20, 0x20, 0x20, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x5F, 0x65, 0x76, - 0x65, 0x6E, 0x74, 0x73, 0x43, 0x6F, 0x75, 0x6E, 0x74, 0x20, 0x3D, 0x20, 0x30, 0x3B, 0x0A, 0x0A, - 0x20, 0x20, 0x20, 0x20, 0x4F, 0x62, 0x6A, 0x65, 0x63, 0x74, 0x2E, 0x73, 0x65, 0x74, 0x50, 0x72, - 0x6F, 0x74, 0x6F, 0x74, 0x79, 0x70, 0x65, 0x4F, 0x66, 0x28, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, - 0x73, 0x2C, 0x20, 0x4F, 0x62, 0x6A, 0x65, 0x63, 0x74, 0x2E, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, - 0x28, 0x45, 0x76, 0x65, 0x6E, 0x74, 0x45, 0x6D, 0x69, 0x74, 0x74, 0x65, 0x72, 0x2E, 0x70, 0x72, - 0x6F, 0x74, 0x6F, 0x74, 0x79, 0x70, 0x65, 0x2C, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x6F, 0x72, 0x3A, 0x20, 0x7B, 0x0A, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x6C, 0x75, 0x65, 0x3A, 0x20, 0x70, - 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, - 0x6F, 0x72, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x7D, - 0x29, 0x29, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x45, 0x76, 0x65, 0x6E, 0x74, 0x45, 0x6D, - 0x69, 0x74, 0x74, 0x65, 0x72, 0x2E, 0x63, 0x61, 0x6C, 0x6C, 0x28, 0x70, 0x72, 0x6F, 0x63, 0x65, - 0x73, 0x73, 0x29, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x6C, 0x65, 0x74, 0x20, 0x65, 0x65, - 0x57, 0x61, 0x72, 0x6E, 0x65, 0x64, 0x20, 0x3D, 0x20, 0x66, 0x61, 0x6C, 0x73, 0x65, 0x3B, 0x0A, - 0x20, 0x20, 0x20, 0x20, 0x4F, 0x62, 0x6A, 0x65, 0x63, 0x74, 0x2E, 0x64, 0x65, 0x66, 0x69, 0x6E, - 0x65, 0x50, 0x72, 0x6F, 0x70, 0x65, 0x72, 0x74, 0x79, 0x28, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, - 0x73, 0x2C, 0x20, 0x27, 0x45, 0x76, 0x65, 0x6E, 0x74, 0x45, 0x6D, 0x69, 0x74, 0x74, 0x65, 0x72, - 0x27, 0x2C, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x67, 0x65, 0x74, 0x28, 0x29, - 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, - 0x20, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x6E, 0x61, 0x6C, 0x55, 0x74, 0x69, 0x6C, 0x20, 0x3D, 0x20, - 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x72, 0x65, 0x71, - 0x75, 0x69, 0x72, 0x65, 0x28, 0x27, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x6E, 0x61, 0x6C, 0x2F, 0x75, - 0x74, 0x69, 0x6C, 0x27, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, - 0x65, 0x57, 0x61, 0x72, 0x6E, 0x65, 0x64, 0x20, 0x3D, 0x20, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x6E, - 0x61, 0x6C, 0x55, 0x74, 0x69, 0x6C, 0x2E, 0x70, 0x72, 0x69, 0x6E, 0x74, 0x44, 0x65, 0x70, 0x72, - 0x65, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x4D, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x28, 0x0A, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x70, 0x72, 0x6F, 0x63, 0x65, - 0x73, 0x73, 0x2E, 0x45, 0x76, 0x65, 0x6E, 0x74, 0x45, 0x6D, 0x69, 0x74, 0x74, 0x65, 0x72, 0x20, - 0x69, 0x73, 0x20, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x2E, 0x20, 0x55, - 0x73, 0x65, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x27, 0x65, 0x76, 0x65, 0x6E, - 0x74, 0x73, 0x27, 0x29, 0x20, 0x69, 0x6E, 0x73, 0x74, 0x65, 0x61, 0x64, 0x2E, 0x22, 0x2C, 0x0A, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x65, 0x57, 0x61, 0x72, 0x6E, - 0x65, 0x64, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x29, 0x3B, 0x0A, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6E, 0x20, 0x45, 0x76, 0x65, - 0x6E, 0x74, 0x45, 0x6D, 0x69, 0x74, 0x74, 0x65, 0x72, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x7D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x29, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, - 0x73, 0x65, 0x74, 0x75, 0x70, 0x50, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x4F, 0x62, 0x6A, 0x65, - 0x63, 0x74, 0x28, 0x29, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x64, 0x6F, - 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x67, 0x6F, 0x6F, 0x64, 0x20, 0x61, 0x6E, 0x64, 0x20, 0x65, - 0x61, 0x72, 0x6C, 0x79, 0x2C, 0x20, 0x73, 0x69, 0x6E, 0x63, 0x65, 0x20, 0x69, 0x74, 0x20, 0x68, - 0x61, 0x6E, 0x64, 0x6C, 0x65, 0x73, 0x20, 0x65, 0x72, 0x72, 0x6F, 0x72, 0x73, 0x2E, 0x0A, 0x20, - 0x20, 0x20, 0x20, 0x73, 0x65, 0x74, 0x75, 0x70, 0x50, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x46, - 0x61, 0x74, 0x61, 0x6C, 0x28, 0x29, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65, 0x74, - 0x75, 0x70, 0x47, 0x6C, 0x6F, 0x62, 0x61, 0x6C, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6C, 0x65, - 0x73, 0x28, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x21, 0x70, 0x72, - 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x5F, 0x6E, 0x6F, 0x42, 0x72, 0x6F, 0x77, 0x73, 0x65, 0x72, - 0x47, 0x6C, 0x6F, 0x62, 0x61, 0x6C, 0x73, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x73, 0x65, 0x74, 0x75, 0x70, 0x47, 0x6C, 0x6F, 0x62, 0x61, 0x6C, 0x54, 0x69, 0x6D, 0x65, - 0x6F, 0x75, 0x74, 0x73, 0x28, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65, - 0x74, 0x75, 0x70, 0x47, 0x6C, 0x6F, 0x62, 0x61, 0x6C, 0x43, 0x6F, 0x6E, 0x73, 0x6F, 0x6C, 0x65, - 0x28, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x73, - 0x65, 0x74, 0x75, 0x70, 0x41, 0x73, 0x61, 0x72, 0x53, 0x75, 0x70, 0x70, 0x6F, 0x72, 0x74, 0x28, - 0x29, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x20, 0x5F, 0x70, - 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x20, 0x3D, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, - 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x27, 0x69, - 0x6E, 0x74, 0x65, 0x72, 0x6E, 0x61, 0x6C, 0x2F, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x27, - 0x29, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x5F, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, - 0x2E, 0x73, 0x65, 0x74, 0x75, 0x70, 0x5F, 0x68, 0x72, 0x74, 0x69, 0x6D, 0x65, 0x28, 0x29, 0x3B, - 0x0A, 0x20, 0x20, 0x20, 0x20, 0x5F, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x73, 0x65, - 0x74, 0x75, 0x70, 0x5F, 0x63, 0x70, 0x75, 0x55, 0x73, 0x61, 0x67, 0x65, 0x28, 0x29, 0x3B, 0x0A, - 0x20, 0x20, 0x20, 0x20, 0x5F, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x73, 0x65, 0x74, - 0x75, 0x70, 0x43, 0x6F, 0x6E, 0x66, 0x69, 0x67, 0x28, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, - 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x5F, 0x73, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x29, 0x3B, 0x0A, - 0x20, 0x20, 0x20, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, - 0x2E, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x27, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x6E, - 0x61, 0x6C, 0x2F, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2F, 0x77, 0x61, 0x72, 0x6E, 0x69, - 0x6E, 0x67, 0x27, 0x29, 0x2E, 0x73, 0x65, 0x74, 0x75, 0x70, 0x28, 0x29, 0x3B, 0x0A, 0x20, 0x20, - 0x20, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x72, - 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x27, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x6E, 0x61, 0x6C, - 0x2F, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2F, 0x6E, 0x65, 0x78, 0x74, 0x5F, 0x74, 0x69, - 0x63, 0x6B, 0x27, 0x29, 0x2E, 0x73, 0x65, 0x74, 0x75, 0x70, 0x28, 0x29, 0x3B, 0x0A, 0x20, 0x20, - 0x20, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x72, - 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x27, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x6E, 0x61, 0x6C, - 0x2F, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2F, 0x73, 0x74, 0x64, 0x69, 0x6F, 0x27, 0x29, - 0x2E, 0x73, 0x65, 0x74, 0x75, 0x70, 0x28, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x5F, 0x70, - 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x73, 0x65, 0x74, 0x75, 0x70, 0x4B, 0x69, 0x6C, 0x6C, - 0x41, 0x6E, 0x64, 0x45, 0x78, 0x69, 0x74, 0x28, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x5F, - 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x73, 0x65, 0x74, 0x75, 0x70, 0x53, 0x69, 0x67, - 0x6E, 0x61, 0x6C, 0x48, 0x61, 0x6E, 0x64, 0x6C, 0x65, 0x72, 0x73, 0x28, 0x29, 0x3B, 0x0A, 0x0A, - 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x44, 0x6F, 0x20, 0x6E, 0x6F, 0x74, 0x20, 0x69, 0x6E, - 0x69, 0x74, 0x69, 0x61, 0x6C, 0x69, 0x7A, 0x65, 0x20, 0x63, 0x68, 0x61, 0x6E, 0x6E, 0x65, 0x6C, - 0x20, 0x69, 0x6E, 0x20, 0x64, 0x65, 0x62, 0x75, 0x67, 0x67, 0x65, 0x72, 0x20, 0x61, 0x67, 0x65, - 0x6E, 0x74, 0x2C, 0x20, 0x69, 0x74, 0x20, 0x64, 0x65, 0x6C, 0x65, 0x74, 0x65, 0x73, 0x20, 0x65, - 0x6E, 0x76, 0x20, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6C, 0x65, 0x0A, 0x20, 0x20, 0x20, 0x20, - 0x2F, 0x2F, 0x20, 0x61, 0x6E, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6D, 0x61, 0x69, 0x6E, 0x20, - 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x20, 0x77, 0x6F, 0x6E, 0x27, 0x74, 0x20, 0x73, 0x65, 0x65, - 0x20, 0x69, 0x74, 0x2E, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x70, 0x72, 0x6F, - 0x63, 0x65, 0x73, 0x73, 0x2E, 0x61, 0x72, 0x67, 0x76, 0x5B, 0x31, 0x5D, 0x20, 0x21, 0x3D, 0x3D, - 0x20, 0x27, 0x2D, 0x2D, 0x64, 0x65, 0x62, 0x75, 0x67, 0x2D, 0x61, 0x67, 0x65, 0x6E, 0x74, 0x27, - 0x29, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5F, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, - 0x2E, 0x73, 0x65, 0x74, 0x75, 0x70, 0x43, 0x68, 0x61, 0x6E, 0x6E, 0x65, 0x6C, 0x28, 0x29, 0x3B, - 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x5F, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x73, - 0x65, 0x74, 0x75, 0x70, 0x52, 0x61, 0x77, 0x44, 0x65, 0x62, 0x75, 0x67, 0x28, 0x29, 0x3B, 0x0A, - 0x0A, 0x20, 0x20, 0x20, 0x20, 0x4F, 0x62, 0x6A, 0x65, 0x63, 0x74, 0x2E, 0x64, 0x65, 0x66, 0x69, - 0x6E, 0x65, 0x50, 0x72, 0x6F, 0x70, 0x65, 0x72, 0x74, 0x79, 0x28, 0x70, 0x72, 0x6F, 0x63, 0x65, - 0x73, 0x73, 0x2C, 0x20, 0x27, 0x61, 0x72, 0x67, 0x76, 0x30, 0x27, 0x2C, 0x20, 0x7B, 0x0A, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6E, 0x75, 0x6D, 0x65, 0x72, 0x61, 0x62, 0x6C, 0x65, 0x3A, - 0x20, 0x74, 0x72, 0x75, 0x65, 0x2C, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, 0x6E, - 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x62, 0x6C, 0x65, 0x3A, 0x20, 0x66, 0x61, 0x6C, 0x73, 0x65, - 0x2C, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x6C, 0x75, 0x65, 0x3A, 0x20, 0x70, - 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x61, 0x72, 0x67, 0x76, 0x5B, 0x30, 0x5D, 0x0A, 0x20, - 0x20, 0x20, 0x20, 0x7D, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x70, 0x72, 0x6F, 0x63, 0x65, - 0x73, 0x73, 0x2E, 0x61, 0x72, 0x67, 0x76, 0x5B, 0x30, 0x5D, 0x20, 0x3D, 0x20, 0x70, 0x72, 0x6F, - 0x63, 0x65, 0x73, 0x73, 0x2E, 0x65, 0x78, 0x65, 0x63, 0x50, 0x61, 0x74, 0x68, 0x3B, 0x0A, 0x0A, - 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x54, 0x68, 0x65, 0x72, 0x65, 0x20, 0x61, 0x72, 0x65, - 0x20, 0x76, 0x61, 0x72, 0x69, 0x6F, 0x75, 0x73, 0x20, 0x6D, 0x6F, 0x64, 0x65, 0x73, 0x20, 0x74, - 0x68, 0x61, 0x74, 0x20, 0x4E, 0x6F, 0x64, 0x65, 0x20, 0x63, 0x61, 0x6E, 0x20, 0x72, 0x75, 0x6E, - 0x20, 0x69, 0x6E, 0x2E, 0x20, 0x54, 0x68, 0x65, 0x20, 0x6D, 0x6F, 0x73, 0x74, 0x20, 0x63, 0x6F, - 0x6D, 0x6D, 0x6F, 0x6E, 0x20, 0x74, 0x77, 0x6F, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, - 0x61, 0x72, 0x65, 0x20, 0x72, 0x75, 0x6E, 0x6E, 0x69, 0x6E, 0x67, 0x20, 0x66, 0x72, 0x6F, 0x6D, - 0x20, 0x61, 0x20, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x20, 0x61, 0x6E, 0x64, 0x20, 0x72, 0x75, - 0x6E, 0x6E, 0x69, 0x6E, 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x52, 0x45, 0x50, 0x4C, 0x20, 0x2D, - 0x20, 0x62, 0x75, 0x74, 0x20, 0x74, 0x68, 0x65, 0x72, 0x65, 0x20, 0x61, 0x72, 0x65, 0x20, 0x61, - 0x20, 0x66, 0x65, 0x77, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x6F, 0x74, 0x68, 0x65, - 0x72, 0x73, 0x20, 0x6C, 0x69, 0x6B, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x65, 0x62, 0x75, - 0x67, 0x67, 0x65, 0x72, 0x20, 0x6F, 0x72, 0x20, 0x72, 0x75, 0x6E, 0x6E, 0x69, 0x6E, 0x67, 0x20, - 0x2D, 0x2D, 0x65, 0x76, 0x61, 0x6C, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6D, 0x65, 0x6E, 0x74, 0x73, - 0x2E, 0x20, 0x48, 0x65, 0x72, 0x65, 0x20, 0x77, 0x65, 0x20, 0x64, 0x65, 0x63, 0x69, 0x64, 0x65, - 0x0A, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x77, 0x68, 0x69, 0x63, 0x68, 0x20, 0x6D, 0x6F, - 0x64, 0x65, 0x20, 0x77, 0x65, 0x20, 0x72, 0x75, 0x6E, 0x20, 0x69, 0x6E, 0x2E, 0x0A, 0x0A, 0x20, - 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, - 0x75, 0x6C, 0x65, 0x2E, 0x65, 0x78, 0x69, 0x73, 0x74, 0x73, 0x28, 0x27, 0x5F, 0x74, 0x68, 0x69, - 0x72, 0x64, 0x5F, 0x70, 0x61, 0x72, 0x74, 0x79, 0x5F, 0x6D, 0x61, 0x69, 0x6E, 0x27, 0x29, 0x29, - 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x54, 0x6F, 0x20, 0x61, - 0x6C, 0x6C, 0x6F, 0x77, 0x20, 0x70, 0x65, 0x6F, 0x70, 0x6C, 0x65, 0x20, 0x74, 0x6F, 0x20, 0x65, - 0x78, 0x74, 0x65, 0x6E, 0x64, 0x20, 0x4E, 0x6F, 0x64, 0x65, 0x20, 0x69, 0x6E, 0x20, 0x64, 0x69, - 0x66, 0x66, 0x65, 0x72, 0x65, 0x6E, 0x74, 0x20, 0x77, 0x61, 0x79, 0x73, 0x2C, 0x20, 0x74, 0x68, - 0x69, 0x73, 0x20, 0x68, 0x6F, 0x6F, 0x6B, 0x20, 0x61, 0x6C, 0x6C, 0x6F, 0x77, 0x73, 0x0A, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x6F, 0x6E, 0x65, 0x20, 0x74, 0x6F, 0x20, 0x64, - 0x72, 0x6F, 0x70, 0x20, 0x61, 0x20, 0x66, 0x69, 0x6C, 0x65, 0x20, 0x6C, 0x69, 0x62, 0x2F, 0x5F, - 0x74, 0x68, 0x69, 0x72, 0x64, 0x5F, 0x70, 0x61, 0x72, 0x74, 0x79, 0x5F, 0x6D, 0x61, 0x69, 0x6E, - 0x2E, 0x6A, 0x73, 0x20, 0x69, 0x6E, 0x74, 0x6F, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x75, 0x69, - 0x6C, 0x64, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x64, 0x69, 0x72, 0x65, - 0x63, 0x74, 0x6F, 0x72, 0x79, 0x20, 0x77, 0x68, 0x69, 0x63, 0x68, 0x20, 0x77, 0x69, 0x6C, 0x6C, - 0x20, 0x62, 0x65, 0x20, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x64, 0x20, 0x69, 0x6E, 0x73, - 0x74, 0x65, 0x61, 0x64, 0x20, 0x6F, 0x66, 0x20, 0x4E, 0x6F, 0x64, 0x65, 0x27, 0x73, 0x20, 0x6E, - 0x6F, 0x72, 0x6D, 0x61, 0x6C, 0x20, 0x6C, 0x6F, 0x61, 0x64, 0x69, 0x6E, 0x67, 0x2E, 0x0A, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x6E, 0x65, 0x78, - 0x74, 0x54, 0x69, 0x63, 0x6B, 0x28, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, 0x29, - 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, - 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, - 0x27, 0x5F, 0x74, 0x68, 0x69, 0x72, 0x64, 0x5F, 0x70, 0x61, 0x72, 0x74, 0x79, 0x5F, 0x6D, 0x61, - 0x69, 0x6E, 0x27, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x29, 0x3B, 0x0A, - 0x0A, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x20, 0x65, 0x6C, 0x73, 0x65, 0x20, 0x69, 0x66, 0x20, 0x28, - 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x61, 0x72, 0x67, 0x76, 0x5B, 0x31, 0x5D, 0x20, - 0x3D, 0x3D, 0x3D, 0x20, 0x27, 0x64, 0x65, 0x62, 0x75, 0x67, 0x27, 0x29, 0x20, 0x7B, 0x0A, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x53, 0x74, 0x61, 0x72, 0x74, 0x20, 0x74, 0x68, - 0x65, 0x20, 0x64, 0x65, 0x62, 0x75, 0x67, 0x67, 0x65, 0x72, 0x20, 0x61, 0x67, 0x65, 0x6E, 0x74, - 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, - 0x75, 0x6C, 0x65, 0x2E, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x27, 0x5F, 0x64, 0x65, - 0x62, 0x75, 0x67, 0x67, 0x65, 0x72, 0x27, 0x29, 0x2E, 0x73, 0x74, 0x61, 0x72, 0x74, 0x28, 0x29, - 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x20, 0x65, 0x6C, 0x73, 0x65, 0x20, 0x69, 0x66, - 0x20, 0x28, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x61, 0x72, 0x67, 0x76, 0x5B, 0x31, - 0x5D, 0x20, 0x3D, 0x3D, 0x3D, 0x20, 0x27, 0x2D, 0x2D, 0x72, 0x65, 0x6D, 0x6F, 0x74, 0x65, 0x5F, - 0x64, 0x65, 0x62, 0x75, 0x67, 0x67, 0x69, 0x6E, 0x67, 0x5F, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x27, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x53, 0x74, - 0x61, 0x72, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x65, 0x62, 0x75, 0x67, 0x67, 0x69, 0x6E, - 0x67, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x4E, - 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x72, 0x65, 0x71, 0x75, - 0x69, 0x72, 0x65, 0x28, 0x27, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x6E, 0x61, 0x6C, 0x2F, 0x69, 0x6E, - 0x73, 0x70, 0x65, 0x63, 0x74, 0x6F, 0x72, 0x2F, 0x72, 0x65, 0x6D, 0x6F, 0x74, 0x65, 0x5F, 0x64, - 0x65, 0x62, 0x75, 0x67, 0x67, 0x69, 0x6E, 0x67, 0x5F, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x27, - 0x29, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x20, 0x65, 0x6C, 0x73, 0x65, 0x20, 0x69, - 0x66, 0x20, 0x28, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x61, 0x72, 0x67, 0x76, 0x5B, - 0x31, 0x5D, 0x20, 0x3D, 0x3D, 0x3D, 0x20, 0x27, 0x2D, 0x2D, 0x64, 0x65, 0x62, 0x75, 0x67, 0x2D, - 0x61, 0x67, 0x65, 0x6E, 0x74, 0x27, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x2F, 0x2F, 0x20, 0x53, 0x74, 0x61, 0x72, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x65, 0x62, - 0x75, 0x67, 0x67, 0x65, 0x72, 0x20, 0x61, 0x67, 0x65, 0x6E, 0x74, 0x0A, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x72, - 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x27, 0x5F, 0x64, 0x65, 0x62, 0x75, 0x67, 0x5F, 0x61, - 0x67, 0x65, 0x6E, 0x74, 0x27, 0x29, 0x2E, 0x73, 0x74, 0x61, 0x72, 0x74, 0x28, 0x29, 0x3B, 0x0A, - 0x0A, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x20, 0x65, 0x6C, 0x73, 0x65, 0x20, 0x69, 0x66, 0x20, 0x28, - 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x70, 0x72, 0x6F, 0x66, 0x50, 0x72, 0x6F, 0x63, - 0x65, 0x73, 0x73, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x4E, 0x61, 0x74, - 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, - 0x65, 0x28, 0x27, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x6E, 0x61, 0x6C, 0x2F, 0x76, 0x38, 0x5F, 0x70, - 0x72, 0x6F, 0x66, 0x5F, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x6F, 0x72, 0x27, 0x29, 0x3B, - 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x20, 0x65, 0x6C, 0x73, 0x65, 0x20, 0x7B, 0x0A, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x54, 0x68, 0x65, 0x72, 0x65, 0x20, 0x69, 0x73, - 0x20, 0x75, 0x73, 0x65, 0x72, 0x20, 0x63, 0x6F, 0x64, 0x65, 0x20, 0x74, 0x6F, 0x20, 0x62, 0x65, - 0x20, 0x72, 0x75, 0x6E, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x49, - 0x66, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x77, 0x6F, 0x72, 0x6B, - 0x65, 0x72, 0x20, 0x69, 0x6E, 0x20, 0x63, 0x6C, 0x75, 0x73, 0x74, 0x65, 0x72, 0x20, 0x6D, 0x6F, - 0x64, 0x65, 0x2C, 0x20, 0x73, 0x74, 0x61, 0x72, 0x74, 0x20, 0x75, 0x70, 0x20, 0x74, 0x68, 0x65, - 0x20, 0x63, 0x6F, 0x6D, 0x6D, 0x75, 0x6E, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x0A, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x63, 0x68, 0x61, 0x6E, 0x6E, 0x65, 0x6C, 0x2E, - 0x20, 0x54, 0x68, 0x69, 0x73, 0x20, 0x6E, 0x65, 0x65, 0x64, 0x73, 0x20, 0x74, 0x6F, 0x20, 0x62, - 0x65, 0x20, 0x64, 0x6F, 0x6E, 0x65, 0x20, 0x62, 0x65, 0x66, 0x6F, 0x72, 0x65, 0x20, 0x61, 0x6E, - 0x79, 0x20, 0x75, 0x73, 0x65, 0x72, 0x20, 0x63, 0x6F, 0x64, 0x65, 0x20, 0x67, 0x65, 0x74, 0x73, - 0x20, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x64, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x2F, 0x2F, 0x20, 0x28, 0x69, 0x6E, 0x63, 0x6C, 0x75, 0x64, 0x69, 0x6E, 0x67, 0x20, 0x70, 0x72, - 0x65, 0x6C, 0x6F, 0x61, 0x64, 0x20, 0x6D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x73, 0x29, 0x2E, 0x0A, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, - 0x73, 0x2E, 0x61, 0x72, 0x67, 0x76, 0x5B, 0x31, 0x5D, 0x20, 0x26, 0x26, 0x20, 0x70, 0x72, 0x6F, - 0x63, 0x65, 0x73, 0x73, 0x2E, 0x65, 0x6E, 0x76, 0x2E, 0x4E, 0x4F, 0x44, 0x45, 0x5F, 0x55, 0x4E, - 0x49, 0x51, 0x55, 0x45, 0x5F, 0x49, 0x44, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x20, 0x63, 0x6C, 0x75, 0x73, 0x74, 0x65, 0x72, - 0x20, 0x3D, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, - 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x27, 0x63, 0x6C, 0x75, 0x73, 0x74, 0x65, 0x72, - 0x27, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6C, 0x75, 0x73, - 0x74, 0x65, 0x72, 0x2E, 0x5F, 0x73, 0x65, 0x74, 0x75, 0x70, 0x57, 0x6F, 0x72, 0x6B, 0x65, 0x72, - 0x28, 0x29, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, - 0x4D, 0x61, 0x6B, 0x65, 0x20, 0x73, 0x75, 0x72, 0x65, 0x20, 0x69, 0x74, 0x27, 0x73, 0x20, 0x6E, - 0x6F, 0x74, 0x20, 0x61, 0x63, 0x63, 0x69, 0x64, 0x65, 0x6E, 0x74, 0x61, 0x6C, 0x6C, 0x79, 0x20, - 0x69, 0x6E, 0x68, 0x65, 0x72, 0x69, 0x74, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x63, 0x68, 0x69, - 0x6C, 0x64, 0x20, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x65, 0x73, 0x2E, 0x0A, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x65, 0x6C, 0x65, 0x74, 0x65, 0x20, 0x70, 0x72, 0x6F, - 0x63, 0x65, 0x73, 0x73, 0x2E, 0x65, 0x6E, 0x76, 0x2E, 0x4E, 0x4F, 0x44, 0x45, 0x5F, 0x55, 0x4E, - 0x49, 0x51, 0x55, 0x45, 0x5F, 0x49, 0x44, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7D, - 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x70, 0x72, 0x6F, 0x63, - 0x65, 0x73, 0x73, 0x2E, 0x5F, 0x65, 0x76, 0x61, 0x6C, 0x20, 0x21, 0x3D, 0x20, 0x6E, 0x75, 0x6C, - 0x6C, 0x20, 0x26, 0x26, 0x20, 0x21, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x5F, 0x66, - 0x6F, 0x72, 0x63, 0x65, 0x52, 0x65, 0x70, 0x6C, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x55, 0x73, 0x65, 0x72, 0x20, 0x70, 0x61, 0x73, 0x73, - 0x65, 0x64, 0x20, 0x27, 0x2D, 0x65, 0x27, 0x20, 0x6F, 0x72, 0x20, 0x27, 0x2D, 0x2D, 0x65, 0x76, - 0x61, 0x6C, 0x27, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6D, 0x65, 0x6E, 0x74, 0x73, 0x20, 0x74, 0x6F, - 0x20, 0x4E, 0x6F, 0x64, 0x65, 0x20, 0x77, 0x69, 0x74, 0x68, 0x6F, 0x75, 0x74, 0x20, 0x27, 0x2D, - 0x69, 0x27, 0x20, 0x6F, 0x72, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, - 0x20, 0x27, 0x2D, 0x2D, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x27, - 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x72, 0x65, 0x6C, 0x6F, 0x61, 0x64, - 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x73, 0x28, 0x29, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x20, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x6E, - 0x61, 0x6C, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x20, 0x3D, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, - 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, - 0x27, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x6E, 0x61, 0x6C, 0x2F, 0x6D, 0x6F, 0x64, 0x75, 0x6C, 0x65, - 0x27, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6E, 0x74, 0x65, - 0x72, 0x6E, 0x61, 0x6C, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x61, 0x64, 0x64, 0x42, 0x75, - 0x69, 0x6C, 0x74, 0x69, 0x6E, 0x4C, 0x69, 0x62, 0x73, 0x54, 0x6F, 0x4F, 0x62, 0x6A, 0x65, 0x63, - 0x74, 0x28, 0x67, 0x6C, 0x6F, 0x62, 0x61, 0x6C, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x72, 0x75, 0x6E, 0x28, 0x28, 0x29, 0x20, 0x3D, 0x3E, 0x20, 0x7B, 0x0A, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x76, 0x61, 0x6C, 0x53, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x28, 0x27, 0x5B, 0x65, 0x76, 0x61, 0x6C, 0x5D, 0x27, 0x29, 0x3B, 0x0A, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x7D, 0x20, 0x65, 0x6C, 0x73, 0x65, 0x20, 0x69, 0x66, 0x20, 0x28, 0x70, 0x72, 0x6F, 0x63, - 0x65, 0x73, 0x73, 0x2E, 0x61, 0x72, 0x67, 0x76, 0x5B, 0x31, 0x5D, 0x29, 0x20, 0x7B, 0x0A, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x6D, 0x61, 0x6B, 0x65, 0x20, 0x70, - 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x61, 0x72, 0x67, 0x76, 0x5B, 0x31, 0x5D, 0x20, 0x69, - 0x6E, 0x74, 0x6F, 0x20, 0x61, 0x20, 0x66, 0x75, 0x6C, 0x6C, 0x20, 0x70, 0x61, 0x74, 0x68, 0x0A, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x20, 0x70, 0x61, - 0x74, 0x68, 0x20, 0x3D, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, - 0x65, 0x2E, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x27, 0x70, 0x61, 0x74, 0x68, 0x27, - 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x72, 0x6F, 0x63, 0x65, - 0x73, 0x73, 0x2E, 0x61, 0x72, 0x67, 0x76, 0x5B, 0x31, 0x5D, 0x20, 0x3D, 0x20, 0x70, 0x61, 0x74, - 0x68, 0x2E, 0x72, 0x65, 0x73, 0x6F, 0x6C, 0x76, 0x65, 0x28, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, - 0x73, 0x2E, 0x61, 0x72, 0x67, 0x76, 0x5B, 0x31, 0x5D, 0x29, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x20, 0x4D, 0x6F, 0x64, 0x75, 0x6C, - 0x65, 0x20, 0x3D, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, - 0x2E, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x27, 0x6D, 0x6F, 0x64, 0x75, 0x6C, 0x65, - 0x27, 0x29, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, - 0x63, 0x68, 0x65, 0x63, 0x6B, 0x20, 0x69, 0x66, 0x20, 0x75, 0x73, 0x65, 0x72, 0x20, 0x70, 0x61, - 0x73, 0x73, 0x65, 0x64, 0x20, 0x60, 0x2D, 0x63, 0x60, 0x20, 0x6F, 0x72, 0x20, 0x60, 0x2D, 0x2D, - 0x63, 0x68, 0x65, 0x63, 0x6B, 0x60, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6D, 0x65, 0x6E, 0x74, 0x73, - 0x20, 0x74, 0x6F, 0x20, 0x4E, 0x6F, 0x64, 0x65, 0x2E, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x5F, 0x73, - 0x79, 0x6E, 0x74, 0x61, 0x78, 0x5F, 0x63, 0x68, 0x65, 0x63, 0x6B, 0x5F, 0x6F, 0x6E, 0x6C, 0x79, - 0x20, 0x21, 0x3D, 0x20, 0x6E, 0x75, 0x6C, 0x6C, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x20, 0x76, 0x6D, 0x20, 0x3D, - 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x72, 0x65, - 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x27, 0x76, 0x6D, 0x27, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x20, 0x66, 0x73, 0x20, - 0x3D, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x72, - 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x27, 0x66, 0x73, 0x27, 0x29, 0x3B, 0x0A, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x20, 0x69, 0x6E, - 0x74, 0x65, 0x72, 0x6E, 0x61, 0x6C, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x20, 0x3D, 0x20, 0x4E, - 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x72, 0x65, 0x71, 0x75, - 0x69, 0x72, 0x65, 0x28, 0x27, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x6E, 0x61, 0x6C, 0x2F, 0x6D, 0x6F, - 0x64, 0x75, 0x6C, 0x65, 0x27, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x72, 0x65, 0x61, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x6F, - 0x75, 0x72, 0x63, 0x65, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, - 0x6F, 0x6E, 0x73, 0x74, 0x20, 0x66, 0x69, 0x6C, 0x65, 0x6E, 0x61, 0x6D, 0x65, 0x20, 0x3D, 0x20, - 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x5F, 0x72, 0x65, 0x73, 0x6F, 0x6C, 0x76, 0x65, 0x46, - 0x69, 0x6C, 0x65, 0x6E, 0x61, 0x6D, 0x65, 0x28, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, - 0x61, 0x72, 0x67, 0x76, 0x5B, 0x31, 0x5D, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x73, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x20, 0x3D, - 0x20, 0x66, 0x73, 0x2E, 0x72, 0x65, 0x61, 0x64, 0x46, 0x69, 0x6C, 0x65, 0x53, 0x79, 0x6E, 0x63, - 0x28, 0x66, 0x69, 0x6C, 0x65, 0x6E, 0x61, 0x6D, 0x65, 0x2C, 0x20, 0x27, 0x75, 0x74, 0x66, 0x2D, - 0x38, 0x27, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, - 0x2F, 0x20, 0x72, 0x65, 0x6D, 0x6F, 0x76, 0x65, 0x20, 0x73, 0x68, 0x65, 0x62, 0x61, 0x6E, 0x67, - 0x20, 0x61, 0x6E, 0x64, 0x20, 0x42, 0x4F, 0x4D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x73, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x20, 0x3D, 0x20, 0x69, 0x6E, 0x74, 0x65, - 0x72, 0x6E, 0x61, 0x6C, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x73, 0x74, 0x72, 0x69, 0x70, - 0x42, 0x4F, 0x4D, 0x28, 0x73, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x2E, 0x72, 0x65, 0x70, 0x6C, 0x61, - 0x63, 0x65, 0x28, 0x2F, 0x5E, 0x23, 0x21, 0x2E, 0x2A, 0x2F, 0x2C, 0x20, 0x27, 0x27, 0x29, 0x29, - 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x77, - 0x72, 0x61, 0x70, 0x20, 0x69, 0x74, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x73, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x20, 0x3D, 0x20, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, - 0x2E, 0x77, 0x72, 0x61, 0x70, 0x28, 0x73, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x29, 0x3B, 0x0A, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x63, 0x6F, 0x6D, 0x70, - 0x69, 0x6C, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2C, 0x20, - 0x74, 0x68, 0x69, 0x73, 0x20, 0x77, 0x69, 0x6C, 0x6C, 0x20, 0x74, 0x68, 0x72, 0x6F, 0x77, 0x20, - 0x69, 0x66, 0x20, 0x69, 0x74, 0x20, 0x66, 0x61, 0x69, 0x6C, 0x73, 0x0A, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6E, 0x65, 0x77, 0x20, 0x76, 0x6D, 0x2E, 0x53, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x28, 0x73, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x2C, 0x20, 0x7B, 0x66, 0x69, 0x6C, - 0x65, 0x6E, 0x61, 0x6D, 0x65, 0x3A, 0x20, 0x66, 0x69, 0x6C, 0x65, 0x6E, 0x61, 0x6D, 0x65, 0x2C, - 0x20, 0x64, 0x69, 0x73, 0x70, 0x6C, 0x61, 0x79, 0x45, 0x72, 0x72, 0x6F, 0x72, 0x73, 0x3A, 0x20, - 0x74, 0x72, 0x75, 0x65, 0x7D, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x65, 0x78, 0x69, 0x74, 0x28, 0x30, - 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x0A, 0x0A, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x72, 0x65, 0x6C, 0x6F, 0x61, 0x64, 0x4D, 0x6F, 0x64, - 0x75, 0x6C, 0x65, 0x73, 0x28, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x72, 0x75, 0x6E, 0x28, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x72, 0x75, 0x6E, 0x4D, 0x61, - 0x69, 0x6E, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x20, 0x65, 0x6C, 0x73, - 0x65, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x72, 0x65, 0x6C, - 0x6F, 0x61, 0x64, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x73, 0x28, 0x29, 0x3B, 0x0A, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x49, 0x66, 0x20, 0x2D, 0x69, 0x20, 0x6F, - 0x72, 0x20, 0x2D, 0x2D, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x20, - 0x77, 0x65, 0x72, 0x65, 0x20, 0x70, 0x61, 0x73, 0x73, 0x65, 0x64, 0x2C, 0x20, 0x6F, 0x72, 0x20, - 0x73, 0x74, 0x64, 0x69, 0x6E, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x54, 0x54, 0x59, 0x2E, 0x0A, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x70, 0x72, 0x6F, 0x63, - 0x65, 0x73, 0x73, 0x2E, 0x5F, 0x66, 0x6F, 0x72, 0x63, 0x65, 0x52, 0x65, 0x70, 0x6C, 0x29, 0x20, - 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x52, - 0x45, 0x50, 0x4C, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, - 0x6E, 0x73, 0x74, 0x20, 0x63, 0x6C, 0x69, 0x52, 0x65, 0x70, 0x6C, 0x20, 0x3D, 0x20, 0x4E, 0x61, - 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x72, 0x65, 0x71, 0x75, 0x69, - 0x72, 0x65, 0x28, 0x27, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x6E, 0x61, 0x6C, 0x2F, 0x72, 0x65, 0x70, - 0x6C, 0x27, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, - 0x6C, 0x69, 0x52, 0x65, 0x70, 0x6C, 0x2E, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x49, 0x6E, 0x74, - 0x65, 0x72, 0x6E, 0x61, 0x6C, 0x52, 0x65, 0x70, 0x6C, 0x28, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, - 0x73, 0x2E, 0x65, 0x6E, 0x76, 0x2C, 0x20, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, - 0x65, 0x72, 0x72, 0x2C, 0x20, 0x72, 0x65, 0x70, 0x6C, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x65, 0x72, 0x72, - 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x74, 0x68, 0x72, 0x6F, 0x77, 0x20, 0x65, 0x72, 0x72, 0x3B, 0x0A, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x70, 0x6C, 0x2E, 0x6F, 0x6E, 0x28, 0x27, - 0x65, 0x78, 0x69, 0x74, 0x27, 0x2C, 0x20, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, - 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x72, 0x65, 0x70, 0x6C, 0x2E, 0x5F, 0x66, 0x6C, 0x75, 0x73, - 0x68, 0x69, 0x6E, 0x67, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x70, 0x6C, 0x2E, 0x70, 0x61, 0x75, - 0x73, 0x65, 0x28, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6E, 0x20, 0x72, 0x65, 0x70, - 0x6C, 0x2E, 0x6F, 0x6E, 0x63, 0x65, 0x28, 0x27, 0x66, 0x6C, 0x75, 0x73, 0x68, 0x48, 0x69, 0x73, - 0x74, 0x6F, 0x72, 0x79, 0x27, 0x2C, 0x20, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, - 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x65, 0x78, - 0x69, 0x74, 0x28, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, - 0x65, 0x78, 0x69, 0x74, 0x28, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x7D, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x7D, 0x29, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x69, 0x66, 0x20, 0x28, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x5F, 0x65, 0x76, - 0x61, 0x6C, 0x20, 0x21, 0x3D, 0x20, 0x6E, 0x75, 0x6C, 0x6C, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x55, 0x73, 0x65, - 0x72, 0x20, 0x70, 0x61, 0x73, 0x73, 0x65, 0x64, 0x20, 0x27, 0x2D, 0x65, 0x27, 0x20, 0x6F, 0x72, - 0x20, 0x27, 0x2D, 0x2D, 0x65, 0x76, 0x61, 0x6C, 0x27, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x76, 0x61, 0x6C, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x28, 0x27, 0x5B, 0x65, 0x76, 0x61, 0x6C, 0x5D, 0x27, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x7D, 0x20, 0x65, 0x6C, 0x73, 0x65, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x52, 0x65, 0x61, 0x64, 0x20, 0x61, 0x6C, 0x6C, 0x20, 0x6F, - 0x66, 0x20, 0x73, 0x74, 0x64, 0x69, 0x6E, 0x20, 0x2D, 0x20, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, - 0x65, 0x20, 0x69, 0x74, 0x2E, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x73, 0x74, 0x64, 0x69, 0x6E, 0x2E, 0x73, 0x65, - 0x74, 0x45, 0x6E, 0x63, 0x6F, 0x64, 0x69, 0x6E, 0x67, 0x28, 0x27, 0x75, 0x74, 0x66, 0x38, 0x27, - 0x29, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, - 0x72, 0x20, 0x63, 0x6F, 0x64, 0x65, 0x20, 0x3D, 0x20, 0x27, 0x27, 0x3B, 0x0A, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x73, - 0x74, 0x64, 0x69, 0x6E, 0x2E, 0x6F, 0x6E, 0x28, 0x27, 0x64, 0x61, 0x74, 0x61, 0x27, 0x2C, 0x20, - 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, 0x64, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, 0x64, 0x65, 0x20, 0x2B, - 0x3D, 0x20, 0x64, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7D, - 0x29, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x72, - 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x73, 0x74, 0x64, 0x69, 0x6E, 0x2E, 0x6F, 0x6E, 0x28, 0x27, - 0x65, 0x6E, 0x64, 0x27, 0x2C, 0x20, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, 0x29, - 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, - 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x5F, 0x65, 0x76, 0x61, 0x6C, 0x20, 0x3D, 0x20, 0x63, - 0x6F, 0x64, 0x65, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x65, 0x76, 0x61, 0x6C, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x28, 0x27, 0x5B, 0x73, 0x74, - 0x64, 0x69, 0x6E, 0x5D, 0x27, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x7D, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x0A, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x0A, 0x20, 0x20, - 0x7D, 0x0A, 0x0A, 0x20, 0x20, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x73, 0x65, - 0x74, 0x75, 0x70, 0x50, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x4F, 0x62, 0x6A, 0x65, 0x63, 0x74, - 0x28, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, - 0x2E, 0x5F, 0x73, 0x65, 0x74, 0x75, 0x70, 0x50, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x4F, 0x62, - 0x6A, 0x65, 0x63, 0x74, 0x28, 0x70, 0x75, 0x73, 0x68, 0x56, 0x61, 0x6C, 0x75, 0x65, 0x54, 0x6F, - 0x41, 0x72, 0x72, 0x61, 0x79, 0x29, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6E, - 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x70, 0x75, 0x73, 0x68, 0x56, 0x61, 0x6C, 0x75, 0x65, 0x54, - 0x6F, 0x41, 0x72, 0x72, 0x61, 0x79, 0x28, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x66, 0x6F, 0x72, 0x20, 0x28, 0x76, 0x61, 0x72, 0x20, 0x69, 0x20, 0x3D, 0x20, 0x30, 0x3B, - 0x20, 0x69, 0x20, 0x3C, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6D, 0x65, 0x6E, 0x74, 0x73, 0x2E, 0x6C, - 0x65, 0x6E, 0x67, 0x74, 0x68, 0x3B, 0x20, 0x69, 0x2B, 0x2B, 0x29, 0x0A, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x70, 0x75, 0x73, 0x68, 0x28, 0x61, 0x72, - 0x67, 0x75, 0x6D, 0x65, 0x6E, 0x74, 0x73, 0x5B, 0x69, 0x5D, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, - 0x20, 0x7D, 0x0A, 0x20, 0x20, 0x7D, 0x0A, 0x0A, 0x20, 0x20, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, - 0x6F, 0x6E, 0x20, 0x73, 0x65, 0x74, 0x75, 0x70, 0x47, 0x6C, 0x6F, 0x62, 0x61, 0x6C, 0x56, 0x61, - 0x72, 0x69, 0x61, 0x62, 0x6C, 0x65, 0x73, 0x28, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, - 0x67, 0x6C, 0x6F, 0x62, 0x61, 0x6C, 0x2E, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x20, 0x3D, - 0x20, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, - 0x6E, 0x73, 0x74, 0x20, 0x75, 0x74, 0x69, 0x6C, 0x20, 0x3D, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, - 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, - 0x27, 0x75, 0x74, 0x69, 0x6C, 0x27, 0x29, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, - 0x20, 0x44, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x20, 0x47, 0x4C, 0x4F, 0x42, 0x41, - 0x4C, 0x20, 0x61, 0x6E, 0x64, 0x20, 0x72, 0x6F, 0x6F, 0x74, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x5B, - 0x27, 0x47, 0x4C, 0x4F, 0x42, 0x41, 0x4C, 0x27, 0x2C, 0x20, 0x27, 0x72, 0x6F, 0x6F, 0x74, 0x27, - 0x5D, 0x2E, 0x66, 0x6F, 0x72, 0x45, 0x61, 0x63, 0x68, 0x28, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, - 0x6F, 0x6E, 0x28, 0x6E, 0x61, 0x6D, 0x65, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x2F, 0x2F, 0x20, 0x67, 0x65, 0x74, 0x74, 0x65, 0x72, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x20, 0x67, 0x65, 0x74, 0x20, 0x3D, 0x20, 0x75, 0x74, 0x69, - 0x6C, 0x2E, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x28, 0x66, 0x75, 0x6E, 0x63, - 0x74, 0x69, 0x6F, 0x6E, 0x28, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6E, 0x20, 0x74, 0x68, 0x69, 0x73, 0x3B, 0x0A, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x7D, 0x2C, 0x20, 0x60, 0x27, 0x24, 0x7B, 0x6E, 0x61, 0x6D, 0x65, 0x7D, - 0x27, 0x20, 0x69, 0x73, 0x20, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x2C, - 0x20, 0x75, 0x73, 0x65, 0x20, 0x27, 0x67, 0x6C, 0x6F, 0x62, 0x61, 0x6C, 0x27, 0x60, 0x29, 0x3B, - 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x73, 0x65, 0x74, 0x74, 0x65, 0x72, - 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x20, 0x73, 0x65, 0x74, - 0x20, 0x3D, 0x20, 0x75, 0x74, 0x69, 0x6C, 0x2E, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, - 0x65, 0x28, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, 0x76, 0x61, 0x6C, 0x75, 0x65, - 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x4F, 0x62, 0x6A, 0x65, - 0x63, 0x74, 0x2E, 0x64, 0x65, 0x66, 0x69, 0x6E, 0x65, 0x50, 0x72, 0x6F, 0x70, 0x65, 0x72, 0x74, - 0x79, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2C, 0x20, 0x6E, 0x61, 0x6D, 0x65, 0x2C, 0x20, 0x7B, 0x0A, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, 0x6E, 0x66, 0x69, 0x67, - 0x75, 0x72, 0x61, 0x62, 0x6C, 0x65, 0x3A, 0x20, 0x74, 0x72, 0x75, 0x65, 0x2C, 0x0A, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x77, 0x72, 0x69, 0x74, 0x61, 0x62, 0x6C, 0x65, - 0x3A, 0x20, 0x74, 0x72, 0x75, 0x65, 0x2C, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x65, 0x6E, 0x75, 0x6D, 0x65, 0x72, 0x61, 0x62, 0x6C, 0x65, 0x3A, 0x20, 0x74, 0x72, - 0x75, 0x65, 0x2C, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, - 0x6C, 0x75, 0x65, 0x3A, 0x20, 0x76, 0x61, 0x6C, 0x75, 0x65, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x7D, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x2C, 0x20, - 0x60, 0x27, 0x24, 0x7B, 0x6E, 0x61, 0x6D, 0x65, 0x7D, 0x27, 0x20, 0x69, 0x73, 0x20, 0x64, 0x65, - 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x2C, 0x20, 0x75, 0x73, 0x65, 0x20, 0x27, 0x67, - 0x6C, 0x6F, 0x62, 0x61, 0x6C, 0x27, 0x60, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x2F, 0x2F, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6E, 0x65, 0x20, 0x70, 0x72, 0x6F, 0x70, 0x65, 0x72, - 0x74, 0x79, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x4F, 0x62, 0x6A, 0x65, 0x63, 0x74, 0x2E, - 0x64, 0x65, 0x66, 0x69, 0x6E, 0x65, 0x50, 0x72, 0x6F, 0x70, 0x65, 0x72, 0x74, 0x79, 0x28, 0x67, - 0x6C, 0x6F, 0x62, 0x61, 0x6C, 0x2C, 0x20, 0x6E, 0x61, 0x6D, 0x65, 0x2C, 0x20, 0x7B, 0x20, 0x67, - 0x65, 0x74, 0x2C, 0x20, 0x73, 0x65, 0x74, 0x2C, 0x20, 0x63, 0x6F, 0x6E, 0x66, 0x69, 0x67, 0x75, - 0x72, 0x61, 0x62, 0x6C, 0x65, 0x3A, 0x20, 0x74, 0x72, 0x75, 0x65, 0x20, 0x7D, 0x29, 0x3B, 0x0A, - 0x20, 0x20, 0x20, 0x20, 0x7D, 0x29, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x67, 0x6C, 0x6F, - 0x62, 0x61, 0x6C, 0x2E, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x20, 0x3D, 0x20, 0x4E, 0x61, 0x74, - 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, - 0x65, 0x28, 0x27, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x27, 0x29, 0x2E, 0x42, 0x75, 0x66, 0x66, - 0x65, 0x72, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, - 0x64, 0x6F, 0x6D, 0x61, 0x69, 0x6E, 0x20, 0x3D, 0x20, 0x6E, 0x75, 0x6C, 0x6C, 0x3B, 0x0A, 0x20, - 0x20, 0x20, 0x20, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x5F, 0x65, 0x78, 0x69, 0x74, - 0x69, 0x6E, 0x67, 0x20, 0x3D, 0x20, 0x66, 0x61, 0x6C, 0x73, 0x65, 0x3B, 0x0A, 0x20, 0x20, 0x7D, - 0x0A, 0x0A, 0x20, 0x20, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x73, 0x65, 0x74, - 0x75, 0x70, 0x47, 0x6C, 0x6F, 0x62, 0x61, 0x6C, 0x54, 0x69, 0x6D, 0x65, 0x6F, 0x75, 0x74, 0x73, - 0x28, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x20, 0x74, - 0x69, 0x6D, 0x65, 0x72, 0x73, 0x20, 0x3D, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, - 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x27, 0x74, 0x69, - 0x6D, 0x65, 0x72, 0x73, 0x27, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x67, 0x6C, 0x6F, 0x62, - 0x61, 0x6C, 0x2E, 0x63, 0x6C, 0x65, 0x61, 0x72, 0x49, 0x6D, 0x6D, 0x65, 0x64, 0x69, 0x61, 0x74, - 0x65, 0x20, 0x3D, 0x20, 0x74, 0x69, 0x6D, 0x65, 0x72, 0x73, 0x2E, 0x63, 0x6C, 0x65, 0x61, 0x72, - 0x49, 0x6D, 0x6D, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x67, - 0x6C, 0x6F, 0x62, 0x61, 0x6C, 0x2E, 0x63, 0x6C, 0x65, 0x61, 0x72, 0x49, 0x6E, 0x74, 0x65, 0x72, - 0x76, 0x61, 0x6C, 0x20, 0x3D, 0x20, 0x74, 0x69, 0x6D, 0x65, 0x72, 0x73, 0x2E, 0x63, 0x6C, 0x65, - 0x61, 0x72, 0x49, 0x6E, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6C, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, - 0x67, 0x6C, 0x6F, 0x62, 0x61, 0x6C, 0x2E, 0x63, 0x6C, 0x65, 0x61, 0x72, 0x54, 0x69, 0x6D, 0x65, - 0x6F, 0x75, 0x74, 0x20, 0x3D, 0x20, 0x74, 0x69, 0x6D, 0x65, 0x72, 0x73, 0x2E, 0x63, 0x6C, 0x65, - 0x61, 0x72, 0x54, 0x69, 0x6D, 0x65, 0x6F, 0x75, 0x74, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x67, - 0x6C, 0x6F, 0x62, 0x61, 0x6C, 0x2E, 0x73, 0x65, 0x74, 0x49, 0x6D, 0x6D, 0x65, 0x64, 0x69, 0x61, - 0x74, 0x65, 0x20, 0x3D, 0x20, 0x74, 0x69, 0x6D, 0x65, 0x72, 0x73, 0x2E, 0x73, 0x65, 0x74, 0x49, - 0x6D, 0x6D, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x67, 0x6C, - 0x6F, 0x62, 0x61, 0x6C, 0x2E, 0x73, 0x65, 0x74, 0x49, 0x6E, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6C, - 0x20, 0x3D, 0x20, 0x74, 0x69, 0x6D, 0x65, 0x72, 0x73, 0x2E, 0x73, 0x65, 0x74, 0x49, 0x6E, 0x74, - 0x65, 0x72, 0x76, 0x61, 0x6C, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x67, 0x6C, 0x6F, 0x62, 0x61, - 0x6C, 0x2E, 0x73, 0x65, 0x74, 0x54, 0x69, 0x6D, 0x65, 0x6F, 0x75, 0x74, 0x20, 0x3D, 0x20, 0x74, - 0x69, 0x6D, 0x65, 0x72, 0x73, 0x2E, 0x73, 0x65, 0x74, 0x54, 0x69, 0x6D, 0x65, 0x6F, 0x75, 0x74, - 0x3B, 0x0A, 0x20, 0x20, 0x7D, 0x0A, 0x0A, 0x20, 0x20, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, - 0x6E, 0x20, 0x73, 0x65, 0x74, 0x75, 0x70, 0x47, 0x6C, 0x6F, 0x62, 0x61, 0x6C, 0x43, 0x6F, 0x6E, - 0x73, 0x6F, 0x6C, 0x65, 0x28, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, - 0x20, 0x69, 0x6E, 0x73, 0x70, 0x65, 0x63, 0x74, 0x6F, 0x72, 0x43, 0x6F, 0x6E, 0x73, 0x6F, 0x6C, - 0x65, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x77, 0x72, 0x61, 0x70, 0x43, - 0x6F, 0x6E, 0x73, 0x6F, 0x6C, 0x65, 0x43, 0x61, 0x6C, 0x6C, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, - 0x69, 0x66, 0x20, 0x28, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x69, 0x6E, 0x73, 0x70, - 0x65, 0x63, 0x74, 0x6F, 0x72, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, - 0x6E, 0x73, 0x70, 0x65, 0x63, 0x74, 0x6F, 0x72, 0x43, 0x6F, 0x6E, 0x73, 0x6F, 0x6C, 0x65, 0x20, - 0x3D, 0x20, 0x67, 0x6C, 0x6F, 0x62, 0x61, 0x6C, 0x2E, 0x63, 0x6F, 0x6E, 0x73, 0x6F, 0x6C, 0x65, - 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x77, 0x72, 0x61, 0x70, 0x43, 0x6F, 0x6E, 0x73, - 0x6F, 0x6C, 0x65, 0x43, 0x61, 0x6C, 0x6C, 0x20, 0x3D, 0x20, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, - 0x73, 0x2E, 0x69, 0x6E, 0x73, 0x70, 0x65, 0x63, 0x74, 0x6F, 0x72, 0x2E, 0x77, 0x72, 0x61, 0x70, - 0x43, 0x6F, 0x6E, 0x73, 0x6F, 0x6C, 0x65, 0x43, 0x61, 0x6C, 0x6C, 0x3B, 0x0A, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x64, 0x65, 0x6C, 0x65, 0x74, 0x65, 0x20, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, - 0x73, 0x2E, 0x69, 0x6E, 0x73, 0x70, 0x65, 0x63, 0x74, 0x6F, 0x72, 0x3B, 0x0A, 0x20, 0x20, 0x20, - 0x20, 0x7D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x6F, - 0x6C, 0x65, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x4F, 0x62, 0x6A, 0x65, 0x63, 0x74, 0x2E, 0x64, - 0x65, 0x66, 0x69, 0x6E, 0x65, 0x50, 0x72, 0x6F, 0x70, 0x65, 0x72, 0x74, 0x79, 0x28, 0x67, 0x6C, - 0x6F, 0x62, 0x61, 0x6C, 0x2C, 0x20, 0x27, 0x63, 0x6F, 0x6E, 0x73, 0x6F, 0x6C, 0x65, 0x27, 0x2C, - 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, 0x6E, 0x66, 0x69, 0x67, 0x75, - 0x72, 0x61, 0x62, 0x6C, 0x65, 0x3A, 0x20, 0x74, 0x72, 0x75, 0x65, 0x2C, 0x0A, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x65, 0x6E, 0x75, 0x6D, 0x65, 0x72, 0x61, 0x62, 0x6C, 0x65, 0x3A, 0x20, 0x74, - 0x72, 0x75, 0x65, 0x2C, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x67, 0x65, 0x74, 0x3A, 0x20, - 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x21, 0x63, 0x6F, 0x6E, 0x73, 0x6F, 0x6C, - 0x65, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, - 0x6F, 0x6E, 0x73, 0x6F, 0x6C, 0x65, 0x20, 0x3D, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, - 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x27, 0x63, - 0x6F, 0x6E, 0x73, 0x6F, 0x6C, 0x65, 0x27, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x69, 0x6E, 0x73, 0x74, 0x61, 0x6C, 0x6C, 0x49, 0x6E, 0x73, 0x70, 0x65, - 0x63, 0x74, 0x6F, 0x72, 0x43, 0x6F, 0x6E, 0x73, 0x6F, 0x6C, 0x65, 0x49, 0x66, 0x4E, 0x65, 0x65, - 0x64, 0x65, 0x64, 0x28, 0x63, 0x6F, 0x6E, 0x73, 0x6F, 0x6C, 0x65, 0x2C, 0x0A, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6E, 0x73, 0x70, 0x65, 0x63, 0x74, 0x6F, 0x72, - 0x43, 0x6F, 0x6E, 0x73, 0x6F, 0x6C, 0x65, 0x2C, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x77, 0x72, 0x61, 0x70, 0x43, 0x6F, 0x6E, 0x73, 0x6F, 0x6C, 0x65, 0x43, 0x61, - 0x6C, 0x6C, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x0A, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6E, 0x20, 0x63, 0x6F, - 0x6E, 0x73, 0x6F, 0x6C, 0x65, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x0A, 0x20, - 0x20, 0x20, 0x20, 0x7D, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x7D, 0x0A, 0x0A, 0x20, 0x20, 0x66, 0x75, - 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x69, 0x6E, 0x73, 0x74, 0x61, 0x6C, 0x6C, 0x49, 0x6E, - 0x73, 0x70, 0x65, 0x63, 0x74, 0x6F, 0x72, 0x43, 0x6F, 0x6E, 0x73, 0x6F, 0x6C, 0x65, 0x49, 0x66, - 0x4E, 0x65, 0x65, 0x64, 0x65, 0x64, 0x28, 0x63, 0x6F, 0x6E, 0x73, 0x6F, 0x6C, 0x65, 0x2C, 0x0A, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6E, 0x73, 0x70, 0x65, - 0x63, 0x74, 0x6F, 0x72, 0x43, 0x6F, 0x6E, 0x73, 0x6F, 0x6C, 0x65, 0x2C, 0x0A, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x77, 0x72, 0x61, 0x70, 0x43, 0x6F, 0x6E, 0x73, - 0x6F, 0x6C, 0x65, 0x43, 0x61, 0x6C, 0x6C, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x69, - 0x66, 0x20, 0x28, 0x21, 0x69, 0x6E, 0x73, 0x70, 0x65, 0x63, 0x74, 0x6F, 0x72, 0x43, 0x6F, 0x6E, - 0x73, 0x6F, 0x6C, 0x65, 0x29, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, - 0x72, 0x6E, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x20, 0x63, 0x6F, - 0x6E, 0x66, 0x69, 0x67, 0x20, 0x3D, 0x20, 0x7B, 0x7D, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x66, - 0x6F, 0x72, 0x20, 0x28, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x20, 0x6B, 0x65, 0x79, 0x20, 0x6F, 0x66, - 0x20, 0x4F, 0x62, 0x6A, 0x65, 0x63, 0x74, 0x2E, 0x6B, 0x65, 0x79, 0x73, 0x28, 0x63, 0x6F, 0x6E, - 0x73, 0x6F, 0x6C, 0x65, 0x29, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, - 0x66, 0x20, 0x28, 0x21, 0x69, 0x6E, 0x73, 0x70, 0x65, 0x63, 0x74, 0x6F, 0x72, 0x43, 0x6F, 0x6E, - 0x73, 0x6F, 0x6C, 0x65, 0x2E, 0x68, 0x61, 0x73, 0x4F, 0x77, 0x6E, 0x50, 0x72, 0x6F, 0x70, 0x65, - 0x72, 0x74, 0x79, 0x28, 0x6B, 0x65, 0x79, 0x29, 0x29, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x63, 0x6F, 0x6E, 0x74, 0x69, 0x6E, 0x75, 0x65, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x49, 0x66, 0x20, 0x6E, 0x6F, 0x64, 0x65, 0x20, 0x63, 0x6F, 0x6E, - 0x73, 0x6F, 0x6C, 0x65, 0x20, 0x68, 0x61, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x61, 0x6D, - 0x65, 0x20, 0x6D, 0x65, 0x74, 0x68, 0x6F, 0x64, 0x20, 0x61, 0x73, 0x20, 0x69, 0x6E, 0x73, 0x70, - 0x65, 0x63, 0x74, 0x6F, 0x72, 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x6F, 0x6C, 0x65, 0x2C, 0x0A, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x74, 0x68, 0x65, 0x6E, 0x20, 0x77, 0x72, 0x61, - 0x70, 0x20, 0x74, 0x68, 0x65, 0x73, 0x65, 0x20, 0x74, 0x77, 0x6F, 0x20, 0x6D, 0x65, 0x74, 0x68, - 0x6F, 0x64, 0x73, 0x20, 0x69, 0x6E, 0x74, 0x6F, 0x20, 0x6F, 0x6E, 0x65, 0x2E, 0x20, 0x4E, 0x61, - 0x74, 0x69, 0x76, 0x65, 0x20, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x20, 0x77, 0x69, 0x6C, - 0x6C, 0x20, 0x70, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x2F, 0x2F, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6F, 0x72, 0x69, 0x67, 0x69, 0x6E, 0x61, 0x6C, - 0x20, 0x73, 0x74, 0x61, 0x63, 0x6B, 0x2E, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, - 0x6E, 0x73, 0x6F, 0x6C, 0x65, 0x5B, 0x6B, 0x65, 0x79, 0x5D, 0x20, 0x3D, 0x20, 0x77, 0x72, 0x61, - 0x70, 0x43, 0x6F, 0x6E, 0x73, 0x6F, 0x6C, 0x65, 0x43, 0x61, 0x6C, 0x6C, 0x28, 0x69, 0x6E, 0x73, - 0x70, 0x65, 0x63, 0x74, 0x6F, 0x72, 0x43, 0x6F, 0x6E, 0x73, 0x6F, 0x6C, 0x65, 0x5B, 0x6B, 0x65, - 0x79, 0x5D, 0x2C, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x6F, 0x6C, 0x65, - 0x5B, 0x6B, 0x65, 0x79, 0x5D, 0x2C, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, 0x6E, 0x66, - 0x69, 0x67, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x66, - 0x6F, 0x72, 0x20, 0x28, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x20, 0x6B, 0x65, 0x79, 0x20, 0x6F, 0x66, - 0x20, 0x4F, 0x62, 0x6A, 0x65, 0x63, 0x74, 0x2E, 0x6B, 0x65, 0x79, 0x73, 0x28, 0x69, 0x6E, 0x73, - 0x70, 0x65, 0x63, 0x74, 0x6F, 0x72, 0x43, 0x6F, 0x6E, 0x73, 0x6F, 0x6C, 0x65, 0x29, 0x29, 0x20, - 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x63, 0x6F, 0x6E, 0x73, - 0x6F, 0x6C, 0x65, 0x2E, 0x68, 0x61, 0x73, 0x4F, 0x77, 0x6E, 0x50, 0x72, 0x6F, 0x70, 0x65, 0x72, - 0x74, 0x79, 0x28, 0x6B, 0x65, 0x79, 0x29, 0x29, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x63, 0x6F, 0x6E, 0x74, 0x69, 0x6E, 0x75, 0x65, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x6F, 0x6C, 0x65, 0x5B, 0x6B, 0x65, 0x79, 0x5D, 0x20, 0x3D, 0x20, - 0x69, 0x6E, 0x73, 0x70, 0x65, 0x63, 0x74, 0x6F, 0x72, 0x43, 0x6F, 0x6E, 0x73, 0x6F, 0x6C, 0x65, - 0x5B, 0x6B, 0x65, 0x79, 0x5D, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x0A, 0x20, 0x20, 0x7D, - 0x0A, 0x0A, 0x20, 0x20, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x73, 0x65, 0x74, - 0x75, 0x70, 0x50, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x46, 0x61, 0x74, 0x61, 0x6C, 0x28, 0x29, - 0x20, 0x7B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, - 0x5F, 0x66, 0x61, 0x74, 0x61, 0x6C, 0x45, 0x78, 0x63, 0x65, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x20, - 0x3D, 0x20, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, 0x65, 0x72, 0x29, 0x20, 0x7B, - 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x63, 0x61, 0x75, 0x67, 0x68, - 0x74, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x70, 0x72, - 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x64, 0x6F, 0x6D, 0x61, 0x69, 0x6E, 0x20, 0x26, 0x26, 0x20, - 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x64, 0x6F, 0x6D, 0x61, 0x69, 0x6E, 0x2E, 0x5F, - 0x65, 0x72, 0x72, 0x6F, 0x72, 0x48, 0x61, 0x6E, 0x64, 0x6C, 0x65, 0x72, 0x29, 0x0A, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x75, 0x67, 0x68, 0x74, 0x20, 0x3D, 0x20, 0x70, - 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x64, 0x6F, 0x6D, 0x61, 0x69, 0x6E, 0x2E, 0x5F, 0x65, - 0x72, 0x72, 0x6F, 0x72, 0x48, 0x61, 0x6E, 0x64, 0x6C, 0x65, 0x72, 0x28, 0x65, 0x72, 0x29, 0x20, - 0x7C, 0x7C, 0x20, 0x63, 0x61, 0x75, 0x67, 0x68, 0x74, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x21, 0x63, 0x61, 0x75, 0x67, 0x68, 0x74, 0x29, 0x0A, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x75, 0x67, 0x68, 0x74, 0x20, 0x3D, 0x20, - 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x65, 0x6D, 0x69, 0x74, 0x28, 0x27, 0x75, 0x6E, - 0x63, 0x61, 0x75, 0x67, 0x68, 0x74, 0x45, 0x78, 0x63, 0x65, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x27, - 0x2C, 0x20, 0x65, 0x72, 0x29, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, - 0x20, 0x49, 0x66, 0x20, 0x73, 0x6F, 0x6D, 0x65, 0x6F, 0x6E, 0x65, 0x20, 0x68, 0x61, 0x6E, 0x64, - 0x6C, 0x65, 0x64, 0x20, 0x69, 0x74, 0x2C, 0x20, 0x74, 0x68, 0x65, 0x6E, 0x20, 0x67, 0x72, 0x65, - 0x61, 0x74, 0x2E, 0x20, 0x20, 0x6F, 0x74, 0x68, 0x65, 0x72, 0x77, 0x69, 0x73, 0x65, 0x2C, 0x20, - 0x64, 0x69, 0x65, 0x20, 0x69, 0x6E, 0x20, 0x43, 0x2B, 0x2B, 0x20, 0x6C, 0x61, 0x6E, 0x64, 0x0A, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x73, 0x69, 0x6E, 0x63, 0x65, 0x20, 0x74, - 0x68, 0x61, 0x74, 0x20, 0x6D, 0x65, 0x61, 0x6E, 0x73, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x77, - 0x65, 0x27, 0x6C, 0x6C, 0x20, 0x65, 0x78, 0x69, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x72, - 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2C, 0x20, 0x65, 0x6D, 0x69, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, - 0x27, 0x65, 0x78, 0x69, 0x74, 0x27, 0x20, 0x65, 0x76, 0x65, 0x6E, 0x74, 0x0A, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x21, 0x63, 0x61, 0x75, 0x67, 0x68, 0x74, 0x29, 0x20, - 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x72, 0x79, 0x20, 0x7B, 0x0A, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x21, 0x70, - 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x5F, 0x65, 0x78, 0x69, 0x74, 0x69, 0x6E, 0x67, 0x29, - 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, - 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x5F, 0x65, 0x78, 0x69, 0x74, 0x69, 0x6E, 0x67, 0x20, - 0x3D, 0x20, 0x74, 0x72, 0x75, 0x65, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x65, 0x6D, 0x69, 0x74, - 0x28, 0x27, 0x65, 0x78, 0x69, 0x74, 0x27, 0x2C, 0x20, 0x31, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x7D, 0x20, 0x63, 0x61, 0x74, 0x63, 0x68, 0x20, 0x28, 0x65, 0x72, 0x29, 0x20, 0x7B, 0x0A, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x6E, 0x6F, 0x74, - 0x68, 0x69, 0x6E, 0x67, 0x20, 0x74, 0x6F, 0x20, 0x62, 0x65, 0x20, 0x64, 0x6F, 0x6E, 0x65, 0x20, - 0x61, 0x62, 0x6F, 0x75, 0x74, 0x20, 0x69, 0x74, 0x20, 0x61, 0x74, 0x20, 0x74, 0x68, 0x69, 0x73, - 0x20, 0x70, 0x6F, 0x69, 0x6E, 0x74, 0x2E, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x7D, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x69, 0x66, 0x20, 0x77, - 0x65, 0x20, 0x68, 0x61, 0x6E, 0x64, 0x6C, 0x65, 0x64, 0x20, 0x61, 0x6E, 0x20, 0x65, 0x72, 0x72, - 0x6F, 0x72, 0x2C, 0x20, 0x74, 0x68, 0x65, 0x6E, 0x20, 0x6D, 0x61, 0x6B, 0x65, 0x20, 0x73, 0x75, - 0x72, 0x65, 0x20, 0x61, 0x6E, 0x79, 0x20, 0x74, 0x69, 0x63, 0x6B, 0x73, 0x20, 0x67, 0x65, 0x74, - 0x20, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x7D, 0x20, 0x65, 0x6C, 0x73, 0x65, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x72, - 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x27, 0x74, 0x69, 0x6D, 0x65, 0x72, 0x73, 0x27, 0x29, - 0x2E, 0x73, 0x65, 0x74, 0x49, 0x6D, 0x6D, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x28, 0x70, 0x72, - 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x5F, 0x74, 0x69, 0x63, 0x6B, 0x43, 0x61, 0x6C, 0x6C, 0x62, - 0x61, 0x63, 0x6B, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x0A, 0x0A, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6E, 0x20, 0x63, 0x61, 0x75, 0x67, - 0x68, 0x74, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x3B, 0x0A, 0x20, 0x20, 0x7D, 0x0A, 0x0A, - 0x20, 0x20, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x74, 0x72, 0x79, 0x47, 0x65, - 0x74, 0x43, 0x77, 0x64, 0x28, 0x70, 0x61, 0x74, 0x68, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, - 0x20, 0x76, 0x61, 0x72, 0x20, 0x74, 0x68, 0x72, 0x65, 0x77, 0x20, 0x3D, 0x20, 0x74, 0x72, 0x75, - 0x65, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x63, 0x77, 0x64, 0x3B, 0x0A, - 0x20, 0x20, 0x20, 0x20, 0x74, 0x72, 0x79, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x63, 0x77, 0x64, 0x20, 0x3D, 0x20, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x63, 0x77, - 0x64, 0x28, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, 0x72, 0x65, 0x77, - 0x20, 0x3D, 0x20, 0x66, 0x61, 0x6C, 0x73, 0x65, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x20, - 0x66, 0x69, 0x6E, 0x61, 0x6C, 0x6C, 0x79, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x69, 0x66, 0x20, 0x28, 0x74, 0x68, 0x72, 0x65, 0x77, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x67, 0x65, 0x74, 0x63, 0x77, 0x64, 0x28, 0x33, - 0x29, 0x20, 0x63, 0x61, 0x6E, 0x20, 0x66, 0x61, 0x69, 0x6C, 0x20, 0x69, 0x66, 0x20, 0x74, 0x68, - 0x65, 0x20, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6E, 0x74, 0x20, 0x77, 0x6F, 0x72, 0x6B, 0x69, 0x6E, - 0x67, 0x20, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6F, 0x72, 0x79, 0x20, 0x68, 0x61, 0x73, 0x20, - 0x62, 0x65, 0x65, 0x6E, 0x20, 0x64, 0x65, 0x6C, 0x65, 0x74, 0x65, 0x64, 0x2E, 0x0A, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x46, 0x61, 0x6C, 0x6C, 0x20, 0x62, 0x61, - 0x63, 0x6B, 0x20, 0x74, 0x6F, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, - 0x6F, 0x72, 0x79, 0x20, 0x6E, 0x61, 0x6D, 0x65, 0x20, 0x6F, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, - 0x28, 0x61, 0x62, 0x73, 0x6F, 0x6C, 0x75, 0x74, 0x65, 0x29, 0x20, 0x65, 0x78, 0x65, 0x63, 0x75, - 0x74, 0x61, 0x62, 0x6C, 0x65, 0x20, 0x70, 0x61, 0x74, 0x68, 0x2E, 0x0A, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x49, 0x74, 0x27, 0x73, 0x20, 0x6E, 0x6F, 0x74, 0x20, - 0x72, 0x65, 0x61, 0x6C, 0x6C, 0x79, 0x20, 0x63, 0x6F, 0x72, 0x72, 0x65, 0x63, 0x74, 0x20, 0x62, - 0x75, 0x74, 0x20, 0x77, 0x68, 0x61, 0x74, 0x20, 0x61, 0x72, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, - 0x61, 0x6C, 0x74, 0x65, 0x72, 0x6E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x73, 0x3F, 0x0A, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6E, 0x20, 0x70, 0x61, 0x74, - 0x68, 0x2E, 0x64, 0x69, 0x72, 0x6E, 0x61, 0x6D, 0x65, 0x28, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, - 0x73, 0x2E, 0x65, 0x78, 0x65, 0x63, 0x50, 0x61, 0x74, 0x68, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x7D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x72, - 0x65, 0x74, 0x75, 0x72, 0x6E, 0x20, 0x63, 0x77, 0x64, 0x3B, 0x0A, 0x20, 0x20, 0x7D, 0x0A, 0x0A, - 0x20, 0x20, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x65, 0x76, 0x61, 0x6C, 0x53, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x28, 0x6E, 0x61, 0x6D, 0x65, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, - 0x20, 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x20, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x20, 0x3D, - 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x72, 0x65, - 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x27, 0x6D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x27, 0x29, 0x3B, - 0x0A, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x20, 0x70, 0x61, 0x74, 0x68, 0x20, - 0x3D, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x72, - 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x27, 0x70, 0x61, 0x74, 0x68, 0x27, 0x29, 0x3B, 0x0A, - 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x20, 0x63, 0x77, 0x64, 0x20, 0x3D, 0x20, - 0x74, 0x72, 0x79, 0x47, 0x65, 0x74, 0x43, 0x77, 0x64, 0x28, 0x70, 0x61, 0x74, 0x68, 0x29, 0x3B, - 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x20, 0x6D, 0x6F, 0x64, 0x75, - 0x6C, 0x65, 0x20, 0x3D, 0x20, 0x6E, 0x65, 0x77, 0x20, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x28, - 0x6E, 0x61, 0x6D, 0x65, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x6D, 0x6F, 0x64, 0x75, 0x6C, - 0x65, 0x2E, 0x66, 0x69, 0x6C, 0x65, 0x6E, 0x61, 0x6D, 0x65, 0x20, 0x3D, 0x20, 0x70, 0x61, 0x74, - 0x68, 0x2E, 0x6A, 0x6F, 0x69, 0x6E, 0x28, 0x63, 0x77, 0x64, 0x2C, 0x20, 0x6E, 0x61, 0x6D, 0x65, - 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x6D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x70, 0x61, - 0x74, 0x68, 0x73, 0x20, 0x3D, 0x20, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x5F, 0x6E, 0x6F, - 0x64, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x50, 0x61, 0x74, 0x68, 0x73, 0x28, 0x63, 0x77, - 0x64, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x20, 0x62, 0x6F, - 0x64, 0x79, 0x20, 0x3D, 0x20, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x5F, 0x65, 0x76, - 0x61, 0x6C, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x20, 0x73, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x20, 0x3D, 0x20, 0x60, 0x67, 0x6C, 0x6F, 0x62, 0x61, 0x6C, 0x2E, 0x5F, - 0x5F, 0x66, 0x69, 0x6C, 0x65, 0x6E, 0x61, 0x6D, 0x65, 0x20, 0x3D, 0x20, 0x24, 0x7B, 0x4A, 0x53, - 0x4F, 0x4E, 0x2E, 0x73, 0x74, 0x72, 0x69, 0x6E, 0x67, 0x69, 0x66, 0x79, 0x28, 0x6E, 0x61, 0x6D, - 0x65, 0x29, 0x7D, 0x3B, 0x5C, 0x6E, 0x60, 0x20, 0x2B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x27, 0x67, 0x6C, - 0x6F, 0x62, 0x61, 0x6C, 0x2E, 0x65, 0x78, 0x70, 0x6F, 0x72, 0x74, 0x73, 0x20, 0x3D, 0x20, 0x65, - 0x78, 0x70, 0x6F, 0x72, 0x74, 0x73, 0x3B, 0x5C, 0x6E, 0x27, 0x20, 0x2B, 0x0A, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x27, 0x67, 0x6C, 0x6F, 0x62, 0x61, 0x6C, 0x2E, 0x6D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x20, 0x3D, - 0x20, 0x6D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x3B, 0x5C, 0x6E, 0x27, 0x20, 0x2B, 0x0A, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x27, 0x67, 0x6C, 0x6F, 0x62, 0x61, 0x6C, 0x2E, 0x5F, 0x5F, 0x64, 0x69, 0x72, 0x6E, 0x61, - 0x6D, 0x65, 0x20, 0x3D, 0x20, 0x5F, 0x5F, 0x64, 0x69, 0x72, 0x6E, 0x61, 0x6D, 0x65, 0x3B, 0x5C, - 0x6E, 0x27, 0x20, 0x2B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x27, 0x67, 0x6C, 0x6F, 0x62, 0x61, 0x6C, 0x2E, - 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x20, 0x3D, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, - 0x65, 0x3B, 0x5C, 0x6E, 0x27, 0x20, 0x2B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x27, 0x72, 0x65, 0x74, 0x75, - 0x72, 0x6E, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x22, 0x76, 0x6D, 0x22, 0x29, - 0x2E, 0x72, 0x75, 0x6E, 0x49, 0x6E, 0x54, 0x68, 0x69, 0x73, 0x43, 0x6F, 0x6E, 0x74, 0x65, 0x78, - 0x74, 0x28, 0x27, 0x20, 0x2B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x60, 0x24, 0x7B, 0x4A, 0x53, 0x4F, 0x4E, - 0x2E, 0x73, 0x74, 0x72, 0x69, 0x6E, 0x67, 0x69, 0x66, 0x79, 0x28, 0x62, 0x6F, 0x64, 0x79, 0x29, - 0x7D, 0x2C, 0x20, 0x7B, 0x20, 0x66, 0x69, 0x6C, 0x65, 0x6E, 0x61, 0x6D, 0x65, 0x3A, 0x20, 0x60, - 0x20, 0x2B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x60, 0x24, 0x7B, 0x4A, 0x53, 0x4F, 0x4E, 0x2E, 0x73, 0x74, - 0x72, 0x69, 0x6E, 0x67, 0x69, 0x66, 0x79, 0x28, 0x6E, 0x61, 0x6D, 0x65, 0x29, 0x7D, 0x2C, 0x20, - 0x64, 0x69, 0x73, 0x70, 0x6C, 0x61, 0x79, 0x45, 0x72, 0x72, 0x6F, 0x72, 0x73, 0x3A, 0x20, 0x74, - 0x72, 0x75, 0x65, 0x20, 0x7D, 0x29, 0x3B, 0x5C, 0x6E, 0x60, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, - 0x2F, 0x2F, 0x20, 0x44, 0x65, 0x66, 0x65, 0x72, 0x20, 0x65, 0x76, 0x61, 0x6C, 0x75, 0x61, 0x74, - 0x69, 0x6F, 0x6E, 0x20, 0x66, 0x6F, 0x72, 0x20, 0x61, 0x20, 0x74, 0x69, 0x63, 0x6B, 0x2E, 0x20, - 0x20, 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x77, 0x6F, 0x72, 0x6B, 0x61, - 0x72, 0x6F, 0x75, 0x6E, 0x64, 0x20, 0x66, 0x6F, 0x72, 0x20, 0x64, 0x65, 0x66, 0x65, 0x72, 0x72, - 0x65, 0x64, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x65, 0x76, 0x65, 0x6E, 0x74, 0x73, - 0x20, 0x6E, 0x6F, 0x74, 0x20, 0x66, 0x69, 0x72, 0x69, 0x6E, 0x67, 0x20, 0x77, 0x68, 0x65, 0x6E, - 0x20, 0x65, 0x76, 0x61, 0x6C, 0x75, 0x61, 0x74, 0x69, 0x6E, 0x67, 0x20, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x73, 0x20, 0x66, 0x72, 0x6F, 0x6D, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6F, 0x6D, - 0x6D, 0x61, 0x6E, 0x64, 0x20, 0x6C, 0x69, 0x6E, 0x65, 0x2C, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x2F, - 0x2F, 0x20, 0x73, 0x65, 0x65, 0x20, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3A, 0x2F, 0x2F, 0x67, 0x69, - 0x74, 0x68, 0x75, 0x62, 0x2E, 0x63, 0x6F, 0x6D, 0x2F, 0x6E, 0x6F, 0x64, 0x65, 0x6A, 0x73, 0x2F, - 0x6E, 0x6F, 0x64, 0x65, 0x2F, 0x69, 0x73, 0x73, 0x75, 0x65, 0x73, 0x2F, 0x31, 0x36, 0x30, 0x30, - 0x2E, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65, 0x74, 0x49, 0x6D, 0x6D, 0x65, 0x64, 0x69, 0x61, - 0x74, 0x65, 0x28, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, 0x29, 0x20, 0x7B, 0x0A, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x20, 0x72, 0x65, 0x73, 0x75, - 0x6C, 0x74, 0x20, 0x3D, 0x20, 0x6D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x5F, 0x63, 0x6F, 0x6D, - 0x70, 0x69, 0x6C, 0x65, 0x28, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2C, 0x20, 0x60, 0x24, 0x7B, - 0x6E, 0x61, 0x6D, 0x65, 0x7D, 0x2D, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x60, 0x29, 0x3B, - 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x70, 0x72, 0x6F, 0x63, 0x65, - 0x73, 0x73, 0x2E, 0x5F, 0x70, 0x72, 0x69, 0x6E, 0x74, 0x5F, 0x65, 0x76, 0x61, 0x6C, 0x29, 0x20, - 0x63, 0x6F, 0x6E, 0x73, 0x6F, 0x6C, 0x65, 0x2E, 0x6C, 0x6F, 0x67, 0x28, 0x72, 0x65, 0x73, 0x75, - 0x6C, 0x74, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x7D, - 0x0A, 0x0A, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x4C, 0x6F, 0x61, 0x64, 0x20, 0x70, 0x72, 0x65, 0x6C, - 0x6F, 0x61, 0x64, 0x20, 0x6D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x73, 0x0A, 0x20, 0x20, 0x66, 0x75, - 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x70, 0x72, 0x65, 0x6C, 0x6F, 0x61, 0x64, 0x4D, 0x6F, - 0x64, 0x75, 0x6C, 0x65, 0x73, 0x28, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, - 0x20, 0x28, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x5F, 0x70, 0x72, 0x65, 0x6C, 0x6F, - 0x61, 0x64, 0x5F, 0x6D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x73, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, - 0x2E, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x27, 0x6D, 0x6F, 0x64, 0x75, 0x6C, 0x65, - 0x27, 0x29, 0x2E, 0x5F, 0x70, 0x72, 0x65, 0x6C, 0x6F, 0x61, 0x64, 0x4D, 0x6F, 0x64, 0x75, 0x6C, - 0x65, 0x73, 0x28, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x5F, 0x70, 0x72, 0x65, 0x6C, - 0x6F, 0x61, 0x64, 0x5F, 0x6D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x73, 0x29, 0x3B, 0x0A, 0x20, 0x20, - 0x20, 0x20, 0x7D, 0x0A, 0x20, 0x20, 0x7D, 0x0A, 0x0A, 0x20, 0x20, 0x66, 0x75, 0x6E, 0x63, 0x74, - 0x69, 0x6F, 0x6E, 0x20, 0x69, 0x73, 0x44, 0x65, 0x62, 0x75, 0x67, 0x42, 0x72, 0x65, 0x61, 0x6B, - 0x28, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6E, 0x20, - 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x65, 0x78, 0x65, 0x63, 0x41, 0x72, 0x67, 0x76, - 0x2E, 0x73, 0x6F, 0x6D, 0x65, 0x28, 0x28, 0x61, 0x72, 0x67, 0x29, 0x20, 0x3D, 0x3E, 0x20, 0x7B, - 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6E, 0x20, 0x61, 0x72, - 0x67, 0x2E, 0x6D, 0x61, 0x74, 0x63, 0x68, 0x28, 0x2F, 0x5E, 0x2D, 0x2D, 0x64, 0x65, 0x62, 0x75, - 0x67, 0x2D, 0x62, 0x72, 0x6B, 0x28, 0x3D, 0x5B, 0x30, 0x2D, 0x39, 0x5D, 0x2A, 0x29, 0x3F, 0x24, - 0x2F, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x7D, 0x0A, - 0x0A, 0x20, 0x20, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x72, 0x75, 0x6E, 0x28, - 0x65, 0x6E, 0x74, 0x72, 0x79, 0x46, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x29, 0x20, 0x7B, - 0x0A, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, - 0x2E, 0x5F, 0x64, 0x65, 0x62, 0x75, 0x67, 0x57, 0x61, 0x69, 0x74, 0x43, 0x6F, 0x6E, 0x6E, 0x65, - 0x63, 0x74, 0x20, 0x26, 0x26, 0x20, 0x69, 0x73, 0x44, 0x65, 0x62, 0x75, 0x67, 0x42, 0x72, 0x65, - 0x61, 0x6B, 0x28, 0x29, 0x29, 0x20, 0x7B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, - 0x2F, 0x20, 0x58, 0x58, 0x58, 0x20, 0x46, 0x69, 0x78, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x74, - 0x65, 0x72, 0x72, 0x69, 0x62, 0x6C, 0x65, 0x20, 0x68, 0x61, 0x63, 0x6B, 0x21, 0x0A, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, - 0x47, 0x69, 0x76, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6C, 0x69, 0x65, 0x6E, 0x74, 0x20, - 0x70, 0x72, 0x6F, 0x67, 0x72, 0x61, 0x6D, 0x20, 0x61, 0x20, 0x66, 0x65, 0x77, 0x20, 0x74, 0x69, - 0x63, 0x6B, 0x73, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6F, 0x6E, 0x6E, 0x65, 0x63, 0x74, 0x2E, 0x0A, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x4F, 0x74, 0x68, 0x65, 0x72, 0x77, 0x69, - 0x73, 0x65, 0x2C, 0x20, 0x74, 0x68, 0x65, 0x72, 0x65, 0x27, 0x73, 0x20, 0x61, 0x20, 0x72, 0x61, - 0x63, 0x65, 0x20, 0x63, 0x6F, 0x6E, 0x64, 0x69, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x77, 0x68, 0x65, - 0x72, 0x65, 0x20, 0x60, 0x6E, 0x6F, 0x64, 0x65, 0x20, 0x64, 0x65, 0x62, 0x75, 0x67, 0x20, 0x66, - 0x6F, 0x6F, 0x2E, 0x6A, 0x73, 0x60, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, - 0x77, 0x69, 0x6C, 0x6C, 0x20, 0x6E, 0x6F, 0x74, 0x20, 0x62, 0x65, 0x20, 0x61, 0x62, 0x6C, 0x65, - 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6F, 0x6E, 0x6E, 0x65, 0x63, 0x74, 0x20, 0x69, 0x6E, 0x20, 0x74, - 0x69, 0x6D, 0x65, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x61, 0x74, 0x63, 0x68, 0x20, 0x74, 0x68, 0x65, - 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, - 0x62, 0x72, 0x65, 0x61, 0x6B, 0x70, 0x6F, 0x69, 0x6E, 0x74, 0x20, 0x6D, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x20, 0x6F, 0x6E, 0x20, 0x6C, 0x69, 0x6E, 0x65, 0x20, 0x31, 0x2E, 0x0A, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, - 0x41, 0x20, 0x62, 0x65, 0x74, 0x74, 0x65, 0x72, 0x20, 0x66, 0x69, 0x78, 0x20, 0x77, 0x6F, 0x75, - 0x6C, 0x64, 0x20, 0x62, 0x65, 0x20, 0x74, 0x6F, 0x20, 0x73, 0x6F, 0x6D, 0x65, 0x68, 0x6F, 0x77, - 0x20, 0x67, 0x65, 0x74, 0x20, 0x61, 0x20, 0x6D, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x66, - 0x72, 0x6F, 0x6D, 0x20, 0x74, 0x68, 0x65, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, - 0x20, 0x56, 0x38, 0x20, 0x64, 0x65, 0x62, 0x75, 0x67, 0x20, 0x6F, 0x62, 0x6A, 0x65, 0x63, 0x74, - 0x20, 0x61, 0x62, 0x6F, 0x75, 0x74, 0x20, 0x61, 0x20, 0x63, 0x6F, 0x6E, 0x6E, 0x65, 0x63, 0x74, - 0x69, 0x6F, 0x6E, 0x2C, 0x20, 0x61, 0x6E, 0x64, 0x20, 0x72, 0x75, 0x6E, 0x4D, 0x61, 0x69, 0x6E, - 0x20, 0x77, 0x68, 0x65, 0x6E, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x74, - 0x68, 0x61, 0x74, 0x20, 0x6F, 0x63, 0x63, 0x75, 0x72, 0x73, 0x2E, 0x20, 0x20, 0x2D, 0x2D, 0x69, - 0x73, 0x61, 0x61, 0x63, 0x73, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, 0x6E, - 0x73, 0x74, 0x20, 0x64, 0x65, 0x62, 0x75, 0x67, 0x54, 0x69, 0x6D, 0x65, 0x6F, 0x75, 0x74, 0x20, - 0x3D, 0x20, 0x2B, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x65, 0x6E, 0x76, 0x2E, 0x4E, - 0x4F, 0x44, 0x45, 0x5F, 0x44, 0x45, 0x42, 0x55, 0x47, 0x5F, 0x54, 0x49, 0x4D, 0x45, 0x4F, 0x55, - 0x54, 0x20, 0x7C, 0x7C, 0x20, 0x35, 0x30, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, - 0x65, 0x74, 0x54, 0x69, 0x6D, 0x65, 0x6F, 0x75, 0x74, 0x28, 0x65, 0x6E, 0x74, 0x72, 0x79, 0x46, - 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x2C, 0x20, 0x64, 0x65, 0x62, 0x75, 0x67, 0x54, 0x69, - 0x6D, 0x65, 0x6F, 0x75, 0x74, 0x29, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x20, 0x65, - 0x6C, 0x73, 0x65, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x4D, - 0x61, 0x69, 0x6E, 0x20, 0x65, 0x6E, 0x74, 0x72, 0x79, 0x20, 0x70, 0x6F, 0x69, 0x6E, 0x74, 0x20, - 0x69, 0x6E, 0x74, 0x6F, 0x20, 0x6D, 0x6F, 0x73, 0x74, 0x20, 0x70, 0x72, 0x6F, 0x67, 0x72, 0x61, - 0x6D, 0x73, 0x3A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6E, 0x74, 0x72, 0x79, 0x46, - 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x7D, - 0x0A, 0x20, 0x20, 0x7D, 0x0A, 0x0A, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x42, 0x65, 0x6C, 0x6F, 0x77, - 0x20, 0x79, 0x6F, 0x75, 0x20, 0x66, 0x69, 0x6E, 0x64, 0x20, 0x61, 0x20, 0x6D, 0x69, 0x6E, 0x69, - 0x6D, 0x61, 0x6C, 0x20, 0x6D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x20, 0x73, 0x79, 0x73, 0x74, 0x65, - 0x6D, 0x2C, 0x20, 0x77, 0x68, 0x69, 0x63, 0x68, 0x20, 0x69, 0x73, 0x20, 0x75, 0x73, 0x65, 0x64, - 0x20, 0x74, 0x6F, 0x20, 0x6C, 0x6F, 0x61, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6E, 0x6F, 0x64, - 0x65, 0x0A, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x63, 0x6F, 0x72, 0x65, 0x20, 0x6D, 0x6F, 0x64, 0x75, - 0x6C, 0x65, 0x73, 0x20, 0x66, 0x6F, 0x75, 0x6E, 0x64, 0x20, 0x69, 0x6E, 0x20, 0x6C, 0x69, 0x62, - 0x2F, 0x2A, 0x2E, 0x6A, 0x73, 0x2E, 0x20, 0x41, 0x6C, 0x6C, 0x20, 0x63, 0x6F, 0x72, 0x65, 0x20, - 0x6D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x63, 0x6F, 0x6D, 0x70, - 0x69, 0x6C, 0x65, 0x64, 0x20, 0x69, 0x6E, 0x74, 0x6F, 0x20, 0x74, 0x68, 0x65, 0x0A, 0x20, 0x20, - 0x2F, 0x2F, 0x20, 0x6E, 0x6F, 0x64, 0x65, 0x20, 0x62, 0x69, 0x6E, 0x61, 0x72, 0x79, 0x2C, 0x20, - 0x73, 0x6F, 0x20, 0x74, 0x68, 0x65, 0x79, 0x20, 0x63, 0x61, 0x6E, 0x20, 0x62, 0x65, 0x20, 0x6C, - 0x6F, 0x61, 0x64, 0x65, 0x64, 0x20, 0x66, 0x61, 0x73, 0x74, 0x65, 0x72, 0x2E, 0x0A, 0x0A, 0x20, - 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x20, 0x43, 0x6F, 0x6E, 0x74, 0x65, 0x78, 0x74, 0x69, 0x66, - 0x79, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x20, 0x3D, 0x20, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, - 0x73, 0x2E, 0x62, 0x69, 0x6E, 0x64, 0x69, 0x6E, 0x67, 0x28, 0x27, 0x63, 0x6F, 0x6E, 0x74, 0x65, - 0x78, 0x74, 0x69, 0x66, 0x79, 0x27, 0x29, 0x2E, 0x43, 0x6F, 0x6E, 0x74, 0x65, 0x78, 0x74, 0x69, - 0x66, 0x79, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x3B, 0x0A, 0x20, 0x20, 0x66, 0x75, 0x6E, 0x63, - 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x72, 0x75, 0x6E, 0x49, 0x6E, 0x54, 0x68, 0x69, 0x73, 0x43, 0x6F, - 0x6E, 0x74, 0x65, 0x78, 0x74, 0x28, 0x63, 0x6F, 0x64, 0x65, 0x2C, 0x20, 0x6F, 0x70, 0x74, 0x69, - 0x6F, 0x6E, 0x73, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, - 0x20, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x20, 0x3D, 0x20, 0x6E, 0x65, 0x77, 0x20, 0x43, 0x6F, - 0x6E, 0x74, 0x65, 0x78, 0x74, 0x69, 0x66, 0x79, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x28, 0x63, - 0x6F, 0x64, 0x65, 0x2C, 0x20, 0x6F, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x73, 0x29, 0x3B, 0x0A, 0x20, - 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6E, 0x20, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x2E, 0x72, 0x75, 0x6E, 0x49, 0x6E, 0x54, 0x68, 0x69, 0x73, 0x43, 0x6F, 0x6E, 0x74, 0x65, 0x78, - 0x74, 0x28, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x7D, 0x0A, 0x0A, 0x20, 0x20, 0x66, 0x75, 0x6E, 0x63, - 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, - 0x65, 0x28, 0x69, 0x64, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, 0x69, 0x73, - 0x2E, 0x66, 0x69, 0x6C, 0x65, 0x6E, 0x61, 0x6D, 0x65, 0x20, 0x3D, 0x20, 0x60, 0x24, 0x7B, 0x69, - 0x64, 0x7D, 0x2E, 0x6A, 0x73, 0x60, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, 0x69, 0x73, - 0x2E, 0x69, 0x64, 0x20, 0x3D, 0x20, 0x69, 0x64, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, - 0x69, 0x73, 0x2E, 0x65, 0x78, 0x70, 0x6F, 0x72, 0x74, 0x73, 0x20, 0x3D, 0x20, 0x7B, 0x7D, 0x3B, - 0x0A, 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x6C, 0x6F, 0x61, 0x64, 0x65, 0x64, - 0x20, 0x3D, 0x20, 0x66, 0x61, 0x6C, 0x73, 0x65, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, - 0x69, 0x73, 0x2E, 0x6C, 0x6F, 0x61, 0x64, 0x69, 0x6E, 0x67, 0x20, 0x3D, 0x20, 0x66, 0x61, 0x6C, - 0x73, 0x65, 0x3B, 0x0A, 0x20, 0x20, 0x7D, 0x0A, 0x0A, 0x20, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, - 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x5F, 0x73, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x20, - 0x3D, 0x20, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x62, 0x69, 0x6E, 0x64, 0x69, 0x6E, - 0x67, 0x28, 0x27, 0x6E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x73, 0x27, 0x29, 0x3B, 0x0A, 0x20, 0x20, - 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x5F, 0x63, 0x61, - 0x63, 0x68, 0x65, 0x20, 0x3D, 0x20, 0x7B, 0x7D, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x4E, 0x61, 0x74, - 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, - 0x65, 0x20, 0x3D, 0x20, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, 0x69, 0x64, 0x29, - 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x69, 0x64, 0x20, 0x3D, 0x3D, - 0x3D, 0x20, 0x27, 0x6E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5F, 0x6D, 0x6F, 0x64, 0x75, 0x6C, 0x65, - 0x27, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, - 0x6E, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x3B, 0x0A, - 0x20, 0x20, 0x20, 0x20, 0x7D, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, - 0x20, 0x63, 0x61, 0x63, 0x68, 0x65, 0x64, 0x20, 0x3D, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, - 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x67, 0x65, 0x74, 0x43, 0x61, 0x63, 0x68, 0x65, 0x64, - 0x28, 0x69, 0x64, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x63, 0x61, - 0x63, 0x68, 0x65, 0x64, 0x20, 0x26, 0x26, 0x20, 0x28, 0x63, 0x61, 0x63, 0x68, 0x65, 0x64, 0x2E, - 0x6C, 0x6F, 0x61, 0x64, 0x65, 0x64, 0x20, 0x7C, 0x7C, 0x20, 0x63, 0x61, 0x63, 0x68, 0x65, 0x64, - 0x2E, 0x6C, 0x6F, 0x61, 0x64, 0x69, 0x6E, 0x67, 0x29, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6E, 0x20, 0x63, 0x61, 0x63, 0x68, 0x65, 0x64, - 0x2E, 0x65, 0x78, 0x70, 0x6F, 0x72, 0x74, 0x73, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x0A, - 0x0A, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x21, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, - 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x65, 0x78, 0x69, 0x73, 0x74, 0x73, 0x28, 0x69, 0x64, - 0x29, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, 0x72, 0x6F, 0x77, - 0x20, 0x6E, 0x65, 0x77, 0x20, 0x45, 0x72, 0x72, 0x6F, 0x72, 0x28, 0x60, 0x4E, 0x6F, 0x20, 0x73, - 0x75, 0x63, 0x68, 0x20, 0x6E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x6D, 0x6F, 0x64, 0x75, 0x6C, - 0x65, 0x20, 0x24, 0x7B, 0x69, 0x64, 0x7D, 0x60, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x7D, - 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x6D, 0x6F, - 0x64, 0x75, 0x6C, 0x65, 0x4C, 0x6F, 0x61, 0x64, 0x4C, 0x69, 0x73, 0x74, 0x2E, 0x70, 0x75, 0x73, - 0x68, 0x28, 0x60, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x20, - 0x24, 0x7B, 0x69, 0x64, 0x7D, 0x60, 0x29, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, - 0x6E, 0x73, 0x74, 0x20, 0x6E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, - 0x20, 0x3D, 0x20, 0x6E, 0x65, 0x77, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, - 0x75, 0x6C, 0x65, 0x28, 0x69, 0x64, 0x29, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x6E, 0x61, - 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x63, 0x61, 0x63, 0x68, 0x65, - 0x28, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x6E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, - 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x63, 0x6F, 0x6D, 0x70, 0x69, 0x6C, 0x65, 0x28, 0x29, 0x3B, 0x0A, - 0x0A, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6E, 0x20, 0x6E, 0x61, 0x74, 0x69, - 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x65, 0x78, 0x70, 0x6F, 0x72, 0x74, 0x73, - 0x3B, 0x0A, 0x20, 0x20, 0x7D, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, - 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x67, 0x65, 0x74, 0x43, 0x61, 0x63, 0x68, 0x65, 0x64, - 0x20, 0x3D, 0x20, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, 0x69, 0x64, 0x29, 0x20, - 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6E, 0x20, 0x4E, 0x61, 0x74, - 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x5F, 0x63, 0x61, 0x63, 0x68, 0x65, - 0x5B, 0x69, 0x64, 0x5D, 0x3B, 0x0A, 0x20, 0x20, 0x7D, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x4E, 0x61, - 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x65, 0x78, 0x69, 0x73, 0x74, - 0x73, 0x20, 0x3D, 0x20, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, 0x69, 0x64, 0x29, - 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6E, 0x20, 0x4E, 0x61, - 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x5F, 0x73, 0x6F, 0x75, 0x72, - 0x63, 0x65, 0x2E, 0x68, 0x61, 0x73, 0x4F, 0x77, 0x6E, 0x50, 0x72, 0x6F, 0x70, 0x65, 0x72, 0x74, - 0x79, 0x28, 0x69, 0x64, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x7D, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x63, - 0x6F, 0x6E, 0x73, 0x74, 0x20, 0x45, 0x58, 0x50, 0x4F, 0x53, 0x45, 0x5F, 0x49, 0x4E, 0x54, 0x45, - 0x52, 0x4E, 0x41, 0x4C, 0x53, 0x20, 0x3D, 0x20, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, - 0x65, 0x78, 0x65, 0x63, 0x41, 0x72, 0x67, 0x76, 0x2E, 0x73, 0x6F, 0x6D, 0x65, 0x28, 0x66, 0x75, - 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, 0x61, 0x72, 0x67, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, - 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6E, 0x20, 0x61, 0x72, 0x67, 0x2E, 0x6D, 0x61, 0x74, - 0x63, 0x68, 0x28, 0x2F, 0x5E, 0x2D, 0x2D, 0x65, 0x78, 0x70, 0x6F, 0x73, 0x65, 0x5B, 0x2D, 0x5F, - 0x5D, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x6E, 0x61, 0x6C, 0x73, 0x24, 0x2F, 0x29, 0x3B, 0x0A, 0x20, - 0x20, 0x7D, 0x29, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x45, 0x58, 0x50, 0x4F, - 0x53, 0x45, 0x5F, 0x49, 0x4E, 0x54, 0x45, 0x52, 0x4E, 0x41, 0x4C, 0x53, 0x29, 0x20, 0x7B, 0x0A, - 0x20, 0x20, 0x20, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, - 0x2E, 0x6E, 0x6F, 0x6E, 0x49, 0x6E, 0x74, 0x65, 0x72, 0x6E, 0x61, 0x6C, 0x45, 0x78, 0x69, 0x73, - 0x74, 0x73, 0x20, 0x3D, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, - 0x65, 0x2E, 0x65, 0x78, 0x69, 0x73, 0x74, 0x73, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x4E, - 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x69, 0x73, 0x49, 0x6E, - 0x74, 0x65, 0x72, 0x6E, 0x61, 0x6C, 0x20, 0x3D, 0x20, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, - 0x6E, 0x28, 0x69, 0x64, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, - 0x74, 0x75, 0x72, 0x6E, 0x20, 0x66, 0x61, 0x6C, 0x73, 0x65, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, - 0x7D, 0x3B, 0x0A, 0x20, 0x20, 0x7D, 0x20, 0x65, 0x6C, 0x73, 0x65, 0x20, 0x7B, 0x0A, 0x20, 0x20, - 0x20, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x6E, - 0x6F, 0x6E, 0x49, 0x6E, 0x74, 0x65, 0x72, 0x6E, 0x61, 0x6C, 0x45, 0x78, 0x69, 0x73, 0x74, 0x73, - 0x20, 0x3D, 0x20, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, 0x69, 0x64, 0x29, 0x20, - 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6E, 0x20, 0x4E, - 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x65, 0x78, 0x69, 0x73, - 0x74, 0x73, 0x28, 0x69, 0x64, 0x29, 0x20, 0x26, 0x26, 0x20, 0x21, 0x4E, 0x61, 0x74, 0x69, 0x76, - 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x69, 0x73, 0x49, 0x6E, 0x74, 0x65, 0x72, 0x6E, - 0x61, 0x6C, 0x28, 0x69, 0x64, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x3B, 0x0A, 0x0A, - 0x20, 0x20, 0x20, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, - 0x2E, 0x69, 0x73, 0x49, 0x6E, 0x74, 0x65, 0x72, 0x6E, 0x61, 0x6C, 0x20, 0x3D, 0x20, 0x66, 0x75, - 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, 0x69, 0x64, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6E, 0x20, 0x69, 0x64, 0x2E, 0x73, 0x74, 0x61, - 0x72, 0x74, 0x73, 0x57, 0x69, 0x74, 0x68, 0x28, 0x27, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x6E, 0x61, - 0x6C, 0x2F, 0x27, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x3B, 0x0A, 0x20, 0x20, 0x7D, - 0x0A, 0x0A, 0x0A, 0x20, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, - 0x65, 0x2E, 0x67, 0x65, 0x74, 0x53, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x20, 0x3D, 0x20, 0x66, 0x75, - 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, 0x69, 0x64, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, - 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6E, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, - 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x5F, 0x73, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x5B, 0x69, 0x64, 0x5D, - 0x3B, 0x0A, 0x20, 0x20, 0x7D, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, - 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x77, 0x72, 0x61, 0x70, 0x20, 0x3D, 0x20, 0x66, 0x75, - 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x29, 0x20, 0x7B, - 0x0A, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6E, 0x20, 0x4E, 0x61, 0x74, 0x69, - 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, - 0x5B, 0x30, 0x5D, 0x20, 0x2B, 0x20, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x20, 0x2B, 0x20, 0x4E, - 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x77, 0x72, 0x61, 0x70, - 0x70, 0x65, 0x72, 0x5B, 0x31, 0x5D, 0x3B, 0x0A, 0x20, 0x20, 0x7D, 0x3B, 0x0A, 0x0A, 0x20, 0x20, - 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x77, 0x72, 0x61, - 0x70, 0x70, 0x65, 0x72, 0x20, 0x3D, 0x20, 0x5B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x27, 0x28, 0x66, - 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x28, 0x65, 0x78, 0x70, 0x6F, 0x72, 0x74, 0x73, - 0x2C, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x2C, 0x20, 0x6D, 0x6F, 0x64, 0x75, 0x6C, - 0x65, 0x2C, 0x20, 0x5F, 0x5F, 0x66, 0x69, 0x6C, 0x65, 0x6E, 0x61, 0x6D, 0x65, 0x2C, 0x20, 0x5F, - 0x5F, 0x64, 0x69, 0x72, 0x6E, 0x61, 0x6D, 0x65, 0x29, 0x20, 0x7B, 0x20, 0x27, 0x2C, 0x0A, 0x20, - 0x20, 0x20, 0x20, 0x27, 0x5C, 0x6E, 0x7D, 0x29, 0x3B, 0x27, 0x0A, 0x20, 0x20, 0x5D, 0x3B, 0x0A, - 0x0A, 0x20, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, - 0x70, 0x72, 0x6F, 0x74, 0x6F, 0x74, 0x79, 0x70, 0x65, 0x2E, 0x63, 0x6F, 0x6D, 0x70, 0x69, 0x6C, - 0x65, 0x20, 0x3D, 0x20, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, 0x29, 0x20, 0x7B, - 0x0A, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x73, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x20, - 0x3D, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x67, - 0x65, 0x74, 0x53, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x69, 0x64, - 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x73, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x20, 0x3D, 0x20, - 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x77, 0x72, 0x61, - 0x70, 0x28, 0x73, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x29, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, - 0x74, 0x68, 0x69, 0x73, 0x2E, 0x6C, 0x6F, 0x61, 0x64, 0x69, 0x6E, 0x67, 0x20, 0x3D, 0x20, 0x74, - 0x72, 0x75, 0x65, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x74, 0x72, 0x79, 0x20, 0x7B, 0x0A, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x20, 0x66, 0x6E, 0x20, 0x3D, - 0x20, 0x72, 0x75, 0x6E, 0x49, 0x6E, 0x54, 0x68, 0x69, 0x73, 0x43, 0x6F, 0x6E, 0x74, 0x65, 0x78, - 0x74, 0x28, 0x73, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x2C, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x66, 0x69, 0x6C, 0x65, 0x6E, 0x61, 0x6D, 0x65, 0x3A, 0x20, 0x74, 0x68, - 0x69, 0x73, 0x2E, 0x66, 0x69, 0x6C, 0x65, 0x6E, 0x61, 0x6D, 0x65, 0x2C, 0x0A, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x6C, 0x69, 0x6E, 0x65, 0x4F, 0x66, 0x66, 0x73, 0x65, 0x74, 0x3A, - 0x20, 0x30, 0x2C, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x69, 0x73, 0x70, - 0x6C, 0x61, 0x79, 0x45, 0x72, 0x72, 0x6F, 0x72, 0x73, 0x3A, 0x20, 0x74, 0x72, 0x75, 0x65, 0x0A, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x66, 0x6E, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x65, 0x78, 0x70, 0x6F, 0x72, 0x74, 0x73, 0x2C, - 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x72, 0x65, - 0x71, 0x75, 0x69, 0x72, 0x65, 0x2C, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2C, 0x20, 0x74, 0x68, 0x69, - 0x73, 0x2E, 0x66, 0x69, 0x6C, 0x65, 0x6E, 0x61, 0x6D, 0x65, 0x29, 0x3B, 0x0A, 0x0A, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x6C, 0x6F, 0x61, 0x64, 0x65, 0x64, 0x20, - 0x3D, 0x20, 0x74, 0x72, 0x75, 0x65, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x20, 0x66, 0x69, - 0x6E, 0x61, 0x6C, 0x6C, 0x79, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, - 0x69, 0x73, 0x2E, 0x6C, 0x6F, 0x61, 0x64, 0x69, 0x6E, 0x67, 0x20, 0x3D, 0x20, 0x66, 0x61, 0x6C, - 0x73, 0x65, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x0A, 0x20, 0x20, 0x7D, 0x3B, 0x0A, 0x0A, - 0x20, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x70, - 0x72, 0x6F, 0x74, 0x6F, 0x74, 0x79, 0x70, 0x65, 0x2E, 0x63, 0x61, 0x63, 0x68, 0x65, 0x20, 0x3D, - 0x20, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, - 0x20, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x5F, - 0x63, 0x61, 0x63, 0x68, 0x65, 0x5B, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x69, 0x64, 0x5D, 0x20, 0x3D, - 0x20, 0x74, 0x68, 0x69, 0x73, 0x3B, 0x0A, 0x20, 0x20, 0x7D, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x66, - 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x73, 0x65, 0x74, 0x75, 0x70, 0x41, 0x73, 0x61, - 0x72, 0x53, 0x75, 0x70, 0x70, 0x6F, 0x72, 0x74, 0x28, 0x29, 0x20, 0x7B, 0x0D, 0x0A, 0x20, 0x20, - 0x20, 0x20, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x62, 0x69, 0x6E, 0x64, 0x69, 0x6E, - 0x67, 0x28, 0x27, 0x61, 0x74, 0x6F, 0x6D, 0x5F, 0x63, 0x6F, 0x6D, 0x6D, 0x6F, 0x6E, 0x5F, 0x61, - 0x73, 0x61, 0x72, 0x27, 0x29, 0x2E, 0x69, 0x6E, 0x69, 0x74, 0x41, 0x73, 0x61, 0x72, 0x53, 0x75, - 0x70, 0x70, 0x6F, 0x72, 0x74, 0x28, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2C, 0x20, 0x4E, - 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x72, 0x65, 0x71, 0x75, - 0x69, 0x72, 0x65, 0x29, 0x3B, 0x0D, 0x0A, 0x20, 0x20, 0x7D, 0x0A, 0x0A, 0x20, 0x20, 0x73, 0x74, - 0x61, 0x72, 0x74, 0x75, 0x70, 0x28, 0x29, 0x3B, 0x0A, 0x7D, 0x29, 0x3B, 0x0A + 0x2F, 0x2F, 0x20, 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x2C, 0x20, 0x61, 0x6E, 0x64, 0x20, 0x77, 0x65, + 0x6C, 0x63, 0x6F, 0x6D, 0x65, 0x20, 0x74, 0x6F, 0x20, 0x68, 0x61, 0x63, 0x6B, 0x69, 0x6E, 0x67, + 0x20, 0x6E, 0x6F, 0x64, 0x65, 0x2E, 0x6A, 0x73, 0x21, 0x0A, 0x2F, 0x2F, 0x0A, 0x2F, 0x2F, 0x20, + 0x54, 0x68, 0x69, 0x73, 0x20, 0x66, 0x69, 0x6C, 0x65, 0x20, 0x69, 0x73, 0x20, 0x69, 0x6E, 0x76, + 0x6F, 0x6B, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x6E, 0x6F, 0x64, 0x65, 0x3A, 0x3A, 0x4C, 0x6F, + 0x61, 0x64, 0x45, 0x6E, 0x76, 0x69, 0x72, 0x6F, 0x6E, 0x6D, 0x65, 0x6E, 0x74, 0x20, 0x69, 0x6E, + 0x20, 0x73, 0x72, 0x63, 0x2F, 0x6E, 0x6F, 0x64, 0x65, 0x2E, 0x63, 0x63, 0x2C, 0x20, 0x61, 0x6E, + 0x64, 0x20, 0x69, 0x73, 0x0A, 0x2F, 0x2F, 0x20, 0x72, 0x65, 0x73, 0x70, 0x6F, 0x6E, 0x73, 0x69, + 0x62, 0x6C, 0x65, 0x20, 0x66, 0x6F, 0x72, 0x20, 0x62, 0x6F, 0x6F, 0x74, 0x73, 0x74, 0x72, 0x61, + 0x70, 0x70, 0x69, 0x6E, 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6E, 0x6F, 0x64, 0x65, 0x2E, 0x6A, + 0x73, 0x20, 0x63, 0x6F, 0x72, 0x65, 0x2E, 0x20, 0x41, 0x73, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, + 0x61, 0x6C, 0x20, 0x63, 0x61, 0x75, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x69, 0x73, 0x20, 0x67, 0x69, + 0x76, 0x65, 0x6E, 0x0A, 0x2F, 0x2F, 0x20, 0x74, 0x6F, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x65, + 0x72, 0x66, 0x6F, 0x72, 0x6D, 0x61, 0x6E, 0x63, 0x65, 0x20, 0x6F, 0x66, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x73, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x20, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, + 0x2C, 0x20, 0x6D, 0x61, 0x6E, 0x79, 0x20, 0x64, 0x65, 0x70, 0x65, 0x6E, 0x64, 0x65, 0x6E, 0x63, + 0x69, 0x65, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x69, 0x6E, 0x76, 0x6F, 0x6B, 0x65, 0x64, 0x0A, + 0x2F, 0x2F, 0x20, 0x6C, 0x61, 0x7A, 0x69, 0x6C, 0x79, 0x2E, 0x0A, 0x0A, 0x27, 0x75, 0x73, 0x65, + 0x20, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x27, 0x3B, 0x0A, 0x0A, 0x28, 0x66, 0x75, 0x6E, 0x63, + 0x74, 0x69, 0x6F, 0x6E, 0x28, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x29, 0x20, 0x7B, 0x0A, + 0x0A, 0x20, 0x20, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x73, 0x74, 0x61, 0x72, + 0x74, 0x75, 0x70, 0x28, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, 0x6E, 0x73, + 0x74, 0x20, 0x45, 0x76, 0x65, 0x6E, 0x74, 0x45, 0x6D, 0x69, 0x74, 0x74, 0x65, 0x72, 0x20, 0x3D, + 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x72, 0x65, + 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x27, 0x65, 0x76, 0x65, 0x6E, 0x74, 0x73, 0x27, 0x29, 0x3B, + 0x0A, 0x20, 0x20, 0x20, 0x20, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x5F, 0x65, 0x76, + 0x65, 0x6E, 0x74, 0x73, 0x43, 0x6F, 0x75, 0x6E, 0x74, 0x20, 0x3D, 0x20, 0x30, 0x3B, 0x0A, 0x0A, + 0x20, 0x20, 0x20, 0x20, 0x4F, 0x62, 0x6A, 0x65, 0x63, 0x74, 0x2E, 0x73, 0x65, 0x74, 0x50, 0x72, + 0x6F, 0x74, 0x6F, 0x74, 0x79, 0x70, 0x65, 0x4F, 0x66, 0x28, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, + 0x73, 0x2C, 0x20, 0x4F, 0x62, 0x6A, 0x65, 0x63, 0x74, 0x2E, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x28, 0x45, 0x76, 0x65, 0x6E, 0x74, 0x45, 0x6D, 0x69, 0x74, 0x74, 0x65, 0x72, 0x2E, 0x70, 0x72, + 0x6F, 0x74, 0x6F, 0x74, 0x79, 0x70, 0x65, 0x2C, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x6F, 0x72, 0x3A, 0x20, 0x7B, 0x0A, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x6C, 0x75, 0x65, 0x3A, 0x20, 0x70, + 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, + 0x6F, 0x72, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x7D, + 0x29, 0x29, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x45, 0x76, 0x65, 0x6E, 0x74, 0x45, 0x6D, + 0x69, 0x74, 0x74, 0x65, 0x72, 0x2E, 0x63, 0x61, 0x6C, 0x6C, 0x28, 0x70, 0x72, 0x6F, 0x63, 0x65, + 0x73, 0x73, 0x29, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x6C, 0x65, 0x74, 0x20, 0x65, 0x65, + 0x57, 0x61, 0x72, 0x6E, 0x65, 0x64, 0x20, 0x3D, 0x20, 0x66, 0x61, 0x6C, 0x73, 0x65, 0x3B, 0x0A, + 0x20, 0x20, 0x20, 0x20, 0x4F, 0x62, 0x6A, 0x65, 0x63, 0x74, 0x2E, 0x64, 0x65, 0x66, 0x69, 0x6E, + 0x65, 0x50, 0x72, 0x6F, 0x70, 0x65, 0x72, 0x74, 0x79, 0x28, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, + 0x73, 0x2C, 0x20, 0x27, 0x45, 0x76, 0x65, 0x6E, 0x74, 0x45, 0x6D, 0x69, 0x74, 0x74, 0x65, 0x72, + 0x27, 0x2C, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x67, 0x65, 0x74, 0x28, 0x29, + 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, + 0x20, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x6E, 0x61, 0x6C, 0x55, 0x74, 0x69, 0x6C, 0x20, 0x3D, 0x20, + 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x72, 0x65, 0x71, + 0x75, 0x69, 0x72, 0x65, 0x28, 0x27, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x6E, 0x61, 0x6C, 0x2F, 0x75, + 0x74, 0x69, 0x6C, 0x27, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, + 0x65, 0x57, 0x61, 0x72, 0x6E, 0x65, 0x64, 0x20, 0x3D, 0x20, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x6E, + 0x61, 0x6C, 0x55, 0x74, 0x69, 0x6C, 0x2E, 0x70, 0x72, 0x69, 0x6E, 0x74, 0x44, 0x65, 0x70, 0x72, + 0x65, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x4D, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x28, 0x0A, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x70, 0x72, 0x6F, 0x63, 0x65, + 0x73, 0x73, 0x2E, 0x45, 0x76, 0x65, 0x6E, 0x74, 0x45, 0x6D, 0x69, 0x74, 0x74, 0x65, 0x72, 0x20, + 0x69, 0x73, 0x20, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x2E, 0x20, 0x55, + 0x73, 0x65, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x27, 0x65, 0x76, 0x65, 0x6E, + 0x74, 0x73, 0x27, 0x29, 0x20, 0x69, 0x6E, 0x73, 0x74, 0x65, 0x61, 0x64, 0x2E, 0x22, 0x2C, 0x0A, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x65, 0x57, 0x61, 0x72, 0x6E, + 0x65, 0x64, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x29, 0x3B, 0x0A, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6E, 0x20, 0x45, 0x76, 0x65, + 0x6E, 0x74, 0x45, 0x6D, 0x69, 0x74, 0x74, 0x65, 0x72, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x7D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x29, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, + 0x73, 0x65, 0x74, 0x75, 0x70, 0x50, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x4F, 0x62, 0x6A, 0x65, + 0x63, 0x74, 0x28, 0x29, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x64, 0x6F, + 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x67, 0x6F, 0x6F, 0x64, 0x20, 0x61, 0x6E, 0x64, 0x20, 0x65, + 0x61, 0x72, 0x6C, 0x79, 0x2C, 0x20, 0x73, 0x69, 0x6E, 0x63, 0x65, 0x20, 0x69, 0x74, 0x20, 0x68, + 0x61, 0x6E, 0x64, 0x6C, 0x65, 0x73, 0x20, 0x65, 0x72, 0x72, 0x6F, 0x72, 0x73, 0x2E, 0x0A, 0x20, + 0x20, 0x20, 0x20, 0x73, 0x65, 0x74, 0x75, 0x70, 0x50, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x46, + 0x61, 0x74, 0x61, 0x6C, 0x28, 0x29, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65, 0x74, + 0x75, 0x70, 0x47, 0x6C, 0x6F, 0x62, 0x61, 0x6C, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6C, 0x65, + 0x73, 0x28, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x21, 0x70, 0x72, + 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x5F, 0x6E, 0x6F, 0x42, 0x72, 0x6F, 0x77, 0x73, 0x65, 0x72, + 0x47, 0x6C, 0x6F, 0x62, 0x61, 0x6C, 0x73, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x73, 0x65, 0x74, 0x75, 0x70, 0x47, 0x6C, 0x6F, 0x62, 0x61, 0x6C, 0x54, 0x69, 0x6D, 0x65, + 0x6F, 0x75, 0x74, 0x73, 0x28, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65, + 0x74, 0x75, 0x70, 0x47, 0x6C, 0x6F, 0x62, 0x61, 0x6C, 0x43, 0x6F, 0x6E, 0x73, 0x6F, 0x6C, 0x65, + 0x28, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x73, + 0x65, 0x74, 0x75, 0x70, 0x41, 0x73, 0x61, 0x72, 0x53, 0x75, 0x70, 0x70, 0x6F, 0x72, 0x74, 0x28, + 0x29, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x20, 0x5F, 0x70, + 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x20, 0x3D, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, + 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x27, 0x69, + 0x6E, 0x74, 0x65, 0x72, 0x6E, 0x61, 0x6C, 0x2F, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x27, + 0x29, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x5F, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, + 0x2E, 0x73, 0x65, 0x74, 0x75, 0x70, 0x5F, 0x68, 0x72, 0x74, 0x69, 0x6D, 0x65, 0x28, 0x29, 0x3B, + 0x0A, 0x20, 0x20, 0x20, 0x20, 0x5F, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x73, 0x65, + 0x74, 0x75, 0x70, 0x5F, 0x63, 0x70, 0x75, 0x55, 0x73, 0x61, 0x67, 0x65, 0x28, 0x29, 0x3B, 0x0A, + 0x20, 0x20, 0x20, 0x20, 0x5F, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x73, 0x65, 0x74, + 0x75, 0x70, 0x43, 0x6F, 0x6E, 0x66, 0x69, 0x67, 0x28, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, + 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x5F, 0x73, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x29, 0x3B, 0x0A, + 0x20, 0x20, 0x20, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, + 0x2E, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x27, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x6E, + 0x61, 0x6C, 0x2F, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2F, 0x77, 0x61, 0x72, 0x6E, 0x69, + 0x6E, 0x67, 0x27, 0x29, 0x2E, 0x73, 0x65, 0x74, 0x75, 0x70, 0x28, 0x29, 0x3B, 0x0A, 0x20, 0x20, + 0x20, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x72, + 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x27, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x6E, 0x61, 0x6C, + 0x2F, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2F, 0x6E, 0x65, 0x78, 0x74, 0x5F, 0x74, 0x69, + 0x63, 0x6B, 0x27, 0x29, 0x2E, 0x73, 0x65, 0x74, 0x75, 0x70, 0x28, 0x29, 0x3B, 0x0A, 0x20, 0x20, + 0x20, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x72, + 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x27, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x6E, 0x61, 0x6C, + 0x2F, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2F, 0x73, 0x74, 0x64, 0x69, 0x6F, 0x27, 0x29, + 0x2E, 0x73, 0x65, 0x74, 0x75, 0x70, 0x28, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x5F, 0x70, + 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x73, 0x65, 0x74, 0x75, 0x70, 0x4B, 0x69, 0x6C, 0x6C, + 0x41, 0x6E, 0x64, 0x45, 0x78, 0x69, 0x74, 0x28, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x5F, + 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x73, 0x65, 0x74, 0x75, 0x70, 0x53, 0x69, 0x67, + 0x6E, 0x61, 0x6C, 0x48, 0x61, 0x6E, 0x64, 0x6C, 0x65, 0x72, 0x73, 0x28, 0x29, 0x3B, 0x0A, 0x0A, + 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x44, 0x6F, 0x20, 0x6E, 0x6F, 0x74, 0x20, 0x69, 0x6E, + 0x69, 0x74, 0x69, 0x61, 0x6C, 0x69, 0x7A, 0x65, 0x20, 0x63, 0x68, 0x61, 0x6E, 0x6E, 0x65, 0x6C, + 0x20, 0x69, 0x6E, 0x20, 0x64, 0x65, 0x62, 0x75, 0x67, 0x67, 0x65, 0x72, 0x20, 0x61, 0x67, 0x65, + 0x6E, 0x74, 0x2C, 0x20, 0x69, 0x74, 0x20, 0x64, 0x65, 0x6C, 0x65, 0x74, 0x65, 0x73, 0x20, 0x65, + 0x6E, 0x76, 0x20, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6C, 0x65, 0x0A, 0x20, 0x20, 0x20, 0x20, + 0x2F, 0x2F, 0x20, 0x61, 0x6E, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6D, 0x61, 0x69, 0x6E, 0x20, + 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x20, 0x77, 0x6F, 0x6E, 0x27, 0x74, 0x20, 0x73, 0x65, 0x65, + 0x20, 0x69, 0x74, 0x2E, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x70, 0x72, 0x6F, + 0x63, 0x65, 0x73, 0x73, 0x2E, 0x61, 0x72, 0x67, 0x76, 0x5B, 0x31, 0x5D, 0x20, 0x21, 0x3D, 0x3D, + 0x20, 0x27, 0x2D, 0x2D, 0x64, 0x65, 0x62, 0x75, 0x67, 0x2D, 0x61, 0x67, 0x65, 0x6E, 0x74, 0x27, + 0x29, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5F, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, + 0x2E, 0x73, 0x65, 0x74, 0x75, 0x70, 0x43, 0x68, 0x61, 0x6E, 0x6E, 0x65, 0x6C, 0x28, 0x29, 0x3B, + 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x5F, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x73, + 0x65, 0x74, 0x75, 0x70, 0x52, 0x61, 0x77, 0x44, 0x65, 0x62, 0x75, 0x67, 0x28, 0x29, 0x3B, 0x0A, + 0x0A, 0x20, 0x20, 0x20, 0x20, 0x4F, 0x62, 0x6A, 0x65, 0x63, 0x74, 0x2E, 0x64, 0x65, 0x66, 0x69, + 0x6E, 0x65, 0x50, 0x72, 0x6F, 0x70, 0x65, 0x72, 0x74, 0x79, 0x28, 0x70, 0x72, 0x6F, 0x63, 0x65, + 0x73, 0x73, 0x2C, 0x20, 0x27, 0x61, 0x72, 0x67, 0x76, 0x30, 0x27, 0x2C, 0x20, 0x7B, 0x0A, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6E, 0x75, 0x6D, 0x65, 0x72, 0x61, 0x62, 0x6C, 0x65, 0x3A, + 0x20, 0x74, 0x72, 0x75, 0x65, 0x2C, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, 0x6E, + 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x62, 0x6C, 0x65, 0x3A, 0x20, 0x66, 0x61, 0x6C, 0x73, 0x65, + 0x2C, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x6C, 0x75, 0x65, 0x3A, 0x20, 0x70, + 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x61, 0x72, 0x67, 0x76, 0x5B, 0x30, 0x5D, 0x0A, 0x20, + 0x20, 0x20, 0x20, 0x7D, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x70, 0x72, 0x6F, 0x63, 0x65, + 0x73, 0x73, 0x2E, 0x61, 0x72, 0x67, 0x76, 0x5B, 0x30, 0x5D, 0x20, 0x3D, 0x20, 0x70, 0x72, 0x6F, + 0x63, 0x65, 0x73, 0x73, 0x2E, 0x65, 0x78, 0x65, 0x63, 0x50, 0x61, 0x74, 0x68, 0x3B, 0x0A, 0x0A, + 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x54, 0x68, 0x65, 0x72, 0x65, 0x20, 0x61, 0x72, 0x65, + 0x20, 0x76, 0x61, 0x72, 0x69, 0x6F, 0x75, 0x73, 0x20, 0x6D, 0x6F, 0x64, 0x65, 0x73, 0x20, 0x74, + 0x68, 0x61, 0x74, 0x20, 0x4E, 0x6F, 0x64, 0x65, 0x20, 0x63, 0x61, 0x6E, 0x20, 0x72, 0x75, 0x6E, + 0x20, 0x69, 0x6E, 0x2E, 0x20, 0x54, 0x68, 0x65, 0x20, 0x6D, 0x6F, 0x73, 0x74, 0x20, 0x63, 0x6F, + 0x6D, 0x6D, 0x6F, 0x6E, 0x20, 0x74, 0x77, 0x6F, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, + 0x61, 0x72, 0x65, 0x20, 0x72, 0x75, 0x6E, 0x6E, 0x69, 0x6E, 0x67, 0x20, 0x66, 0x72, 0x6F, 0x6D, + 0x20, 0x61, 0x20, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x20, 0x61, 0x6E, 0x64, 0x20, 0x72, 0x75, + 0x6E, 0x6E, 0x69, 0x6E, 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x52, 0x45, 0x50, 0x4C, 0x20, 0x2D, + 0x20, 0x62, 0x75, 0x74, 0x20, 0x74, 0x68, 0x65, 0x72, 0x65, 0x20, 0x61, 0x72, 0x65, 0x20, 0x61, + 0x20, 0x66, 0x65, 0x77, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x6F, 0x74, 0x68, 0x65, + 0x72, 0x73, 0x20, 0x6C, 0x69, 0x6B, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x65, 0x62, 0x75, + 0x67, 0x67, 0x65, 0x72, 0x20, 0x6F, 0x72, 0x20, 0x72, 0x75, 0x6E, 0x6E, 0x69, 0x6E, 0x67, 0x20, + 0x2D, 0x2D, 0x65, 0x76, 0x61, 0x6C, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6D, 0x65, 0x6E, 0x74, 0x73, + 0x2E, 0x20, 0x48, 0x65, 0x72, 0x65, 0x20, 0x77, 0x65, 0x20, 0x64, 0x65, 0x63, 0x69, 0x64, 0x65, + 0x0A, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x77, 0x68, 0x69, 0x63, 0x68, 0x20, 0x6D, 0x6F, + 0x64, 0x65, 0x20, 0x77, 0x65, 0x20, 0x72, 0x75, 0x6E, 0x20, 0x69, 0x6E, 0x2E, 0x0A, 0x0A, 0x20, + 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, + 0x75, 0x6C, 0x65, 0x2E, 0x65, 0x78, 0x69, 0x73, 0x74, 0x73, 0x28, 0x27, 0x5F, 0x74, 0x68, 0x69, + 0x72, 0x64, 0x5F, 0x70, 0x61, 0x72, 0x74, 0x79, 0x5F, 0x6D, 0x61, 0x69, 0x6E, 0x27, 0x29, 0x29, + 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x54, 0x6F, 0x20, 0x61, + 0x6C, 0x6C, 0x6F, 0x77, 0x20, 0x70, 0x65, 0x6F, 0x70, 0x6C, 0x65, 0x20, 0x74, 0x6F, 0x20, 0x65, + 0x78, 0x74, 0x65, 0x6E, 0x64, 0x20, 0x4E, 0x6F, 0x64, 0x65, 0x20, 0x69, 0x6E, 0x20, 0x64, 0x69, + 0x66, 0x66, 0x65, 0x72, 0x65, 0x6E, 0x74, 0x20, 0x77, 0x61, 0x79, 0x73, 0x2C, 0x20, 0x74, 0x68, + 0x69, 0x73, 0x20, 0x68, 0x6F, 0x6F, 0x6B, 0x20, 0x61, 0x6C, 0x6C, 0x6F, 0x77, 0x73, 0x0A, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x6F, 0x6E, 0x65, 0x20, 0x74, 0x6F, 0x20, 0x64, + 0x72, 0x6F, 0x70, 0x20, 0x61, 0x20, 0x66, 0x69, 0x6C, 0x65, 0x20, 0x6C, 0x69, 0x62, 0x2F, 0x5F, + 0x74, 0x68, 0x69, 0x72, 0x64, 0x5F, 0x70, 0x61, 0x72, 0x74, 0x79, 0x5F, 0x6D, 0x61, 0x69, 0x6E, + 0x2E, 0x6A, 0x73, 0x20, 0x69, 0x6E, 0x74, 0x6F, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x75, 0x69, + 0x6C, 0x64, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x64, 0x69, 0x72, 0x65, + 0x63, 0x74, 0x6F, 0x72, 0x79, 0x20, 0x77, 0x68, 0x69, 0x63, 0x68, 0x20, 0x77, 0x69, 0x6C, 0x6C, + 0x20, 0x62, 0x65, 0x20, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x64, 0x20, 0x69, 0x6E, 0x73, + 0x74, 0x65, 0x61, 0x64, 0x20, 0x6F, 0x66, 0x20, 0x4E, 0x6F, 0x64, 0x65, 0x27, 0x73, 0x20, 0x6E, + 0x6F, 0x72, 0x6D, 0x61, 0x6C, 0x20, 0x6C, 0x6F, 0x61, 0x64, 0x69, 0x6E, 0x67, 0x2E, 0x0A, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x6E, 0x65, 0x78, + 0x74, 0x54, 0x69, 0x63, 0x6B, 0x28, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, 0x29, + 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, + 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, + 0x27, 0x5F, 0x74, 0x68, 0x69, 0x72, 0x64, 0x5F, 0x70, 0x61, 0x72, 0x74, 0x79, 0x5F, 0x6D, 0x61, + 0x69, 0x6E, 0x27, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x29, 0x3B, 0x0A, + 0x0A, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x20, 0x65, 0x6C, 0x73, 0x65, 0x20, 0x69, 0x66, 0x20, 0x28, + 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x61, 0x72, 0x67, 0x76, 0x5B, 0x31, 0x5D, 0x20, + 0x3D, 0x3D, 0x3D, 0x20, 0x27, 0x64, 0x65, 0x62, 0x75, 0x67, 0x27, 0x29, 0x20, 0x7B, 0x0A, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x53, 0x74, 0x61, 0x72, 0x74, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x64, 0x65, 0x62, 0x75, 0x67, 0x67, 0x65, 0x72, 0x20, 0x61, 0x67, 0x65, 0x6E, 0x74, + 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, + 0x75, 0x6C, 0x65, 0x2E, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x27, 0x5F, 0x64, 0x65, + 0x62, 0x75, 0x67, 0x67, 0x65, 0x72, 0x27, 0x29, 0x2E, 0x73, 0x74, 0x61, 0x72, 0x74, 0x28, 0x29, + 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x20, 0x65, 0x6C, 0x73, 0x65, 0x20, 0x69, 0x66, + 0x20, 0x28, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x61, 0x72, 0x67, 0x76, 0x5B, 0x31, + 0x5D, 0x20, 0x3D, 0x3D, 0x3D, 0x20, 0x27, 0x2D, 0x2D, 0x72, 0x65, 0x6D, 0x6F, 0x74, 0x65, 0x5F, + 0x64, 0x65, 0x62, 0x75, 0x67, 0x67, 0x69, 0x6E, 0x67, 0x5F, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x27, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x53, 0x74, + 0x61, 0x72, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x65, 0x62, 0x75, 0x67, 0x67, 0x69, 0x6E, + 0x67, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x4E, + 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x72, 0x65, 0x71, 0x75, + 0x69, 0x72, 0x65, 0x28, 0x27, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x6E, 0x61, 0x6C, 0x2F, 0x69, 0x6E, + 0x73, 0x70, 0x65, 0x63, 0x74, 0x6F, 0x72, 0x2F, 0x72, 0x65, 0x6D, 0x6F, 0x74, 0x65, 0x5F, 0x64, + 0x65, 0x62, 0x75, 0x67, 0x67, 0x69, 0x6E, 0x67, 0x5F, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x27, + 0x29, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x20, 0x65, 0x6C, 0x73, 0x65, 0x20, 0x69, + 0x66, 0x20, 0x28, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x61, 0x72, 0x67, 0x76, 0x5B, + 0x31, 0x5D, 0x20, 0x3D, 0x3D, 0x3D, 0x20, 0x27, 0x2D, 0x2D, 0x64, 0x65, 0x62, 0x75, 0x67, 0x2D, + 0x61, 0x67, 0x65, 0x6E, 0x74, 0x27, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x2F, 0x2F, 0x20, 0x53, 0x74, 0x61, 0x72, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x65, 0x62, + 0x75, 0x67, 0x67, 0x65, 0x72, 0x20, 0x61, 0x67, 0x65, 0x6E, 0x74, 0x0A, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x72, + 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x27, 0x5F, 0x64, 0x65, 0x62, 0x75, 0x67, 0x5F, 0x61, + 0x67, 0x65, 0x6E, 0x74, 0x27, 0x29, 0x2E, 0x73, 0x74, 0x61, 0x72, 0x74, 0x28, 0x29, 0x3B, 0x0A, + 0x0A, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x20, 0x65, 0x6C, 0x73, 0x65, 0x20, 0x69, 0x66, 0x20, 0x28, + 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x70, 0x72, 0x6F, 0x66, 0x50, 0x72, 0x6F, 0x63, + 0x65, 0x73, 0x73, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x4E, 0x61, 0x74, + 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, + 0x65, 0x28, 0x27, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x6E, 0x61, 0x6C, 0x2F, 0x76, 0x38, 0x5F, 0x70, + 0x72, 0x6F, 0x66, 0x5F, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x6F, 0x72, 0x27, 0x29, 0x3B, + 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x20, 0x65, 0x6C, 0x73, 0x65, 0x20, 0x7B, 0x0A, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x54, 0x68, 0x65, 0x72, 0x65, 0x20, 0x69, 0x73, + 0x20, 0x75, 0x73, 0x65, 0x72, 0x20, 0x63, 0x6F, 0x64, 0x65, 0x20, 0x74, 0x6F, 0x20, 0x62, 0x65, + 0x20, 0x72, 0x75, 0x6E, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x49, + 0x66, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x77, 0x6F, 0x72, 0x6B, + 0x65, 0x72, 0x20, 0x69, 0x6E, 0x20, 0x63, 0x6C, 0x75, 0x73, 0x74, 0x65, 0x72, 0x20, 0x6D, 0x6F, + 0x64, 0x65, 0x2C, 0x20, 0x73, 0x74, 0x61, 0x72, 0x74, 0x20, 0x75, 0x70, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x63, 0x6F, 0x6D, 0x6D, 0x75, 0x6E, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x0A, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x63, 0x68, 0x61, 0x6E, 0x6E, 0x65, 0x6C, 0x2E, + 0x20, 0x54, 0x68, 0x69, 0x73, 0x20, 0x6E, 0x65, 0x65, 0x64, 0x73, 0x20, 0x74, 0x6F, 0x20, 0x62, + 0x65, 0x20, 0x64, 0x6F, 0x6E, 0x65, 0x20, 0x62, 0x65, 0x66, 0x6F, 0x72, 0x65, 0x20, 0x61, 0x6E, + 0x79, 0x20, 0x75, 0x73, 0x65, 0x72, 0x20, 0x63, 0x6F, 0x64, 0x65, 0x20, 0x67, 0x65, 0x74, 0x73, + 0x20, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x64, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x2F, 0x2F, 0x20, 0x28, 0x69, 0x6E, 0x63, 0x6C, 0x75, 0x64, 0x69, 0x6E, 0x67, 0x20, 0x70, 0x72, + 0x65, 0x6C, 0x6F, 0x61, 0x64, 0x20, 0x6D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x73, 0x29, 0x2E, 0x0A, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, + 0x73, 0x2E, 0x61, 0x72, 0x67, 0x76, 0x5B, 0x31, 0x5D, 0x20, 0x26, 0x26, 0x20, 0x70, 0x72, 0x6F, + 0x63, 0x65, 0x73, 0x73, 0x2E, 0x65, 0x6E, 0x76, 0x2E, 0x4E, 0x4F, 0x44, 0x45, 0x5F, 0x55, 0x4E, + 0x49, 0x51, 0x55, 0x45, 0x5F, 0x49, 0x44, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x20, 0x63, 0x6C, 0x75, 0x73, 0x74, 0x65, 0x72, + 0x20, 0x3D, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, + 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x27, 0x63, 0x6C, 0x75, 0x73, 0x74, 0x65, 0x72, + 0x27, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6C, 0x75, 0x73, + 0x74, 0x65, 0x72, 0x2E, 0x5F, 0x73, 0x65, 0x74, 0x75, 0x70, 0x57, 0x6F, 0x72, 0x6B, 0x65, 0x72, + 0x28, 0x29, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, + 0x4D, 0x61, 0x6B, 0x65, 0x20, 0x73, 0x75, 0x72, 0x65, 0x20, 0x69, 0x74, 0x27, 0x73, 0x20, 0x6E, + 0x6F, 0x74, 0x20, 0x61, 0x63, 0x63, 0x69, 0x64, 0x65, 0x6E, 0x74, 0x61, 0x6C, 0x6C, 0x79, 0x20, + 0x69, 0x6E, 0x68, 0x65, 0x72, 0x69, 0x74, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x63, 0x68, 0x69, + 0x6C, 0x64, 0x20, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x65, 0x73, 0x2E, 0x0A, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x65, 0x6C, 0x65, 0x74, 0x65, 0x20, 0x70, 0x72, 0x6F, + 0x63, 0x65, 0x73, 0x73, 0x2E, 0x65, 0x6E, 0x76, 0x2E, 0x4E, 0x4F, 0x44, 0x45, 0x5F, 0x55, 0x4E, + 0x49, 0x51, 0x55, 0x45, 0x5F, 0x49, 0x44, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7D, + 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x70, 0x72, 0x6F, 0x63, + 0x65, 0x73, 0x73, 0x2E, 0x5F, 0x65, 0x76, 0x61, 0x6C, 0x20, 0x21, 0x3D, 0x20, 0x6E, 0x75, 0x6C, + 0x6C, 0x20, 0x26, 0x26, 0x20, 0x21, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x5F, 0x66, + 0x6F, 0x72, 0x63, 0x65, 0x52, 0x65, 0x70, 0x6C, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x55, 0x73, 0x65, 0x72, 0x20, 0x70, 0x61, 0x73, 0x73, + 0x65, 0x64, 0x20, 0x27, 0x2D, 0x65, 0x27, 0x20, 0x6F, 0x72, 0x20, 0x27, 0x2D, 0x2D, 0x65, 0x76, + 0x61, 0x6C, 0x27, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6D, 0x65, 0x6E, 0x74, 0x73, 0x20, 0x74, 0x6F, + 0x20, 0x4E, 0x6F, 0x64, 0x65, 0x20, 0x77, 0x69, 0x74, 0x68, 0x6F, 0x75, 0x74, 0x20, 0x27, 0x2D, + 0x69, 0x27, 0x20, 0x6F, 0x72, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, + 0x20, 0x27, 0x2D, 0x2D, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x27, + 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x72, 0x65, 0x6C, 0x6F, 0x61, 0x64, + 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x73, 0x28, 0x29, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x20, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x6E, + 0x61, 0x6C, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x20, 0x3D, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, + 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, + 0x27, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x6E, 0x61, 0x6C, 0x2F, 0x6D, 0x6F, 0x64, 0x75, 0x6C, 0x65, + 0x27, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6E, 0x74, 0x65, + 0x72, 0x6E, 0x61, 0x6C, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x61, 0x64, 0x64, 0x42, 0x75, + 0x69, 0x6C, 0x74, 0x69, 0x6E, 0x4C, 0x69, 0x62, 0x73, 0x54, 0x6F, 0x4F, 0x62, 0x6A, 0x65, 0x63, + 0x74, 0x28, 0x67, 0x6C, 0x6F, 0x62, 0x61, 0x6C, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x72, 0x75, 0x6E, 0x28, 0x28, 0x29, 0x20, 0x3D, 0x3E, 0x20, 0x7B, 0x0A, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x76, 0x61, 0x6C, 0x53, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x28, 0x27, 0x5B, 0x65, 0x76, 0x61, 0x6C, 0x5D, 0x27, 0x29, 0x3B, 0x0A, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x7D, 0x20, 0x65, 0x6C, 0x73, 0x65, 0x20, 0x69, 0x66, 0x20, 0x28, 0x70, 0x72, 0x6F, 0x63, + 0x65, 0x73, 0x73, 0x2E, 0x61, 0x72, 0x67, 0x76, 0x5B, 0x31, 0x5D, 0x29, 0x20, 0x7B, 0x0A, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x6D, 0x61, 0x6B, 0x65, 0x20, 0x70, + 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x61, 0x72, 0x67, 0x76, 0x5B, 0x31, 0x5D, 0x20, 0x69, + 0x6E, 0x74, 0x6F, 0x20, 0x61, 0x20, 0x66, 0x75, 0x6C, 0x6C, 0x20, 0x70, 0x61, 0x74, 0x68, 0x0A, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x20, 0x70, 0x61, + 0x74, 0x68, 0x20, 0x3D, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, + 0x65, 0x2E, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x27, 0x70, 0x61, 0x74, 0x68, 0x27, + 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x72, 0x6F, 0x63, 0x65, + 0x73, 0x73, 0x2E, 0x61, 0x72, 0x67, 0x76, 0x5B, 0x31, 0x5D, 0x20, 0x3D, 0x20, 0x70, 0x61, 0x74, + 0x68, 0x2E, 0x72, 0x65, 0x73, 0x6F, 0x6C, 0x76, 0x65, 0x28, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, + 0x73, 0x2E, 0x61, 0x72, 0x67, 0x76, 0x5B, 0x31, 0x5D, 0x29, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x20, 0x4D, 0x6F, 0x64, 0x75, 0x6C, + 0x65, 0x20, 0x3D, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, + 0x2E, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x27, 0x6D, 0x6F, 0x64, 0x75, 0x6C, 0x65, + 0x27, 0x29, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, + 0x63, 0x68, 0x65, 0x63, 0x6B, 0x20, 0x69, 0x66, 0x20, 0x75, 0x73, 0x65, 0x72, 0x20, 0x70, 0x61, + 0x73, 0x73, 0x65, 0x64, 0x20, 0x60, 0x2D, 0x63, 0x60, 0x20, 0x6F, 0x72, 0x20, 0x60, 0x2D, 0x2D, + 0x63, 0x68, 0x65, 0x63, 0x6B, 0x60, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6D, 0x65, 0x6E, 0x74, 0x73, + 0x20, 0x74, 0x6F, 0x20, 0x4E, 0x6F, 0x64, 0x65, 0x2E, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x5F, 0x73, + 0x79, 0x6E, 0x74, 0x61, 0x78, 0x5F, 0x63, 0x68, 0x65, 0x63, 0x6B, 0x5F, 0x6F, 0x6E, 0x6C, 0x79, + 0x20, 0x21, 0x3D, 0x20, 0x6E, 0x75, 0x6C, 0x6C, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x20, 0x76, 0x6D, 0x20, 0x3D, + 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x72, 0x65, + 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x27, 0x76, 0x6D, 0x27, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x20, 0x66, 0x73, 0x20, + 0x3D, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x72, + 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x27, 0x66, 0x73, 0x27, 0x29, 0x3B, 0x0A, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x20, 0x69, 0x6E, + 0x74, 0x65, 0x72, 0x6E, 0x61, 0x6C, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x20, 0x3D, 0x20, 0x4E, + 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x72, 0x65, 0x71, 0x75, + 0x69, 0x72, 0x65, 0x28, 0x27, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x6E, 0x61, 0x6C, 0x2F, 0x6D, 0x6F, + 0x64, 0x75, 0x6C, 0x65, 0x27, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x72, 0x65, 0x61, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x6F, + 0x75, 0x72, 0x63, 0x65, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, + 0x6F, 0x6E, 0x73, 0x74, 0x20, 0x66, 0x69, 0x6C, 0x65, 0x6E, 0x61, 0x6D, 0x65, 0x20, 0x3D, 0x20, + 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x5F, 0x72, 0x65, 0x73, 0x6F, 0x6C, 0x76, 0x65, 0x46, + 0x69, 0x6C, 0x65, 0x6E, 0x61, 0x6D, 0x65, 0x28, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, + 0x61, 0x72, 0x67, 0x76, 0x5B, 0x31, 0x5D, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x73, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x20, 0x3D, + 0x20, 0x66, 0x73, 0x2E, 0x72, 0x65, 0x61, 0x64, 0x46, 0x69, 0x6C, 0x65, 0x53, 0x79, 0x6E, 0x63, + 0x28, 0x66, 0x69, 0x6C, 0x65, 0x6E, 0x61, 0x6D, 0x65, 0x2C, 0x20, 0x27, 0x75, 0x74, 0x66, 0x2D, + 0x38, 0x27, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, + 0x2F, 0x20, 0x72, 0x65, 0x6D, 0x6F, 0x76, 0x65, 0x20, 0x73, 0x68, 0x65, 0x62, 0x61, 0x6E, 0x67, + 0x20, 0x61, 0x6E, 0x64, 0x20, 0x42, 0x4F, 0x4D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x73, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x20, 0x3D, 0x20, 0x69, 0x6E, 0x74, 0x65, + 0x72, 0x6E, 0x61, 0x6C, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x73, 0x74, 0x72, 0x69, 0x70, + 0x42, 0x4F, 0x4D, 0x28, 0x73, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x2E, 0x72, 0x65, 0x70, 0x6C, 0x61, + 0x63, 0x65, 0x28, 0x2F, 0x5E, 0x23, 0x21, 0x2E, 0x2A, 0x2F, 0x2C, 0x20, 0x27, 0x27, 0x29, 0x29, + 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x77, + 0x72, 0x61, 0x70, 0x20, 0x69, 0x74, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x73, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x20, 0x3D, 0x20, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, + 0x2E, 0x77, 0x72, 0x61, 0x70, 0x28, 0x73, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x29, 0x3B, 0x0A, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x63, 0x6F, 0x6D, 0x70, + 0x69, 0x6C, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2C, 0x20, + 0x74, 0x68, 0x69, 0x73, 0x20, 0x77, 0x69, 0x6C, 0x6C, 0x20, 0x74, 0x68, 0x72, 0x6F, 0x77, 0x20, + 0x69, 0x66, 0x20, 0x69, 0x74, 0x20, 0x66, 0x61, 0x69, 0x6C, 0x73, 0x0A, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6E, 0x65, 0x77, 0x20, 0x76, 0x6D, 0x2E, 0x53, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x28, 0x73, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x2C, 0x20, 0x7B, 0x66, 0x69, 0x6C, + 0x65, 0x6E, 0x61, 0x6D, 0x65, 0x3A, 0x20, 0x66, 0x69, 0x6C, 0x65, 0x6E, 0x61, 0x6D, 0x65, 0x2C, + 0x20, 0x64, 0x69, 0x73, 0x70, 0x6C, 0x61, 0x79, 0x45, 0x72, 0x72, 0x6F, 0x72, 0x73, 0x3A, 0x20, + 0x74, 0x72, 0x75, 0x65, 0x7D, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x65, 0x78, 0x69, 0x74, 0x28, 0x30, + 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x0A, 0x0A, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x72, 0x65, 0x6C, 0x6F, 0x61, 0x64, 0x4D, 0x6F, 0x64, + 0x75, 0x6C, 0x65, 0x73, 0x28, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x75, 0x6E, 0x28, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x72, 0x75, 0x6E, 0x4D, 0x61, + 0x69, 0x6E, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x20, 0x65, 0x6C, 0x73, + 0x65, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x72, 0x65, 0x6C, + 0x6F, 0x61, 0x64, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x73, 0x28, 0x29, 0x3B, 0x0A, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x49, 0x66, 0x20, 0x2D, 0x69, 0x20, 0x6F, + 0x72, 0x20, 0x2D, 0x2D, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x20, + 0x77, 0x65, 0x72, 0x65, 0x20, 0x70, 0x61, 0x73, 0x73, 0x65, 0x64, 0x2C, 0x20, 0x6F, 0x72, 0x20, + 0x73, 0x74, 0x64, 0x69, 0x6E, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x54, 0x54, 0x59, 0x2E, 0x0A, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x70, 0x72, 0x6F, 0x63, + 0x65, 0x73, 0x73, 0x2E, 0x5F, 0x66, 0x6F, 0x72, 0x63, 0x65, 0x52, 0x65, 0x70, 0x6C, 0x29, 0x20, + 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x52, + 0x45, 0x50, 0x4C, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, + 0x6E, 0x73, 0x74, 0x20, 0x63, 0x6C, 0x69, 0x52, 0x65, 0x70, 0x6C, 0x20, 0x3D, 0x20, 0x4E, 0x61, + 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x72, 0x65, 0x71, 0x75, 0x69, + 0x72, 0x65, 0x28, 0x27, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x6E, 0x61, 0x6C, 0x2F, 0x72, 0x65, 0x70, + 0x6C, 0x27, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, + 0x6C, 0x69, 0x52, 0x65, 0x70, 0x6C, 0x2E, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x49, 0x6E, 0x74, + 0x65, 0x72, 0x6E, 0x61, 0x6C, 0x52, 0x65, 0x70, 0x6C, 0x28, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, + 0x73, 0x2E, 0x65, 0x6E, 0x76, 0x2C, 0x20, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, + 0x65, 0x72, 0x72, 0x2C, 0x20, 0x72, 0x65, 0x70, 0x6C, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x65, 0x72, 0x72, + 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x74, 0x68, 0x72, 0x6F, 0x77, 0x20, 0x65, 0x72, 0x72, 0x3B, 0x0A, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x70, 0x6C, 0x2E, 0x6F, 0x6E, 0x28, 0x27, + 0x65, 0x78, 0x69, 0x74, 0x27, 0x2C, 0x20, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, + 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x72, 0x65, 0x70, 0x6C, 0x2E, 0x5F, 0x66, 0x6C, 0x75, 0x73, + 0x68, 0x69, 0x6E, 0x67, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x70, 0x6C, 0x2E, 0x70, 0x61, 0x75, + 0x73, 0x65, 0x28, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6E, 0x20, 0x72, 0x65, 0x70, + 0x6C, 0x2E, 0x6F, 0x6E, 0x63, 0x65, 0x28, 0x27, 0x66, 0x6C, 0x75, 0x73, 0x68, 0x48, 0x69, 0x73, + 0x74, 0x6F, 0x72, 0x79, 0x27, 0x2C, 0x20, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, + 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x65, 0x78, + 0x69, 0x74, 0x28, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, + 0x65, 0x78, 0x69, 0x74, 0x28, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7D, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x7D, 0x29, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x69, 0x66, 0x20, 0x28, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x5F, 0x65, 0x76, + 0x61, 0x6C, 0x20, 0x21, 0x3D, 0x20, 0x6E, 0x75, 0x6C, 0x6C, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x55, 0x73, 0x65, + 0x72, 0x20, 0x70, 0x61, 0x73, 0x73, 0x65, 0x64, 0x20, 0x27, 0x2D, 0x65, 0x27, 0x20, 0x6F, 0x72, + 0x20, 0x27, 0x2D, 0x2D, 0x65, 0x76, 0x61, 0x6C, 0x27, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x76, 0x61, 0x6C, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x28, 0x27, 0x5B, 0x65, 0x76, 0x61, 0x6C, 0x5D, 0x27, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x7D, 0x20, 0x65, 0x6C, 0x73, 0x65, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x52, 0x65, 0x61, 0x64, 0x20, 0x61, 0x6C, 0x6C, 0x20, 0x6F, + 0x66, 0x20, 0x73, 0x74, 0x64, 0x69, 0x6E, 0x20, 0x2D, 0x20, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, + 0x65, 0x20, 0x69, 0x74, 0x2E, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x73, 0x74, 0x64, 0x69, 0x6E, 0x2E, 0x73, 0x65, + 0x74, 0x45, 0x6E, 0x63, 0x6F, 0x64, 0x69, 0x6E, 0x67, 0x28, 0x27, 0x75, 0x74, 0x66, 0x38, 0x27, + 0x29, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, + 0x72, 0x20, 0x63, 0x6F, 0x64, 0x65, 0x20, 0x3D, 0x20, 0x27, 0x27, 0x3B, 0x0A, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x73, + 0x74, 0x64, 0x69, 0x6E, 0x2E, 0x6F, 0x6E, 0x28, 0x27, 0x64, 0x61, 0x74, 0x61, 0x27, 0x2C, 0x20, + 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, 0x64, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, 0x64, 0x65, 0x20, 0x2B, + 0x3D, 0x20, 0x64, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7D, + 0x29, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x72, + 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x73, 0x74, 0x64, 0x69, 0x6E, 0x2E, 0x6F, 0x6E, 0x28, 0x27, + 0x65, 0x6E, 0x64, 0x27, 0x2C, 0x20, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, 0x29, + 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, + 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x5F, 0x65, 0x76, 0x61, 0x6C, 0x20, 0x3D, 0x20, 0x63, + 0x6F, 0x64, 0x65, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x65, 0x76, 0x61, 0x6C, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x28, 0x27, 0x5B, 0x73, 0x74, + 0x64, 0x69, 0x6E, 0x5D, 0x27, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x7D, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x0A, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x0A, 0x20, 0x20, + 0x7D, 0x0A, 0x0A, 0x20, 0x20, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x73, 0x65, + 0x74, 0x75, 0x70, 0x50, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x4F, 0x62, 0x6A, 0x65, 0x63, 0x74, + 0x28, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, + 0x2E, 0x5F, 0x73, 0x65, 0x74, 0x75, 0x70, 0x50, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x4F, 0x62, + 0x6A, 0x65, 0x63, 0x74, 0x28, 0x70, 0x75, 0x73, 0x68, 0x56, 0x61, 0x6C, 0x75, 0x65, 0x54, 0x6F, + 0x41, 0x72, 0x72, 0x61, 0x79, 0x29, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6E, + 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x70, 0x75, 0x73, 0x68, 0x56, 0x61, 0x6C, 0x75, 0x65, 0x54, + 0x6F, 0x41, 0x72, 0x72, 0x61, 0x79, 0x28, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x66, 0x6F, 0x72, 0x20, 0x28, 0x76, 0x61, 0x72, 0x20, 0x69, 0x20, 0x3D, 0x20, 0x30, 0x3B, + 0x20, 0x69, 0x20, 0x3C, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6D, 0x65, 0x6E, 0x74, 0x73, 0x2E, 0x6C, + 0x65, 0x6E, 0x67, 0x74, 0x68, 0x3B, 0x20, 0x69, 0x2B, 0x2B, 0x29, 0x0A, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x70, 0x75, 0x73, 0x68, 0x28, 0x61, 0x72, + 0x67, 0x75, 0x6D, 0x65, 0x6E, 0x74, 0x73, 0x5B, 0x69, 0x5D, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, + 0x20, 0x7D, 0x0A, 0x20, 0x20, 0x7D, 0x0A, 0x0A, 0x20, 0x20, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, + 0x6F, 0x6E, 0x20, 0x73, 0x65, 0x74, 0x75, 0x70, 0x47, 0x6C, 0x6F, 0x62, 0x61, 0x6C, 0x56, 0x61, + 0x72, 0x69, 0x61, 0x62, 0x6C, 0x65, 0x73, 0x28, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, + 0x67, 0x6C, 0x6F, 0x62, 0x61, 0x6C, 0x2E, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x20, 0x3D, + 0x20, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, + 0x6E, 0x73, 0x74, 0x20, 0x75, 0x74, 0x69, 0x6C, 0x20, 0x3D, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, + 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, + 0x27, 0x75, 0x74, 0x69, 0x6C, 0x27, 0x29, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, + 0x20, 0x44, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x20, 0x47, 0x4C, 0x4F, 0x42, 0x41, + 0x4C, 0x20, 0x61, 0x6E, 0x64, 0x20, 0x72, 0x6F, 0x6F, 0x74, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x5B, + 0x27, 0x47, 0x4C, 0x4F, 0x42, 0x41, 0x4C, 0x27, 0x2C, 0x20, 0x27, 0x72, 0x6F, 0x6F, 0x74, 0x27, + 0x5D, 0x2E, 0x66, 0x6F, 0x72, 0x45, 0x61, 0x63, 0x68, 0x28, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, + 0x6F, 0x6E, 0x28, 0x6E, 0x61, 0x6D, 0x65, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x2F, 0x2F, 0x20, 0x67, 0x65, 0x74, 0x74, 0x65, 0x72, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x20, 0x67, 0x65, 0x74, 0x20, 0x3D, 0x20, 0x75, 0x74, 0x69, + 0x6C, 0x2E, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x28, 0x66, 0x75, 0x6E, 0x63, + 0x74, 0x69, 0x6F, 0x6E, 0x28, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6E, 0x20, 0x74, 0x68, 0x69, 0x73, 0x3B, 0x0A, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7D, 0x2C, 0x20, 0x60, 0x27, 0x24, 0x7B, 0x6E, 0x61, 0x6D, 0x65, 0x7D, + 0x27, 0x20, 0x69, 0x73, 0x20, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x2C, + 0x20, 0x75, 0x73, 0x65, 0x20, 0x27, 0x67, 0x6C, 0x6F, 0x62, 0x61, 0x6C, 0x27, 0x60, 0x29, 0x3B, + 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x73, 0x65, 0x74, 0x74, 0x65, 0x72, + 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x20, 0x73, 0x65, 0x74, + 0x20, 0x3D, 0x20, 0x75, 0x74, 0x69, 0x6C, 0x2E, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, + 0x65, 0x28, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, 0x76, 0x61, 0x6C, 0x75, 0x65, + 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x4F, 0x62, 0x6A, 0x65, + 0x63, 0x74, 0x2E, 0x64, 0x65, 0x66, 0x69, 0x6E, 0x65, 0x50, 0x72, 0x6F, 0x70, 0x65, 0x72, 0x74, + 0x79, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2C, 0x20, 0x6E, 0x61, 0x6D, 0x65, 0x2C, 0x20, 0x7B, 0x0A, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, 0x6E, 0x66, 0x69, 0x67, + 0x75, 0x72, 0x61, 0x62, 0x6C, 0x65, 0x3A, 0x20, 0x74, 0x72, 0x75, 0x65, 0x2C, 0x0A, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x77, 0x72, 0x69, 0x74, 0x61, 0x62, 0x6C, 0x65, + 0x3A, 0x20, 0x74, 0x72, 0x75, 0x65, 0x2C, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x65, 0x6E, 0x75, 0x6D, 0x65, 0x72, 0x61, 0x62, 0x6C, 0x65, 0x3A, 0x20, 0x74, 0x72, + 0x75, 0x65, 0x2C, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, + 0x6C, 0x75, 0x65, 0x3A, 0x20, 0x76, 0x61, 0x6C, 0x75, 0x65, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x7D, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x2C, 0x20, + 0x60, 0x27, 0x24, 0x7B, 0x6E, 0x61, 0x6D, 0x65, 0x7D, 0x27, 0x20, 0x69, 0x73, 0x20, 0x64, 0x65, + 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x2C, 0x20, 0x75, 0x73, 0x65, 0x20, 0x27, 0x67, + 0x6C, 0x6F, 0x62, 0x61, 0x6C, 0x27, 0x60, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x2F, 0x2F, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6E, 0x65, 0x20, 0x70, 0x72, 0x6F, 0x70, 0x65, 0x72, + 0x74, 0x79, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x4F, 0x62, 0x6A, 0x65, 0x63, 0x74, 0x2E, + 0x64, 0x65, 0x66, 0x69, 0x6E, 0x65, 0x50, 0x72, 0x6F, 0x70, 0x65, 0x72, 0x74, 0x79, 0x28, 0x67, + 0x6C, 0x6F, 0x62, 0x61, 0x6C, 0x2C, 0x20, 0x6E, 0x61, 0x6D, 0x65, 0x2C, 0x20, 0x7B, 0x20, 0x67, + 0x65, 0x74, 0x2C, 0x20, 0x73, 0x65, 0x74, 0x2C, 0x20, 0x63, 0x6F, 0x6E, 0x66, 0x69, 0x67, 0x75, + 0x72, 0x61, 0x62, 0x6C, 0x65, 0x3A, 0x20, 0x74, 0x72, 0x75, 0x65, 0x20, 0x7D, 0x29, 0x3B, 0x0A, + 0x20, 0x20, 0x20, 0x20, 0x7D, 0x29, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x67, 0x6C, 0x6F, + 0x62, 0x61, 0x6C, 0x2E, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x20, 0x3D, 0x20, 0x4E, 0x61, 0x74, + 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, + 0x65, 0x28, 0x27, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x27, 0x29, 0x2E, 0x42, 0x75, 0x66, 0x66, + 0x65, 0x72, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, + 0x64, 0x6F, 0x6D, 0x61, 0x69, 0x6E, 0x20, 0x3D, 0x20, 0x6E, 0x75, 0x6C, 0x6C, 0x3B, 0x0A, 0x20, + 0x20, 0x20, 0x20, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x5F, 0x65, 0x78, 0x69, 0x74, + 0x69, 0x6E, 0x67, 0x20, 0x3D, 0x20, 0x66, 0x61, 0x6C, 0x73, 0x65, 0x3B, 0x0A, 0x20, 0x20, 0x7D, + 0x0A, 0x0A, 0x20, 0x20, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x73, 0x65, 0x74, + 0x75, 0x70, 0x47, 0x6C, 0x6F, 0x62, 0x61, 0x6C, 0x54, 0x69, 0x6D, 0x65, 0x6F, 0x75, 0x74, 0x73, + 0x28, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x20, 0x74, + 0x69, 0x6D, 0x65, 0x72, 0x73, 0x20, 0x3D, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, + 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x27, 0x74, 0x69, + 0x6D, 0x65, 0x72, 0x73, 0x27, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x67, 0x6C, 0x6F, 0x62, + 0x61, 0x6C, 0x2E, 0x63, 0x6C, 0x65, 0x61, 0x72, 0x49, 0x6D, 0x6D, 0x65, 0x64, 0x69, 0x61, 0x74, + 0x65, 0x20, 0x3D, 0x20, 0x74, 0x69, 0x6D, 0x65, 0x72, 0x73, 0x2E, 0x63, 0x6C, 0x65, 0x61, 0x72, + 0x49, 0x6D, 0x6D, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x67, + 0x6C, 0x6F, 0x62, 0x61, 0x6C, 0x2E, 0x63, 0x6C, 0x65, 0x61, 0x72, 0x49, 0x6E, 0x74, 0x65, 0x72, + 0x76, 0x61, 0x6C, 0x20, 0x3D, 0x20, 0x74, 0x69, 0x6D, 0x65, 0x72, 0x73, 0x2E, 0x63, 0x6C, 0x65, + 0x61, 0x72, 0x49, 0x6E, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6C, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, + 0x67, 0x6C, 0x6F, 0x62, 0x61, 0x6C, 0x2E, 0x63, 0x6C, 0x65, 0x61, 0x72, 0x54, 0x69, 0x6D, 0x65, + 0x6F, 0x75, 0x74, 0x20, 0x3D, 0x20, 0x74, 0x69, 0x6D, 0x65, 0x72, 0x73, 0x2E, 0x63, 0x6C, 0x65, + 0x61, 0x72, 0x54, 0x69, 0x6D, 0x65, 0x6F, 0x75, 0x74, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x67, + 0x6C, 0x6F, 0x62, 0x61, 0x6C, 0x2E, 0x73, 0x65, 0x74, 0x49, 0x6D, 0x6D, 0x65, 0x64, 0x69, 0x61, + 0x74, 0x65, 0x20, 0x3D, 0x20, 0x74, 0x69, 0x6D, 0x65, 0x72, 0x73, 0x2E, 0x73, 0x65, 0x74, 0x49, + 0x6D, 0x6D, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x67, 0x6C, + 0x6F, 0x62, 0x61, 0x6C, 0x2E, 0x73, 0x65, 0x74, 0x49, 0x6E, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6C, + 0x20, 0x3D, 0x20, 0x74, 0x69, 0x6D, 0x65, 0x72, 0x73, 0x2E, 0x73, 0x65, 0x74, 0x49, 0x6E, 0x74, + 0x65, 0x72, 0x76, 0x61, 0x6C, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x67, 0x6C, 0x6F, 0x62, 0x61, + 0x6C, 0x2E, 0x73, 0x65, 0x74, 0x54, 0x69, 0x6D, 0x65, 0x6F, 0x75, 0x74, 0x20, 0x3D, 0x20, 0x74, + 0x69, 0x6D, 0x65, 0x72, 0x73, 0x2E, 0x73, 0x65, 0x74, 0x54, 0x69, 0x6D, 0x65, 0x6F, 0x75, 0x74, + 0x3B, 0x0A, 0x20, 0x20, 0x7D, 0x0A, 0x0A, 0x20, 0x20, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, + 0x6E, 0x20, 0x73, 0x65, 0x74, 0x75, 0x70, 0x47, 0x6C, 0x6F, 0x62, 0x61, 0x6C, 0x43, 0x6F, 0x6E, + 0x73, 0x6F, 0x6C, 0x65, 0x28, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, + 0x20, 0x69, 0x6E, 0x73, 0x70, 0x65, 0x63, 0x74, 0x6F, 0x72, 0x43, 0x6F, 0x6E, 0x73, 0x6F, 0x6C, + 0x65, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x77, 0x72, 0x61, 0x70, 0x43, + 0x6F, 0x6E, 0x73, 0x6F, 0x6C, 0x65, 0x43, 0x61, 0x6C, 0x6C, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, + 0x69, 0x66, 0x20, 0x28, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x69, 0x6E, 0x73, 0x70, + 0x65, 0x63, 0x74, 0x6F, 0x72, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, + 0x6E, 0x73, 0x70, 0x65, 0x63, 0x74, 0x6F, 0x72, 0x43, 0x6F, 0x6E, 0x73, 0x6F, 0x6C, 0x65, 0x20, + 0x3D, 0x20, 0x67, 0x6C, 0x6F, 0x62, 0x61, 0x6C, 0x2E, 0x63, 0x6F, 0x6E, 0x73, 0x6F, 0x6C, 0x65, + 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x77, 0x72, 0x61, 0x70, 0x43, 0x6F, 0x6E, 0x73, + 0x6F, 0x6C, 0x65, 0x43, 0x61, 0x6C, 0x6C, 0x20, 0x3D, 0x20, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, + 0x73, 0x2E, 0x69, 0x6E, 0x73, 0x70, 0x65, 0x63, 0x74, 0x6F, 0x72, 0x2E, 0x77, 0x72, 0x61, 0x70, + 0x43, 0x6F, 0x6E, 0x73, 0x6F, 0x6C, 0x65, 0x43, 0x61, 0x6C, 0x6C, 0x3B, 0x0A, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x64, 0x65, 0x6C, 0x65, 0x74, 0x65, 0x20, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, + 0x73, 0x2E, 0x69, 0x6E, 0x73, 0x70, 0x65, 0x63, 0x74, 0x6F, 0x72, 0x3B, 0x0A, 0x20, 0x20, 0x20, + 0x20, 0x7D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x6F, + 0x6C, 0x65, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x4F, 0x62, 0x6A, 0x65, 0x63, 0x74, 0x2E, 0x64, + 0x65, 0x66, 0x69, 0x6E, 0x65, 0x50, 0x72, 0x6F, 0x70, 0x65, 0x72, 0x74, 0x79, 0x28, 0x67, 0x6C, + 0x6F, 0x62, 0x61, 0x6C, 0x2C, 0x20, 0x27, 0x63, 0x6F, 0x6E, 0x73, 0x6F, 0x6C, 0x65, 0x27, 0x2C, + 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, 0x6E, 0x66, 0x69, 0x67, 0x75, + 0x72, 0x61, 0x62, 0x6C, 0x65, 0x3A, 0x20, 0x74, 0x72, 0x75, 0x65, 0x2C, 0x0A, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x65, 0x6E, 0x75, 0x6D, 0x65, 0x72, 0x61, 0x62, 0x6C, 0x65, 0x3A, 0x20, 0x74, + 0x72, 0x75, 0x65, 0x2C, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x67, 0x65, 0x74, 0x3A, 0x20, + 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x21, 0x63, 0x6F, 0x6E, 0x73, 0x6F, 0x6C, + 0x65, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, + 0x6F, 0x6E, 0x73, 0x6F, 0x6C, 0x65, 0x20, 0x3D, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, + 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x27, 0x63, + 0x6F, 0x6E, 0x73, 0x6F, 0x6C, 0x65, 0x27, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x69, 0x6E, 0x73, 0x74, 0x61, 0x6C, 0x6C, 0x49, 0x6E, 0x73, 0x70, 0x65, + 0x63, 0x74, 0x6F, 0x72, 0x43, 0x6F, 0x6E, 0x73, 0x6F, 0x6C, 0x65, 0x49, 0x66, 0x4E, 0x65, 0x65, + 0x64, 0x65, 0x64, 0x28, 0x63, 0x6F, 0x6E, 0x73, 0x6F, 0x6C, 0x65, 0x2C, 0x0A, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6E, 0x73, 0x70, 0x65, 0x63, 0x74, 0x6F, 0x72, + 0x43, 0x6F, 0x6E, 0x73, 0x6F, 0x6C, 0x65, 0x2C, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x77, 0x72, 0x61, 0x70, 0x43, 0x6F, 0x6E, 0x73, 0x6F, 0x6C, 0x65, 0x43, 0x61, + 0x6C, 0x6C, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x0A, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6E, 0x20, 0x63, 0x6F, + 0x6E, 0x73, 0x6F, 0x6C, 0x65, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x0A, 0x20, + 0x20, 0x20, 0x20, 0x7D, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x7D, 0x0A, 0x0A, 0x20, 0x20, 0x66, 0x75, + 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x69, 0x6E, 0x73, 0x74, 0x61, 0x6C, 0x6C, 0x49, 0x6E, + 0x73, 0x70, 0x65, 0x63, 0x74, 0x6F, 0x72, 0x43, 0x6F, 0x6E, 0x73, 0x6F, 0x6C, 0x65, 0x49, 0x66, + 0x4E, 0x65, 0x65, 0x64, 0x65, 0x64, 0x28, 0x63, 0x6F, 0x6E, 0x73, 0x6F, 0x6C, 0x65, 0x2C, 0x0A, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6E, 0x73, 0x70, 0x65, + 0x63, 0x74, 0x6F, 0x72, 0x43, 0x6F, 0x6E, 0x73, 0x6F, 0x6C, 0x65, 0x2C, 0x0A, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x77, 0x72, 0x61, 0x70, 0x43, 0x6F, 0x6E, 0x73, + 0x6F, 0x6C, 0x65, 0x43, 0x61, 0x6C, 0x6C, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x69, + 0x66, 0x20, 0x28, 0x21, 0x69, 0x6E, 0x73, 0x70, 0x65, 0x63, 0x74, 0x6F, 0x72, 0x43, 0x6F, 0x6E, + 0x73, 0x6F, 0x6C, 0x65, 0x29, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6E, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x20, 0x63, 0x6F, + 0x6E, 0x66, 0x69, 0x67, 0x20, 0x3D, 0x20, 0x7B, 0x7D, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x66, + 0x6F, 0x72, 0x20, 0x28, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x20, 0x6B, 0x65, 0x79, 0x20, 0x6F, 0x66, + 0x20, 0x4F, 0x62, 0x6A, 0x65, 0x63, 0x74, 0x2E, 0x6B, 0x65, 0x79, 0x73, 0x28, 0x63, 0x6F, 0x6E, + 0x73, 0x6F, 0x6C, 0x65, 0x29, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, + 0x66, 0x20, 0x28, 0x21, 0x69, 0x6E, 0x73, 0x70, 0x65, 0x63, 0x74, 0x6F, 0x72, 0x43, 0x6F, 0x6E, + 0x73, 0x6F, 0x6C, 0x65, 0x2E, 0x68, 0x61, 0x73, 0x4F, 0x77, 0x6E, 0x50, 0x72, 0x6F, 0x70, 0x65, + 0x72, 0x74, 0x79, 0x28, 0x6B, 0x65, 0x79, 0x29, 0x29, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x63, 0x6F, 0x6E, 0x74, 0x69, 0x6E, 0x75, 0x65, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x49, 0x66, 0x20, 0x6E, 0x6F, 0x64, 0x65, 0x20, 0x63, 0x6F, 0x6E, + 0x73, 0x6F, 0x6C, 0x65, 0x20, 0x68, 0x61, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x61, 0x6D, + 0x65, 0x20, 0x6D, 0x65, 0x74, 0x68, 0x6F, 0x64, 0x20, 0x61, 0x73, 0x20, 0x69, 0x6E, 0x73, 0x70, + 0x65, 0x63, 0x74, 0x6F, 0x72, 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x6F, 0x6C, 0x65, 0x2C, 0x0A, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x74, 0x68, 0x65, 0x6E, 0x20, 0x77, 0x72, 0x61, + 0x70, 0x20, 0x74, 0x68, 0x65, 0x73, 0x65, 0x20, 0x74, 0x77, 0x6F, 0x20, 0x6D, 0x65, 0x74, 0x68, + 0x6F, 0x64, 0x73, 0x20, 0x69, 0x6E, 0x74, 0x6F, 0x20, 0x6F, 0x6E, 0x65, 0x2E, 0x20, 0x4E, 0x61, + 0x74, 0x69, 0x76, 0x65, 0x20, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x20, 0x77, 0x69, 0x6C, + 0x6C, 0x20, 0x70, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x2F, 0x2F, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6F, 0x72, 0x69, 0x67, 0x69, 0x6E, 0x61, 0x6C, + 0x20, 0x73, 0x74, 0x61, 0x63, 0x6B, 0x2E, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, + 0x6E, 0x73, 0x6F, 0x6C, 0x65, 0x5B, 0x6B, 0x65, 0x79, 0x5D, 0x20, 0x3D, 0x20, 0x77, 0x72, 0x61, + 0x70, 0x43, 0x6F, 0x6E, 0x73, 0x6F, 0x6C, 0x65, 0x43, 0x61, 0x6C, 0x6C, 0x28, 0x69, 0x6E, 0x73, + 0x70, 0x65, 0x63, 0x74, 0x6F, 0x72, 0x43, 0x6F, 0x6E, 0x73, 0x6F, 0x6C, 0x65, 0x5B, 0x6B, 0x65, + 0x79, 0x5D, 0x2C, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x6F, 0x6C, 0x65, + 0x5B, 0x6B, 0x65, 0x79, 0x5D, 0x2C, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, 0x6E, 0x66, + 0x69, 0x67, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x66, + 0x6F, 0x72, 0x20, 0x28, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x20, 0x6B, 0x65, 0x79, 0x20, 0x6F, 0x66, + 0x20, 0x4F, 0x62, 0x6A, 0x65, 0x63, 0x74, 0x2E, 0x6B, 0x65, 0x79, 0x73, 0x28, 0x69, 0x6E, 0x73, + 0x70, 0x65, 0x63, 0x74, 0x6F, 0x72, 0x43, 0x6F, 0x6E, 0x73, 0x6F, 0x6C, 0x65, 0x29, 0x29, 0x20, + 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x63, 0x6F, 0x6E, 0x73, + 0x6F, 0x6C, 0x65, 0x2E, 0x68, 0x61, 0x73, 0x4F, 0x77, 0x6E, 0x50, 0x72, 0x6F, 0x70, 0x65, 0x72, + 0x74, 0x79, 0x28, 0x6B, 0x65, 0x79, 0x29, 0x29, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x63, 0x6F, 0x6E, 0x74, 0x69, 0x6E, 0x75, 0x65, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x6F, 0x6C, 0x65, 0x5B, 0x6B, 0x65, 0x79, 0x5D, 0x20, 0x3D, 0x20, + 0x69, 0x6E, 0x73, 0x70, 0x65, 0x63, 0x74, 0x6F, 0x72, 0x43, 0x6F, 0x6E, 0x73, 0x6F, 0x6C, 0x65, + 0x5B, 0x6B, 0x65, 0x79, 0x5D, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x0A, 0x20, 0x20, 0x7D, + 0x0A, 0x0A, 0x20, 0x20, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x73, 0x65, 0x74, + 0x75, 0x70, 0x50, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x46, 0x61, 0x74, 0x61, 0x6C, 0x28, 0x29, + 0x20, 0x7B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, + 0x5F, 0x66, 0x61, 0x74, 0x61, 0x6C, 0x45, 0x78, 0x63, 0x65, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x20, + 0x3D, 0x20, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, 0x65, 0x72, 0x29, 0x20, 0x7B, + 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x63, 0x61, 0x75, 0x67, 0x68, + 0x74, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x70, 0x72, + 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x64, 0x6F, 0x6D, 0x61, 0x69, 0x6E, 0x20, 0x26, 0x26, 0x20, + 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x64, 0x6F, 0x6D, 0x61, 0x69, 0x6E, 0x2E, 0x5F, + 0x65, 0x72, 0x72, 0x6F, 0x72, 0x48, 0x61, 0x6E, 0x64, 0x6C, 0x65, 0x72, 0x29, 0x0A, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x75, 0x67, 0x68, 0x74, 0x20, 0x3D, 0x20, 0x70, + 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x64, 0x6F, 0x6D, 0x61, 0x69, 0x6E, 0x2E, 0x5F, 0x65, + 0x72, 0x72, 0x6F, 0x72, 0x48, 0x61, 0x6E, 0x64, 0x6C, 0x65, 0x72, 0x28, 0x65, 0x72, 0x29, 0x20, + 0x7C, 0x7C, 0x20, 0x63, 0x61, 0x75, 0x67, 0x68, 0x74, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x21, 0x63, 0x61, 0x75, 0x67, 0x68, 0x74, 0x29, 0x0A, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x75, 0x67, 0x68, 0x74, 0x20, 0x3D, 0x20, + 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x65, 0x6D, 0x69, 0x74, 0x28, 0x27, 0x75, 0x6E, + 0x63, 0x61, 0x75, 0x67, 0x68, 0x74, 0x45, 0x78, 0x63, 0x65, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x27, + 0x2C, 0x20, 0x65, 0x72, 0x29, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, + 0x20, 0x49, 0x66, 0x20, 0x73, 0x6F, 0x6D, 0x65, 0x6F, 0x6E, 0x65, 0x20, 0x68, 0x61, 0x6E, 0x64, + 0x6C, 0x65, 0x64, 0x20, 0x69, 0x74, 0x2C, 0x20, 0x74, 0x68, 0x65, 0x6E, 0x20, 0x67, 0x72, 0x65, + 0x61, 0x74, 0x2E, 0x20, 0x20, 0x6F, 0x74, 0x68, 0x65, 0x72, 0x77, 0x69, 0x73, 0x65, 0x2C, 0x20, + 0x64, 0x69, 0x65, 0x20, 0x69, 0x6E, 0x20, 0x43, 0x2B, 0x2B, 0x20, 0x6C, 0x61, 0x6E, 0x64, 0x0A, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x73, 0x69, 0x6E, 0x63, 0x65, 0x20, 0x74, + 0x68, 0x61, 0x74, 0x20, 0x6D, 0x65, 0x61, 0x6E, 0x73, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x77, + 0x65, 0x27, 0x6C, 0x6C, 0x20, 0x65, 0x78, 0x69, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x72, + 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2C, 0x20, 0x65, 0x6D, 0x69, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x27, 0x65, 0x78, 0x69, 0x74, 0x27, 0x20, 0x65, 0x76, 0x65, 0x6E, 0x74, 0x0A, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x21, 0x63, 0x61, 0x75, 0x67, 0x68, 0x74, 0x29, 0x20, + 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x72, 0x79, 0x20, 0x7B, 0x0A, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x21, 0x70, + 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x5F, 0x65, 0x78, 0x69, 0x74, 0x69, 0x6E, 0x67, 0x29, + 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, + 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x5F, 0x65, 0x78, 0x69, 0x74, 0x69, 0x6E, 0x67, 0x20, + 0x3D, 0x20, 0x74, 0x72, 0x75, 0x65, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x65, 0x6D, 0x69, 0x74, + 0x28, 0x27, 0x65, 0x78, 0x69, 0x74, 0x27, 0x2C, 0x20, 0x31, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x7D, 0x20, 0x63, 0x61, 0x74, 0x63, 0x68, 0x20, 0x28, 0x65, 0x72, 0x29, 0x20, 0x7B, 0x0A, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x6E, 0x6F, 0x74, + 0x68, 0x69, 0x6E, 0x67, 0x20, 0x74, 0x6F, 0x20, 0x62, 0x65, 0x20, 0x64, 0x6F, 0x6E, 0x65, 0x20, + 0x61, 0x62, 0x6F, 0x75, 0x74, 0x20, 0x69, 0x74, 0x20, 0x61, 0x74, 0x20, 0x74, 0x68, 0x69, 0x73, + 0x20, 0x70, 0x6F, 0x69, 0x6E, 0x74, 0x2E, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x7D, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x69, 0x66, 0x20, 0x77, + 0x65, 0x20, 0x68, 0x61, 0x6E, 0x64, 0x6C, 0x65, 0x64, 0x20, 0x61, 0x6E, 0x20, 0x65, 0x72, 0x72, + 0x6F, 0x72, 0x2C, 0x20, 0x74, 0x68, 0x65, 0x6E, 0x20, 0x6D, 0x61, 0x6B, 0x65, 0x20, 0x73, 0x75, + 0x72, 0x65, 0x20, 0x61, 0x6E, 0x79, 0x20, 0x74, 0x69, 0x63, 0x6B, 0x73, 0x20, 0x67, 0x65, 0x74, + 0x20, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x7D, 0x20, 0x65, 0x6C, 0x73, 0x65, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x72, + 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x27, 0x74, 0x69, 0x6D, 0x65, 0x72, 0x73, 0x27, 0x29, + 0x2E, 0x73, 0x65, 0x74, 0x49, 0x6D, 0x6D, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x28, 0x70, 0x72, + 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x5F, 0x74, 0x69, 0x63, 0x6B, 0x43, 0x61, 0x6C, 0x6C, 0x62, + 0x61, 0x63, 0x6B, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x0A, 0x0A, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6E, 0x20, 0x63, 0x61, 0x75, 0x67, + 0x68, 0x74, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x3B, 0x0A, 0x20, 0x20, 0x7D, 0x0A, 0x0A, + 0x20, 0x20, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x74, 0x72, 0x79, 0x47, 0x65, + 0x74, 0x43, 0x77, 0x64, 0x28, 0x70, 0x61, 0x74, 0x68, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, + 0x20, 0x76, 0x61, 0x72, 0x20, 0x74, 0x68, 0x72, 0x65, 0x77, 0x20, 0x3D, 0x20, 0x74, 0x72, 0x75, + 0x65, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x63, 0x77, 0x64, 0x3B, 0x0A, + 0x20, 0x20, 0x20, 0x20, 0x74, 0x72, 0x79, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x63, 0x77, 0x64, 0x20, 0x3D, 0x20, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x63, 0x77, + 0x64, 0x28, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, 0x72, 0x65, 0x77, + 0x20, 0x3D, 0x20, 0x66, 0x61, 0x6C, 0x73, 0x65, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x20, + 0x66, 0x69, 0x6E, 0x61, 0x6C, 0x6C, 0x79, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x69, 0x66, 0x20, 0x28, 0x74, 0x68, 0x72, 0x65, 0x77, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x67, 0x65, 0x74, 0x63, 0x77, 0x64, 0x28, 0x33, + 0x29, 0x20, 0x63, 0x61, 0x6E, 0x20, 0x66, 0x61, 0x69, 0x6C, 0x20, 0x69, 0x66, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6E, 0x74, 0x20, 0x77, 0x6F, 0x72, 0x6B, 0x69, 0x6E, + 0x67, 0x20, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6F, 0x72, 0x79, 0x20, 0x68, 0x61, 0x73, 0x20, + 0x62, 0x65, 0x65, 0x6E, 0x20, 0x64, 0x65, 0x6C, 0x65, 0x74, 0x65, 0x64, 0x2E, 0x0A, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x46, 0x61, 0x6C, 0x6C, 0x20, 0x62, 0x61, + 0x63, 0x6B, 0x20, 0x74, 0x6F, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, + 0x6F, 0x72, 0x79, 0x20, 0x6E, 0x61, 0x6D, 0x65, 0x20, 0x6F, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x28, 0x61, 0x62, 0x73, 0x6F, 0x6C, 0x75, 0x74, 0x65, 0x29, 0x20, 0x65, 0x78, 0x65, 0x63, 0x75, + 0x74, 0x61, 0x62, 0x6C, 0x65, 0x20, 0x70, 0x61, 0x74, 0x68, 0x2E, 0x0A, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x49, 0x74, 0x27, 0x73, 0x20, 0x6E, 0x6F, 0x74, 0x20, + 0x72, 0x65, 0x61, 0x6C, 0x6C, 0x79, 0x20, 0x63, 0x6F, 0x72, 0x72, 0x65, 0x63, 0x74, 0x20, 0x62, + 0x75, 0x74, 0x20, 0x77, 0x68, 0x61, 0x74, 0x20, 0x61, 0x72, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x61, 0x6C, 0x74, 0x65, 0x72, 0x6E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x73, 0x3F, 0x0A, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6E, 0x20, 0x70, 0x61, 0x74, + 0x68, 0x2E, 0x64, 0x69, 0x72, 0x6E, 0x61, 0x6D, 0x65, 0x28, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, + 0x73, 0x2E, 0x65, 0x78, 0x65, 0x63, 0x50, 0x61, 0x74, 0x68, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x7D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6E, 0x20, 0x63, 0x77, 0x64, 0x3B, 0x0A, 0x20, 0x20, 0x7D, 0x0A, 0x0A, + 0x20, 0x20, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x65, 0x76, 0x61, 0x6C, 0x53, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x28, 0x6E, 0x61, 0x6D, 0x65, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, + 0x20, 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x20, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x20, 0x3D, + 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x72, 0x65, + 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x27, 0x6D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x27, 0x29, 0x3B, + 0x0A, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x20, 0x70, 0x61, 0x74, 0x68, 0x20, + 0x3D, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x72, + 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x27, 0x70, 0x61, 0x74, 0x68, 0x27, 0x29, 0x3B, 0x0A, + 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x20, 0x63, 0x77, 0x64, 0x20, 0x3D, 0x20, + 0x74, 0x72, 0x79, 0x47, 0x65, 0x74, 0x43, 0x77, 0x64, 0x28, 0x70, 0x61, 0x74, 0x68, 0x29, 0x3B, + 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x20, 0x6D, 0x6F, 0x64, 0x75, + 0x6C, 0x65, 0x20, 0x3D, 0x20, 0x6E, 0x65, 0x77, 0x20, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x28, + 0x6E, 0x61, 0x6D, 0x65, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x6D, 0x6F, 0x64, 0x75, 0x6C, + 0x65, 0x2E, 0x66, 0x69, 0x6C, 0x65, 0x6E, 0x61, 0x6D, 0x65, 0x20, 0x3D, 0x20, 0x70, 0x61, 0x74, + 0x68, 0x2E, 0x6A, 0x6F, 0x69, 0x6E, 0x28, 0x63, 0x77, 0x64, 0x2C, 0x20, 0x6E, 0x61, 0x6D, 0x65, + 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x6D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x70, 0x61, + 0x74, 0x68, 0x73, 0x20, 0x3D, 0x20, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x5F, 0x6E, 0x6F, + 0x64, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x50, 0x61, 0x74, 0x68, 0x73, 0x28, 0x63, 0x77, + 0x64, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x20, 0x62, 0x6F, + 0x64, 0x79, 0x20, 0x3D, 0x20, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x5F, 0x65, 0x76, + 0x61, 0x6C, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x20, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x20, 0x3D, 0x20, 0x60, 0x67, 0x6C, 0x6F, 0x62, 0x61, 0x6C, 0x2E, 0x5F, + 0x5F, 0x66, 0x69, 0x6C, 0x65, 0x6E, 0x61, 0x6D, 0x65, 0x20, 0x3D, 0x20, 0x24, 0x7B, 0x4A, 0x53, + 0x4F, 0x4E, 0x2E, 0x73, 0x74, 0x72, 0x69, 0x6E, 0x67, 0x69, 0x66, 0x79, 0x28, 0x6E, 0x61, 0x6D, + 0x65, 0x29, 0x7D, 0x3B, 0x5C, 0x6E, 0x60, 0x20, 0x2B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x27, 0x67, 0x6C, + 0x6F, 0x62, 0x61, 0x6C, 0x2E, 0x65, 0x78, 0x70, 0x6F, 0x72, 0x74, 0x73, 0x20, 0x3D, 0x20, 0x65, + 0x78, 0x70, 0x6F, 0x72, 0x74, 0x73, 0x3B, 0x5C, 0x6E, 0x27, 0x20, 0x2B, 0x0A, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x27, 0x67, 0x6C, 0x6F, 0x62, 0x61, 0x6C, 0x2E, 0x6D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x20, 0x3D, + 0x20, 0x6D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x3B, 0x5C, 0x6E, 0x27, 0x20, 0x2B, 0x0A, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x27, 0x67, 0x6C, 0x6F, 0x62, 0x61, 0x6C, 0x2E, 0x5F, 0x5F, 0x64, 0x69, 0x72, 0x6E, 0x61, + 0x6D, 0x65, 0x20, 0x3D, 0x20, 0x5F, 0x5F, 0x64, 0x69, 0x72, 0x6E, 0x61, 0x6D, 0x65, 0x3B, 0x5C, + 0x6E, 0x27, 0x20, 0x2B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x27, 0x67, 0x6C, 0x6F, 0x62, 0x61, 0x6C, 0x2E, + 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x20, 0x3D, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, + 0x65, 0x3B, 0x5C, 0x6E, 0x27, 0x20, 0x2B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x27, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6E, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x22, 0x76, 0x6D, 0x22, 0x29, + 0x2E, 0x72, 0x75, 0x6E, 0x49, 0x6E, 0x54, 0x68, 0x69, 0x73, 0x43, 0x6F, 0x6E, 0x74, 0x65, 0x78, + 0x74, 0x28, 0x27, 0x20, 0x2B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x60, 0x24, 0x7B, 0x4A, 0x53, 0x4F, 0x4E, + 0x2E, 0x73, 0x74, 0x72, 0x69, 0x6E, 0x67, 0x69, 0x66, 0x79, 0x28, 0x62, 0x6F, 0x64, 0x79, 0x29, + 0x7D, 0x2C, 0x20, 0x7B, 0x20, 0x66, 0x69, 0x6C, 0x65, 0x6E, 0x61, 0x6D, 0x65, 0x3A, 0x20, 0x60, + 0x20, 0x2B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x60, 0x24, 0x7B, 0x4A, 0x53, 0x4F, 0x4E, 0x2E, 0x73, 0x74, + 0x72, 0x69, 0x6E, 0x67, 0x69, 0x66, 0x79, 0x28, 0x6E, 0x61, 0x6D, 0x65, 0x29, 0x7D, 0x2C, 0x20, + 0x64, 0x69, 0x73, 0x70, 0x6C, 0x61, 0x79, 0x45, 0x72, 0x72, 0x6F, 0x72, 0x73, 0x3A, 0x20, 0x74, + 0x72, 0x75, 0x65, 0x20, 0x7D, 0x29, 0x3B, 0x5C, 0x6E, 0x60, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, + 0x2F, 0x2F, 0x20, 0x44, 0x65, 0x66, 0x65, 0x72, 0x20, 0x65, 0x76, 0x61, 0x6C, 0x75, 0x61, 0x74, + 0x69, 0x6F, 0x6E, 0x20, 0x66, 0x6F, 0x72, 0x20, 0x61, 0x20, 0x74, 0x69, 0x63, 0x6B, 0x2E, 0x20, + 0x20, 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x77, 0x6F, 0x72, 0x6B, 0x61, + 0x72, 0x6F, 0x75, 0x6E, 0x64, 0x20, 0x66, 0x6F, 0x72, 0x20, 0x64, 0x65, 0x66, 0x65, 0x72, 0x72, + 0x65, 0x64, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x65, 0x76, 0x65, 0x6E, 0x74, 0x73, + 0x20, 0x6E, 0x6F, 0x74, 0x20, 0x66, 0x69, 0x72, 0x69, 0x6E, 0x67, 0x20, 0x77, 0x68, 0x65, 0x6E, + 0x20, 0x65, 0x76, 0x61, 0x6C, 0x75, 0x61, 0x74, 0x69, 0x6E, 0x67, 0x20, 0x73, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x73, 0x20, 0x66, 0x72, 0x6F, 0x6D, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6F, 0x6D, + 0x6D, 0x61, 0x6E, 0x64, 0x20, 0x6C, 0x69, 0x6E, 0x65, 0x2C, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x2F, + 0x2F, 0x20, 0x73, 0x65, 0x65, 0x20, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3A, 0x2F, 0x2F, 0x67, 0x69, + 0x74, 0x68, 0x75, 0x62, 0x2E, 0x63, 0x6F, 0x6D, 0x2F, 0x6E, 0x6F, 0x64, 0x65, 0x6A, 0x73, 0x2F, + 0x6E, 0x6F, 0x64, 0x65, 0x2F, 0x69, 0x73, 0x73, 0x75, 0x65, 0x73, 0x2F, 0x31, 0x36, 0x30, 0x30, + 0x2E, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65, 0x74, 0x49, 0x6D, 0x6D, 0x65, 0x64, 0x69, 0x61, + 0x74, 0x65, 0x28, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, 0x29, 0x20, 0x7B, 0x0A, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x20, 0x72, 0x65, 0x73, 0x75, + 0x6C, 0x74, 0x20, 0x3D, 0x20, 0x6D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x5F, 0x63, 0x6F, 0x6D, + 0x70, 0x69, 0x6C, 0x65, 0x28, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2C, 0x20, 0x60, 0x24, 0x7B, + 0x6E, 0x61, 0x6D, 0x65, 0x7D, 0x2D, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x60, 0x29, 0x3B, + 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x70, 0x72, 0x6F, 0x63, 0x65, + 0x73, 0x73, 0x2E, 0x5F, 0x70, 0x72, 0x69, 0x6E, 0x74, 0x5F, 0x65, 0x76, 0x61, 0x6C, 0x29, 0x20, + 0x63, 0x6F, 0x6E, 0x73, 0x6F, 0x6C, 0x65, 0x2E, 0x6C, 0x6F, 0x67, 0x28, 0x72, 0x65, 0x73, 0x75, + 0x6C, 0x74, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x7D, + 0x0A, 0x0A, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x4C, 0x6F, 0x61, 0x64, 0x20, 0x70, 0x72, 0x65, 0x6C, + 0x6F, 0x61, 0x64, 0x20, 0x6D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x73, 0x0A, 0x20, 0x20, 0x66, 0x75, + 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x70, 0x72, 0x65, 0x6C, 0x6F, 0x61, 0x64, 0x4D, 0x6F, + 0x64, 0x75, 0x6C, 0x65, 0x73, 0x28, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, + 0x20, 0x28, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x5F, 0x70, 0x72, 0x65, 0x6C, 0x6F, + 0x61, 0x64, 0x5F, 0x6D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x73, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, + 0x2E, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x27, 0x6D, 0x6F, 0x64, 0x75, 0x6C, 0x65, + 0x27, 0x29, 0x2E, 0x5F, 0x70, 0x72, 0x65, 0x6C, 0x6F, 0x61, 0x64, 0x4D, 0x6F, 0x64, 0x75, 0x6C, + 0x65, 0x73, 0x28, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x5F, 0x70, 0x72, 0x65, 0x6C, + 0x6F, 0x61, 0x64, 0x5F, 0x6D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x73, 0x29, 0x3B, 0x0A, 0x20, 0x20, + 0x20, 0x20, 0x7D, 0x0A, 0x20, 0x20, 0x7D, 0x0A, 0x0A, 0x20, 0x20, 0x66, 0x75, 0x6E, 0x63, 0x74, + 0x69, 0x6F, 0x6E, 0x20, 0x69, 0x73, 0x44, 0x65, 0x62, 0x75, 0x67, 0x42, 0x72, 0x65, 0x61, 0x6B, + 0x28, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6E, 0x20, + 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x65, 0x78, 0x65, 0x63, 0x41, 0x72, 0x67, 0x76, + 0x2E, 0x73, 0x6F, 0x6D, 0x65, 0x28, 0x28, 0x61, 0x72, 0x67, 0x29, 0x20, 0x3D, 0x3E, 0x20, 0x7B, + 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6E, 0x20, 0x61, 0x72, + 0x67, 0x2E, 0x6D, 0x61, 0x74, 0x63, 0x68, 0x28, 0x2F, 0x5E, 0x2D, 0x2D, 0x64, 0x65, 0x62, 0x75, + 0x67, 0x2D, 0x62, 0x72, 0x6B, 0x28, 0x3D, 0x5B, 0x30, 0x2D, 0x39, 0x5D, 0x2A, 0x29, 0x3F, 0x24, + 0x2F, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x7D, 0x0A, + 0x0A, 0x20, 0x20, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x72, 0x75, 0x6E, 0x28, + 0x65, 0x6E, 0x74, 0x72, 0x79, 0x46, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x29, 0x20, 0x7B, + 0x0A, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, + 0x2E, 0x5F, 0x64, 0x65, 0x62, 0x75, 0x67, 0x57, 0x61, 0x69, 0x74, 0x43, 0x6F, 0x6E, 0x6E, 0x65, + 0x63, 0x74, 0x20, 0x26, 0x26, 0x20, 0x69, 0x73, 0x44, 0x65, 0x62, 0x75, 0x67, 0x42, 0x72, 0x65, + 0x61, 0x6B, 0x28, 0x29, 0x29, 0x20, 0x7B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, + 0x2F, 0x20, 0x58, 0x58, 0x58, 0x20, 0x46, 0x69, 0x78, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x74, + 0x65, 0x72, 0x72, 0x69, 0x62, 0x6C, 0x65, 0x20, 0x68, 0x61, 0x63, 0x6B, 0x21, 0x0A, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, + 0x47, 0x69, 0x76, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6C, 0x69, 0x65, 0x6E, 0x74, 0x20, + 0x70, 0x72, 0x6F, 0x67, 0x72, 0x61, 0x6D, 0x20, 0x61, 0x20, 0x66, 0x65, 0x77, 0x20, 0x74, 0x69, + 0x63, 0x6B, 0x73, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6F, 0x6E, 0x6E, 0x65, 0x63, 0x74, 0x2E, 0x0A, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x4F, 0x74, 0x68, 0x65, 0x72, 0x77, 0x69, + 0x73, 0x65, 0x2C, 0x20, 0x74, 0x68, 0x65, 0x72, 0x65, 0x27, 0x73, 0x20, 0x61, 0x20, 0x72, 0x61, + 0x63, 0x65, 0x20, 0x63, 0x6F, 0x6E, 0x64, 0x69, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x77, 0x68, 0x65, + 0x72, 0x65, 0x20, 0x60, 0x6E, 0x6F, 0x64, 0x65, 0x20, 0x64, 0x65, 0x62, 0x75, 0x67, 0x20, 0x66, + 0x6F, 0x6F, 0x2E, 0x6A, 0x73, 0x60, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, + 0x77, 0x69, 0x6C, 0x6C, 0x20, 0x6E, 0x6F, 0x74, 0x20, 0x62, 0x65, 0x20, 0x61, 0x62, 0x6C, 0x65, + 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6F, 0x6E, 0x6E, 0x65, 0x63, 0x74, 0x20, 0x69, 0x6E, 0x20, 0x74, + 0x69, 0x6D, 0x65, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x61, 0x74, 0x63, 0x68, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, + 0x62, 0x72, 0x65, 0x61, 0x6B, 0x70, 0x6F, 0x69, 0x6E, 0x74, 0x20, 0x6D, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x20, 0x6F, 0x6E, 0x20, 0x6C, 0x69, 0x6E, 0x65, 0x20, 0x31, 0x2E, 0x0A, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, + 0x41, 0x20, 0x62, 0x65, 0x74, 0x74, 0x65, 0x72, 0x20, 0x66, 0x69, 0x78, 0x20, 0x77, 0x6F, 0x75, + 0x6C, 0x64, 0x20, 0x62, 0x65, 0x20, 0x74, 0x6F, 0x20, 0x73, 0x6F, 0x6D, 0x65, 0x68, 0x6F, 0x77, + 0x20, 0x67, 0x65, 0x74, 0x20, 0x61, 0x20, 0x6D, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x66, + 0x72, 0x6F, 0x6D, 0x20, 0x74, 0x68, 0x65, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, + 0x20, 0x56, 0x38, 0x20, 0x64, 0x65, 0x62, 0x75, 0x67, 0x20, 0x6F, 0x62, 0x6A, 0x65, 0x63, 0x74, + 0x20, 0x61, 0x62, 0x6F, 0x75, 0x74, 0x20, 0x61, 0x20, 0x63, 0x6F, 0x6E, 0x6E, 0x65, 0x63, 0x74, + 0x69, 0x6F, 0x6E, 0x2C, 0x20, 0x61, 0x6E, 0x64, 0x20, 0x72, 0x75, 0x6E, 0x4D, 0x61, 0x69, 0x6E, + 0x20, 0x77, 0x68, 0x65, 0x6E, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x74, + 0x68, 0x61, 0x74, 0x20, 0x6F, 0x63, 0x63, 0x75, 0x72, 0x73, 0x2E, 0x20, 0x20, 0x2D, 0x2D, 0x69, + 0x73, 0x61, 0x61, 0x63, 0x73, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, 0x6E, + 0x73, 0x74, 0x20, 0x64, 0x65, 0x62, 0x75, 0x67, 0x54, 0x69, 0x6D, 0x65, 0x6F, 0x75, 0x74, 0x20, + 0x3D, 0x20, 0x2B, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x65, 0x6E, 0x76, 0x2E, 0x4E, + 0x4F, 0x44, 0x45, 0x5F, 0x44, 0x45, 0x42, 0x55, 0x47, 0x5F, 0x54, 0x49, 0x4D, 0x45, 0x4F, 0x55, + 0x54, 0x20, 0x7C, 0x7C, 0x20, 0x35, 0x30, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, + 0x65, 0x74, 0x54, 0x69, 0x6D, 0x65, 0x6F, 0x75, 0x74, 0x28, 0x65, 0x6E, 0x74, 0x72, 0x79, 0x46, + 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x2C, 0x20, 0x64, 0x65, 0x62, 0x75, 0x67, 0x54, 0x69, + 0x6D, 0x65, 0x6F, 0x75, 0x74, 0x29, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x20, 0x65, + 0x6C, 0x73, 0x65, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x4D, + 0x61, 0x69, 0x6E, 0x20, 0x65, 0x6E, 0x74, 0x72, 0x79, 0x20, 0x70, 0x6F, 0x69, 0x6E, 0x74, 0x20, + 0x69, 0x6E, 0x74, 0x6F, 0x20, 0x6D, 0x6F, 0x73, 0x74, 0x20, 0x70, 0x72, 0x6F, 0x67, 0x72, 0x61, + 0x6D, 0x73, 0x3A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6E, 0x74, 0x72, 0x79, 0x46, + 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x7D, + 0x0A, 0x20, 0x20, 0x7D, 0x0A, 0x0A, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x42, 0x65, 0x6C, 0x6F, 0x77, + 0x20, 0x79, 0x6F, 0x75, 0x20, 0x66, 0x69, 0x6E, 0x64, 0x20, 0x61, 0x20, 0x6D, 0x69, 0x6E, 0x69, + 0x6D, 0x61, 0x6C, 0x20, 0x6D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x20, 0x73, 0x79, 0x73, 0x74, 0x65, + 0x6D, 0x2C, 0x20, 0x77, 0x68, 0x69, 0x63, 0x68, 0x20, 0x69, 0x73, 0x20, 0x75, 0x73, 0x65, 0x64, + 0x20, 0x74, 0x6F, 0x20, 0x6C, 0x6F, 0x61, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6E, 0x6F, 0x64, + 0x65, 0x0A, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x63, 0x6F, 0x72, 0x65, 0x20, 0x6D, 0x6F, 0x64, 0x75, + 0x6C, 0x65, 0x73, 0x20, 0x66, 0x6F, 0x75, 0x6E, 0x64, 0x20, 0x69, 0x6E, 0x20, 0x6C, 0x69, 0x62, + 0x2F, 0x2A, 0x2E, 0x6A, 0x73, 0x2E, 0x20, 0x41, 0x6C, 0x6C, 0x20, 0x63, 0x6F, 0x72, 0x65, 0x20, + 0x6D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x63, 0x6F, 0x6D, 0x70, + 0x69, 0x6C, 0x65, 0x64, 0x20, 0x69, 0x6E, 0x74, 0x6F, 0x20, 0x74, 0x68, 0x65, 0x0A, 0x20, 0x20, + 0x2F, 0x2F, 0x20, 0x6E, 0x6F, 0x64, 0x65, 0x20, 0x62, 0x69, 0x6E, 0x61, 0x72, 0x79, 0x2C, 0x20, + 0x73, 0x6F, 0x20, 0x74, 0x68, 0x65, 0x79, 0x20, 0x63, 0x61, 0x6E, 0x20, 0x62, 0x65, 0x20, 0x6C, + 0x6F, 0x61, 0x64, 0x65, 0x64, 0x20, 0x66, 0x61, 0x73, 0x74, 0x65, 0x72, 0x2E, 0x0A, 0x0A, 0x20, + 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x20, 0x43, 0x6F, 0x6E, 0x74, 0x65, 0x78, 0x74, 0x69, 0x66, + 0x79, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x20, 0x3D, 0x20, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, + 0x73, 0x2E, 0x62, 0x69, 0x6E, 0x64, 0x69, 0x6E, 0x67, 0x28, 0x27, 0x63, 0x6F, 0x6E, 0x74, 0x65, + 0x78, 0x74, 0x69, 0x66, 0x79, 0x27, 0x29, 0x2E, 0x43, 0x6F, 0x6E, 0x74, 0x65, 0x78, 0x74, 0x69, + 0x66, 0x79, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x3B, 0x0A, 0x20, 0x20, 0x66, 0x75, 0x6E, 0x63, + 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x72, 0x75, 0x6E, 0x49, 0x6E, 0x54, 0x68, 0x69, 0x73, 0x43, 0x6F, + 0x6E, 0x74, 0x65, 0x78, 0x74, 0x28, 0x63, 0x6F, 0x64, 0x65, 0x2C, 0x20, 0x6F, 0x70, 0x74, 0x69, + 0x6F, 0x6E, 0x73, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, + 0x20, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x20, 0x3D, 0x20, 0x6E, 0x65, 0x77, 0x20, 0x43, 0x6F, + 0x6E, 0x74, 0x65, 0x78, 0x74, 0x69, 0x66, 0x79, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x28, 0x63, + 0x6F, 0x64, 0x65, 0x2C, 0x20, 0x6F, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x73, 0x29, 0x3B, 0x0A, 0x20, + 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6E, 0x20, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x2E, 0x72, 0x75, 0x6E, 0x49, 0x6E, 0x54, 0x68, 0x69, 0x73, 0x43, 0x6F, 0x6E, 0x74, 0x65, 0x78, + 0x74, 0x28, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x7D, 0x0A, 0x0A, 0x20, 0x20, 0x66, 0x75, 0x6E, 0x63, + 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, + 0x65, 0x28, 0x69, 0x64, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, 0x69, 0x73, + 0x2E, 0x66, 0x69, 0x6C, 0x65, 0x6E, 0x61, 0x6D, 0x65, 0x20, 0x3D, 0x20, 0x60, 0x24, 0x7B, 0x69, + 0x64, 0x7D, 0x2E, 0x6A, 0x73, 0x60, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, 0x69, 0x73, + 0x2E, 0x69, 0x64, 0x20, 0x3D, 0x20, 0x69, 0x64, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, + 0x69, 0x73, 0x2E, 0x65, 0x78, 0x70, 0x6F, 0x72, 0x74, 0x73, 0x20, 0x3D, 0x20, 0x7B, 0x7D, 0x3B, + 0x0A, 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x6C, 0x6F, 0x61, 0x64, 0x65, 0x64, + 0x20, 0x3D, 0x20, 0x66, 0x61, 0x6C, 0x73, 0x65, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, + 0x69, 0x73, 0x2E, 0x6C, 0x6F, 0x61, 0x64, 0x69, 0x6E, 0x67, 0x20, 0x3D, 0x20, 0x66, 0x61, 0x6C, + 0x73, 0x65, 0x3B, 0x0A, 0x20, 0x20, 0x7D, 0x0A, 0x0A, 0x20, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, + 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x5F, 0x73, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x20, + 0x3D, 0x20, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x62, 0x69, 0x6E, 0x64, 0x69, 0x6E, + 0x67, 0x28, 0x27, 0x6E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x73, 0x27, 0x29, 0x3B, 0x0A, 0x20, 0x20, + 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x5F, 0x63, 0x61, + 0x63, 0x68, 0x65, 0x20, 0x3D, 0x20, 0x7B, 0x7D, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x4E, 0x61, 0x74, + 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, + 0x65, 0x20, 0x3D, 0x20, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, 0x69, 0x64, 0x29, + 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x69, 0x64, 0x20, 0x3D, 0x3D, + 0x3D, 0x20, 0x27, 0x6E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5F, 0x6D, 0x6F, 0x64, 0x75, 0x6C, 0x65, + 0x27, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x6E, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x3B, 0x0A, + 0x20, 0x20, 0x20, 0x20, 0x7D, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, + 0x20, 0x63, 0x61, 0x63, 0x68, 0x65, 0x64, 0x20, 0x3D, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, + 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x67, 0x65, 0x74, 0x43, 0x61, 0x63, 0x68, 0x65, 0x64, + 0x28, 0x69, 0x64, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x63, 0x61, + 0x63, 0x68, 0x65, 0x64, 0x20, 0x26, 0x26, 0x20, 0x28, 0x63, 0x61, 0x63, 0x68, 0x65, 0x64, 0x2E, + 0x6C, 0x6F, 0x61, 0x64, 0x65, 0x64, 0x20, 0x7C, 0x7C, 0x20, 0x63, 0x61, 0x63, 0x68, 0x65, 0x64, + 0x2E, 0x6C, 0x6F, 0x61, 0x64, 0x69, 0x6E, 0x67, 0x29, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6E, 0x20, 0x63, 0x61, 0x63, 0x68, 0x65, 0x64, + 0x2E, 0x65, 0x78, 0x70, 0x6F, 0x72, 0x74, 0x73, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x0A, + 0x0A, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x21, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, + 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x65, 0x78, 0x69, 0x73, 0x74, 0x73, 0x28, 0x69, 0x64, + 0x29, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, 0x72, 0x6F, 0x77, + 0x20, 0x6E, 0x65, 0x77, 0x20, 0x45, 0x72, 0x72, 0x6F, 0x72, 0x28, 0x60, 0x4E, 0x6F, 0x20, 0x73, + 0x75, 0x63, 0x68, 0x20, 0x6E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x6D, 0x6F, 0x64, 0x75, 0x6C, + 0x65, 0x20, 0x24, 0x7B, 0x69, 0x64, 0x7D, 0x60, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x7D, + 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x6D, 0x6F, + 0x64, 0x75, 0x6C, 0x65, 0x4C, 0x6F, 0x61, 0x64, 0x4C, 0x69, 0x73, 0x74, 0x2E, 0x70, 0x75, 0x73, + 0x68, 0x28, 0x60, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x20, + 0x24, 0x7B, 0x69, 0x64, 0x7D, 0x60, 0x29, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, + 0x6E, 0x73, 0x74, 0x20, 0x6E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, + 0x20, 0x3D, 0x20, 0x6E, 0x65, 0x77, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, + 0x75, 0x6C, 0x65, 0x28, 0x69, 0x64, 0x29, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x6E, 0x61, + 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x63, 0x61, 0x63, 0x68, 0x65, + 0x28, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x6E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, + 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x63, 0x6F, 0x6D, 0x70, 0x69, 0x6C, 0x65, 0x28, 0x29, 0x3B, 0x0A, + 0x0A, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6E, 0x20, 0x6E, 0x61, 0x74, 0x69, + 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x65, 0x78, 0x70, 0x6F, 0x72, 0x74, 0x73, + 0x3B, 0x0A, 0x20, 0x20, 0x7D, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, + 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x67, 0x65, 0x74, 0x43, 0x61, 0x63, 0x68, 0x65, 0x64, + 0x20, 0x3D, 0x20, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, 0x69, 0x64, 0x29, 0x20, + 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6E, 0x20, 0x4E, 0x61, 0x74, + 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x5F, 0x63, 0x61, 0x63, 0x68, 0x65, + 0x5B, 0x69, 0x64, 0x5D, 0x3B, 0x0A, 0x20, 0x20, 0x7D, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x4E, 0x61, + 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x65, 0x78, 0x69, 0x73, 0x74, + 0x73, 0x20, 0x3D, 0x20, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, 0x69, 0x64, 0x29, + 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6E, 0x20, 0x4E, 0x61, + 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x5F, 0x73, 0x6F, 0x75, 0x72, + 0x63, 0x65, 0x2E, 0x68, 0x61, 0x73, 0x4F, 0x77, 0x6E, 0x50, 0x72, 0x6F, 0x70, 0x65, 0x72, 0x74, + 0x79, 0x28, 0x69, 0x64, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x7D, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x63, + 0x6F, 0x6E, 0x73, 0x74, 0x20, 0x45, 0x58, 0x50, 0x4F, 0x53, 0x45, 0x5F, 0x49, 0x4E, 0x54, 0x45, + 0x52, 0x4E, 0x41, 0x4C, 0x53, 0x20, 0x3D, 0x20, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, + 0x65, 0x78, 0x65, 0x63, 0x41, 0x72, 0x67, 0x76, 0x2E, 0x73, 0x6F, 0x6D, 0x65, 0x28, 0x66, 0x75, + 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, 0x61, 0x72, 0x67, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6E, 0x20, 0x61, 0x72, 0x67, 0x2E, 0x6D, 0x61, 0x74, + 0x63, 0x68, 0x28, 0x2F, 0x5E, 0x2D, 0x2D, 0x65, 0x78, 0x70, 0x6F, 0x73, 0x65, 0x5B, 0x2D, 0x5F, + 0x5D, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x6E, 0x61, 0x6C, 0x73, 0x24, 0x2F, 0x29, 0x3B, 0x0A, 0x20, + 0x20, 0x7D, 0x29, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x45, 0x58, 0x50, 0x4F, + 0x53, 0x45, 0x5F, 0x49, 0x4E, 0x54, 0x45, 0x52, 0x4E, 0x41, 0x4C, 0x53, 0x29, 0x20, 0x7B, 0x0A, + 0x20, 0x20, 0x20, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, + 0x2E, 0x6E, 0x6F, 0x6E, 0x49, 0x6E, 0x74, 0x65, 0x72, 0x6E, 0x61, 0x6C, 0x45, 0x78, 0x69, 0x73, + 0x74, 0x73, 0x20, 0x3D, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, + 0x65, 0x2E, 0x65, 0x78, 0x69, 0x73, 0x74, 0x73, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x4E, + 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x69, 0x73, 0x49, 0x6E, + 0x74, 0x65, 0x72, 0x6E, 0x61, 0x6C, 0x20, 0x3D, 0x20, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, + 0x6E, 0x28, 0x69, 0x64, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6E, 0x20, 0x66, 0x61, 0x6C, 0x73, 0x65, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, + 0x7D, 0x3B, 0x0A, 0x20, 0x20, 0x7D, 0x20, 0x65, 0x6C, 0x73, 0x65, 0x20, 0x7B, 0x0A, 0x20, 0x20, + 0x20, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x6E, + 0x6F, 0x6E, 0x49, 0x6E, 0x74, 0x65, 0x72, 0x6E, 0x61, 0x6C, 0x45, 0x78, 0x69, 0x73, 0x74, 0x73, + 0x20, 0x3D, 0x20, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, 0x69, 0x64, 0x29, 0x20, + 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6E, 0x20, 0x4E, + 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x65, 0x78, 0x69, 0x73, + 0x74, 0x73, 0x28, 0x69, 0x64, 0x29, 0x20, 0x26, 0x26, 0x20, 0x21, 0x4E, 0x61, 0x74, 0x69, 0x76, + 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x69, 0x73, 0x49, 0x6E, 0x74, 0x65, 0x72, 0x6E, + 0x61, 0x6C, 0x28, 0x69, 0x64, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x3B, 0x0A, 0x0A, + 0x20, 0x20, 0x20, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, + 0x2E, 0x69, 0x73, 0x49, 0x6E, 0x74, 0x65, 0x72, 0x6E, 0x61, 0x6C, 0x20, 0x3D, 0x20, 0x66, 0x75, + 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, 0x69, 0x64, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6E, 0x20, 0x69, 0x64, 0x2E, 0x73, 0x74, 0x61, + 0x72, 0x74, 0x73, 0x57, 0x69, 0x74, 0x68, 0x28, 0x27, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x6E, 0x61, + 0x6C, 0x2F, 0x27, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x3B, 0x0A, 0x20, 0x20, 0x7D, + 0x0A, 0x0A, 0x0A, 0x20, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, + 0x65, 0x2E, 0x67, 0x65, 0x74, 0x53, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x20, 0x3D, 0x20, 0x66, 0x75, + 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, 0x69, 0x64, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, + 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6E, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, + 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x5F, 0x73, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x5B, 0x69, 0x64, 0x5D, + 0x3B, 0x0A, 0x20, 0x20, 0x7D, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, + 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x77, 0x72, 0x61, 0x70, 0x20, 0x3D, 0x20, 0x66, 0x75, + 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x29, 0x20, 0x7B, + 0x0A, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6E, 0x20, 0x4E, 0x61, 0x74, 0x69, + 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, + 0x5B, 0x30, 0x5D, 0x20, 0x2B, 0x20, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x20, 0x2B, 0x20, 0x4E, + 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x77, 0x72, 0x61, 0x70, + 0x70, 0x65, 0x72, 0x5B, 0x31, 0x5D, 0x3B, 0x0A, 0x20, 0x20, 0x7D, 0x3B, 0x0A, 0x0A, 0x20, 0x20, + 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x77, 0x72, 0x61, + 0x70, 0x70, 0x65, 0x72, 0x20, 0x3D, 0x20, 0x5B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x27, 0x28, 0x66, + 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x28, 0x65, 0x78, 0x70, 0x6F, 0x72, 0x74, 0x73, + 0x2C, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x2C, 0x20, 0x6D, 0x6F, 0x64, 0x75, 0x6C, + 0x65, 0x2C, 0x20, 0x5F, 0x5F, 0x66, 0x69, 0x6C, 0x65, 0x6E, 0x61, 0x6D, 0x65, 0x2C, 0x20, 0x5F, + 0x5F, 0x64, 0x69, 0x72, 0x6E, 0x61, 0x6D, 0x65, 0x29, 0x20, 0x7B, 0x20, 0x27, 0x2C, 0x0A, 0x20, + 0x20, 0x20, 0x20, 0x27, 0x5C, 0x6E, 0x7D, 0x29, 0x3B, 0x27, 0x0A, 0x20, 0x20, 0x5D, 0x3B, 0x0A, + 0x0A, 0x20, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, + 0x70, 0x72, 0x6F, 0x74, 0x6F, 0x74, 0x79, 0x70, 0x65, 0x2E, 0x63, 0x6F, 0x6D, 0x70, 0x69, 0x6C, + 0x65, 0x20, 0x3D, 0x20, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, 0x29, 0x20, 0x7B, + 0x0A, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x73, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x20, + 0x3D, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x67, + 0x65, 0x74, 0x53, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x69, 0x64, + 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x73, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x20, 0x3D, 0x20, + 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x77, 0x72, 0x61, + 0x70, 0x28, 0x73, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x29, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, + 0x74, 0x68, 0x69, 0x73, 0x2E, 0x6C, 0x6F, 0x61, 0x64, 0x69, 0x6E, 0x67, 0x20, 0x3D, 0x20, 0x74, + 0x72, 0x75, 0x65, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x74, 0x72, 0x79, 0x20, 0x7B, 0x0A, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x20, 0x66, 0x6E, 0x20, 0x3D, + 0x20, 0x72, 0x75, 0x6E, 0x49, 0x6E, 0x54, 0x68, 0x69, 0x73, 0x43, 0x6F, 0x6E, 0x74, 0x65, 0x78, + 0x74, 0x28, 0x73, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x2C, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x66, 0x69, 0x6C, 0x65, 0x6E, 0x61, 0x6D, 0x65, 0x3A, 0x20, 0x74, 0x68, + 0x69, 0x73, 0x2E, 0x66, 0x69, 0x6C, 0x65, 0x6E, 0x61, 0x6D, 0x65, 0x2C, 0x0A, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x6C, 0x69, 0x6E, 0x65, 0x4F, 0x66, 0x66, 0x73, 0x65, 0x74, 0x3A, + 0x20, 0x30, 0x2C, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x69, 0x73, 0x70, + 0x6C, 0x61, 0x79, 0x45, 0x72, 0x72, 0x6F, 0x72, 0x73, 0x3A, 0x20, 0x74, 0x72, 0x75, 0x65, 0x0A, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x29, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x66, 0x6E, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x65, 0x78, 0x70, 0x6F, 0x72, 0x74, 0x73, 0x2C, + 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x72, 0x65, + 0x71, 0x75, 0x69, 0x72, 0x65, 0x2C, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2C, 0x20, 0x74, 0x68, 0x69, + 0x73, 0x2E, 0x66, 0x69, 0x6C, 0x65, 0x6E, 0x61, 0x6D, 0x65, 0x29, 0x3B, 0x0A, 0x0A, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x6C, 0x6F, 0x61, 0x64, 0x65, 0x64, 0x20, + 0x3D, 0x20, 0x74, 0x72, 0x75, 0x65, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x20, 0x66, 0x69, + 0x6E, 0x61, 0x6C, 0x6C, 0x79, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, + 0x69, 0x73, 0x2E, 0x6C, 0x6F, 0x61, 0x64, 0x69, 0x6E, 0x67, 0x20, 0x3D, 0x20, 0x66, 0x61, 0x6C, + 0x73, 0x65, 0x3B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x0A, 0x20, 0x20, 0x7D, 0x3B, 0x0A, 0x0A, + 0x20, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x70, + 0x72, 0x6F, 0x74, 0x6F, 0x74, 0x79, 0x70, 0x65, 0x2E, 0x63, 0x61, 0x63, 0x68, 0x65, 0x20, 0x3D, + 0x20, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, 0x29, 0x20, 0x7B, 0x0A, 0x20, 0x20, + 0x20, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x5F, + 0x63, 0x61, 0x63, 0x68, 0x65, 0x5B, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x69, 0x64, 0x5D, 0x20, 0x3D, + 0x20, 0x74, 0x68, 0x69, 0x73, 0x3B, 0x0A, 0x20, 0x20, 0x7D, 0x3B, 0x0A, 0x0A, 0x20, 0x20, 0x66, + 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x73, 0x65, 0x74, 0x75, 0x70, 0x41, 0x73, 0x61, + 0x72, 0x53, 0x75, 0x70, 0x70, 0x6F, 0x72, 0x74, 0x28, 0x29, 0x20, 0x7B, 0x0D, 0x0A, 0x20, 0x20, + 0x20, 0x20, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2E, 0x62, 0x69, 0x6E, 0x64, 0x69, 0x6E, + 0x67, 0x28, 0x27, 0x61, 0x74, 0x6F, 0x6D, 0x5F, 0x63, 0x6F, 0x6D, 0x6D, 0x6F, 0x6E, 0x5F, 0x61, + 0x73, 0x61, 0x72, 0x27, 0x29, 0x2E, 0x69, 0x6E, 0x69, 0x74, 0x41, 0x73, 0x61, 0x72, 0x53, 0x75, + 0x70, 0x70, 0x6F, 0x72, 0x74, 0x28, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x2C, 0x20, 0x4E, + 0x61, 0x74, 0x69, 0x76, 0x65, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x2E, 0x72, 0x65, 0x71, 0x75, + 0x69, 0x72, 0x65, 0x29, 0x3B, 0x0D, 0x0A, 0x20, 0x20, 0x7D, 0x0A, 0x0A, 0x20, 0x20, 0x73, 0x74, + 0x61, 0x72, 0x74, 0x75, 0x70, 0x28, 0x29, 0x3B, 0x0A, 0x7D, 0x29, 0x3B, 0x0A }; } \ No newline at end of file diff --git a/node/src/cares_wrap.cc b/node/src/cares_wrap.cc index 50d24bf04a..ba73dea3ed 100644 --- a/node/src/cares_wrap.cc +++ b/node/src/cares_wrap.cc @@ -20,1385 +20,1419 @@ #include #include -#if defined(__ANDROID__) || \ - defined(__MINGW32__) || \ - defined(__OpenBSD__) || \ - defined(_MSC_VER) +#if defined(__ANDROID__) || defined(__MINGW32__) || defined(__OpenBSD__) || defined(_MSC_VER) -# include +#include #else -# include +#include #endif #if defined(__OpenBSD__) -# define AI_V4MAPPED 0 +#define AI_V4MAPPED 0 #endif namespace node { namespace cares_wrap { -using v8::Array; -using v8::Context; -using v8::EscapableHandleScope; -using v8::FunctionCallbackInfo; -using v8::FunctionTemplate; -using v8::HandleScope; -using v8::Integer; -using v8::Local; -using v8::Null; -using v8::Object; -using v8::String; -using v8::Value; - - -inline const char* ToErrorCodeString(int status) { - switch (status) { -#define V(code) case ARES_##code: return #code; - V(EADDRGETNETWORKPARAMS) - V(EBADFAMILY) - V(EBADFLAGS) - V(EBADHINTS) - V(EBADNAME) - V(EBADQUERY) - V(EBADRESP) - V(EBADSTR) - V(ECANCELLED) - V(ECONNREFUSED) - V(EDESTRUCTION) - V(EFILE) - V(EFORMERR) - V(ELOADIPHLPAPI) - V(ENODATA) - V(ENOMEM) - V(ENONAME) - V(ENOTFOUND) - V(ENOTIMP) - V(ENOTINITIALIZED) - V(EOF) - V(EREFUSED) - V(ESERVFAIL) - V(ETIMEOUT) + using v8::Array; + using v8::Context; + using v8::EscapableHandleScope; + using v8::FunctionCallbackInfo; + using v8::FunctionTemplate; + using v8::HandleScope; + using v8::Integer; + using v8::Local; + using v8::Null; + using v8::Object; + using v8::String; + using v8::Value; + + inline const char* ToErrorCodeString(int status) + { + switch (status) { +#define V(code) \ + case ARES_##code: \ + return #code; + V(EADDRGETNETWORKPARAMS) + V(EBADFAMILY) + V(EBADFLAGS) + V(EBADHINTS) + V(EBADNAME) + V(EBADQUERY) + V(EBADRESP) + V(EBADSTR) + V(ECANCELLED) + V(ECONNREFUSED) + V(EDESTRUCTION) + V(EFILE) + V(EFORMERR) + V(ELOADIPHLPAPI) + V(ENODATA) + V(ENOMEM) + V(ENONAME) + V(ENOTFOUND) + V(ENOTIMP) + V(ENOTINITIALIZED) + V(EOF) + V(EREFUSED) + V(ESERVFAIL) + V(ETIMEOUT) #undef V - } - return "UNKNOWN_ARES_ERROR"; -} - -class GetAddrInfoReqWrap : public ReqWrap { - public: - GetAddrInfoReqWrap(Environment* env, Local req_wrap_obj); - - size_t self_size() const override { return sizeof(*this); } -}; - -GetAddrInfoReqWrap::GetAddrInfoReqWrap(Environment* env, - Local req_wrap_obj) - : ReqWrap(env, req_wrap_obj, AsyncWrap::PROVIDER_GETADDRINFOREQWRAP) { - Wrap(req_wrap_obj, this); -} - - -static void NewGetAddrInfoReqWrap(const FunctionCallbackInfo& args) { - CHECK(args.IsConstructCall()); -} - - -class GetNameInfoReqWrap : public ReqWrap { - public: - GetNameInfoReqWrap(Environment* env, Local req_wrap_obj); - - size_t self_size() const override { return sizeof(*this); } -}; - -GetNameInfoReqWrap::GetNameInfoReqWrap(Environment* env, - Local req_wrap_obj) - : ReqWrap(env, req_wrap_obj, AsyncWrap::PROVIDER_GETNAMEINFOREQWRAP) { - Wrap(req_wrap_obj, this); -} - - -static void NewGetNameInfoReqWrap(const FunctionCallbackInfo& args) { - CHECK(args.IsConstructCall()); -} - - -static void NewQueryReqWrap(const FunctionCallbackInfo& args) { - CHECK(args.IsConstructCall()); -} - - -static int cmp_ares_tasks(const node_ares_task* a, const node_ares_task* b) { - if (a->sock < b->sock) - return -1; - if (a->sock > b->sock) - return 1; - return 0; -} - - -RB_GENERATE_STATIC(node_ares_task_list, node_ares_task, node, cmp_ares_tasks) - + } + return "UNKNOWN_ARES_ERROR"; + } + class GetAddrInfoReqWrap : public ReqWrap { + public: + GetAddrInfoReqWrap(Environment* env, Local req_wrap_obj); -/* This is called once per second by loop->timer. It is used to constantly */ -/* call back into c-ares for possibly processing timeouts. */ -static void ares_timeout(uv_timer_t* handle) { - Environment* env = Environment::from_cares_timer_handle(handle); - CHECK_EQ(false, RB_EMPTY(env->cares_task_list())); - ares_process_fd(env->cares_channel(), ARES_SOCKET_BAD, ARES_SOCKET_BAD); -} + size_t self_size() const override { return sizeof(*this); } + }; + GetAddrInfoReqWrap::GetAddrInfoReqWrap(Environment* env, + Local req_wrap_obj) + : ReqWrap(env, req_wrap_obj, AsyncWrap::PROVIDER_GETADDRINFOREQWRAP) + { + Wrap(req_wrap_obj, this); + } -static void ares_poll_cb(uv_poll_t* watcher, int status, int events) { - node_ares_task* task = ContainerOf(&node_ares_task::poll_watcher, watcher); - Environment* env = task->env; + static void NewGetAddrInfoReqWrap(const FunctionCallbackInfo& args) + { + NODE_CHECK(args.IsConstructCall()); + } - /* Reset the idle timer */ - uv_timer_again(env->cares_timer_handle()); + class GetNameInfoReqWrap : public ReqWrap { + public: + GetNameInfoReqWrap(Environment* env, Local req_wrap_obj); - if (status < 0) { - /* An error happened. Just pretend that the socket is both readable and */ - /* writable. */ - ares_process_fd(env->cares_channel(), task->sock, task->sock); - return; - } + size_t self_size() const override { return sizeof(*this); } + }; - /* Process DNS responses */ - ares_process_fd(env->cares_channel(), - events & UV_READABLE ? task->sock : ARES_SOCKET_BAD, - events & UV_WRITABLE ? task->sock : ARES_SOCKET_BAD); -} + GetNameInfoReqWrap::GetNameInfoReqWrap(Environment* env, + Local req_wrap_obj) + : ReqWrap(env, req_wrap_obj, AsyncWrap::PROVIDER_GETNAMEINFOREQWRAP) + { + Wrap(req_wrap_obj, this); + } + static void NewGetNameInfoReqWrap(const FunctionCallbackInfo& args) + { + NODE_CHECK(args.IsConstructCall()); + } -static void ares_poll_close_cb(uv_handle_t* watcher) { - node_ares_task* task = ContainerOf(&node_ares_task::poll_watcher, - reinterpret_cast(watcher)); - free(task); -} + static void NewQueryReqWrap(const FunctionCallbackInfo& args) + { + NODE_CHECK(args.IsConstructCall()); + } + static int cmp_ares_tasks(const node_ares_task* a, const node_ares_task* b) + { + if (a->sock < b->sock) + return -1; + if (a->sock > b->sock) + return 1; + return 0; + } -/* Allocates and returns a new node_ares_task */ -static node_ares_task* ares_task_create(Environment* env, ares_socket_t sock) { - node_ares_task* task = - static_cast(node::Malloc(sizeof(*task))); + RB_GENERATE_STATIC(node_ares_task_list, node_ares_task, node, cmp_ares_tasks) - if (task == nullptr) { - /* Out of memory. */ - return nullptr; - } + /* This is called once per second by loop->timer. It is used to constantly */ + /* call back into c-ares for possibly processing timeouts. */ + static void ares_timeout(uv_timer_t* handle) + { + Environment* env = Environment::from_cares_timer_handle(handle); + NODE_CHECK_EQ(false, RB_EMPTY(env->cares_task_list())); + ares_process_fd(env->cares_channel(), ARES_SOCKET_BAD, ARES_SOCKET_BAD); + } - task->env = env; - task->sock = sock; + static void ares_poll_cb(uv_poll_t* watcher, int status, int events) + { + node_ares_task* task = ContainerOf(&node_ares_task::poll_watcher, watcher); + Environment* env = task->env; + + /* Reset the idle timer */ + uv_timer_again(env->cares_timer_handle()); + + if (status < 0) { + /* An error happened. Just pretend that the socket is both readable and */ + /* writable. */ + ares_process_fd(env->cares_channel(), task->sock, task->sock); + return; + } + + /* Process DNS responses */ + ares_process_fd(env->cares_channel(), + events & UV_READABLE ? task->sock : ARES_SOCKET_BAD, + events & UV_WRITABLE ? task->sock : ARES_SOCKET_BAD); + } - if (uv_poll_init_socket(env->event_loop(), &task->poll_watcher, sock) < 0) { - /* This should never happen. */ - free(task); - return nullptr; - } + static void ares_poll_close_cb(uv_handle_t* watcher) + { + node_ares_task* task = ContainerOf(&node_ares_task::poll_watcher, + reinterpret_cast(watcher)); + free(task); + } - return task; -} + /* Allocates and returns a new node_ares_task */ + static node_ares_task* ares_task_create(Environment* env, ares_socket_t sock) + { + node_ares_task* task = static_cast(node::Malloc(sizeof(*task))); + if (task == nullptr) { + /* Out of memory. */ + return nullptr; + } -/* Callback from ares when socket operation is started */ -static void ares_sockstate_cb(void* data, - ares_socket_t sock, - int read, - int write) { - Environment* env = static_cast(data); - node_ares_task* task; + task->env = env; + task->sock = sock; - node_ares_task lookup_task; - lookup_task.sock = sock; - task = RB_FIND(node_ares_task_list, env->cares_task_list(), &lookup_task); + if (uv_poll_init_socket(env->event_loop(), &task->poll_watcher, sock) < 0) { + /* This should never happen. */ + free(task); + return nullptr; + } - if (read || write) { - if (!task) { - /* New socket */ + return task; + } - /* If this is the first socket then start the timer. */ - uv_timer_t* timer_handle = env->cares_timer_handle(); - if (!uv_is_active(reinterpret_cast(timer_handle))) { - CHECK(RB_EMPTY(env->cares_task_list())); - uv_timer_start(timer_handle, ares_timeout, 1000, 1000); - } + /* Callback from ares when socket operation is started */ + static void ares_sockstate_cb(void* data, + ares_socket_t sock, + int read, + int write) + { + Environment* env = static_cast(data); + node_ares_task* task; + + node_ares_task lookup_task; + lookup_task.sock = sock; + task = RB_FIND(node_ares_task_list, env->cares_task_list(), &lookup_task); + + if (read || write) { + if (!task) { + /* New socket */ + + /* If this is the first socket then start the timer. */ + uv_timer_t* timer_handle = env->cares_timer_handle(); + if (!uv_is_active(reinterpret_cast(timer_handle))) { + NODE_CHECK(RB_EMPTY(env->cares_task_list())); + uv_timer_start(timer_handle, ares_timeout, 1000, 1000); + } + + task = ares_task_create(env, sock); + if (task == nullptr) { + /* This should never happen unless we're out of memory or something */ + /* is seriously wrong. The socket won't be polled, but the the query */ + /* will eventually time out. */ + return; + } + + RB_INSERT(node_ares_task_list, env->cares_task_list(), task); + } + + /* This should never fail. If it fails anyway, the query will eventually */ + /* time out. */ + uv_poll_start(&task->poll_watcher, + (read ? UV_READABLE : 0) | (write ? UV_WRITABLE : 0), + ares_poll_cb); + + } else { + /* read == 0 and write == 0 this is c-ares's way of notifying us that */ + /* the socket is now closed. We must free the data associated with */ + /* socket. */ + NODE_CHECK(task && "When an ares socket is closed we should have a handle for it"); + + RB_REMOVE(node_ares_task_list, env->cares_task_list(), task); + uv_close(reinterpret_cast(&task->poll_watcher), + ares_poll_close_cb); + + if (RB_EMPTY(env->cares_task_list())) { + uv_timer_stop(env->cares_timer_handle()); + } + } + } - task = ares_task_create(env, sock); - if (task == nullptr) { - /* This should never happen unless we're out of memory or something */ - /* is seriously wrong. The socket won't be polled, but the the query */ - /* will eventually time out. */ - return; - } + static Local HostentToAddresses(Environment* env, struct hostent* host) + { + EscapableHandleScope scope(env->isolate()); + Local addresses = Array::New(env->isolate()); - RB_INSERT(node_ares_task_list, env->cares_task_list(), task); - } + char ip[INET6_ADDRSTRLEN]; + for (uint32_t i = 0; host->h_addr_list[i] != nullptr; ++i) { + uv_inet_ntop(host->h_addrtype, host->h_addr_list[i], ip, sizeof(ip)); + Local address = OneByteString(env->isolate(), ip); + addresses->Set(i, address); + } - /* This should never fail. If it fails anyway, the query will eventually */ - /* time out. */ - uv_poll_start(&task->poll_watcher, - (read ? UV_READABLE : 0) | (write ? UV_WRITABLE : 0), - ares_poll_cb); - - } else { - /* read == 0 and write == 0 this is c-ares's way of notifying us that */ - /* the socket is now closed. We must free the data associated with */ - /* socket. */ - CHECK(task && - "When an ares socket is closed we should have a handle for it"); - - RB_REMOVE(node_ares_task_list, env->cares_task_list(), task); - uv_close(reinterpret_cast(&task->poll_watcher), - ares_poll_close_cb); - - if (RB_EMPTY(env->cares_task_list())) { - uv_timer_stop(env->cares_timer_handle()); - } - } -} - - -static Local HostentToAddresses(Environment* env, struct hostent* host) { - EscapableHandleScope scope(env->isolate()); - Local addresses = Array::New(env->isolate()); - - char ip[INET6_ADDRSTRLEN]; - for (uint32_t i = 0; host->h_addr_list[i] != nullptr; ++i) { - uv_inet_ntop(host->h_addrtype, host->h_addr_list[i], ip, sizeof(ip)); - Local address = OneByteString(env->isolate(), ip); - addresses->Set(i, address); - } - - return scope.Escape(addresses); -} - - -static Local HostentToNames(Environment* env, struct hostent* host) { - EscapableHandleScope scope(env->isolate()); - Local names = Array::New(env->isolate()); - - for (uint32_t i = 0; host->h_aliases[i] != nullptr; ++i) { - Local address = OneByteString(env->isolate(), host->h_aliases[i]); - names->Set(i, address); - } - - return scope.Escape(names); -} - - -class QueryWrap : public AsyncWrap { - public: - QueryWrap(Environment* env, Local req_wrap_obj) - : AsyncWrap(env, req_wrap_obj, AsyncWrap::PROVIDER_QUERYWRAP) { - if (env->in_domain()) - req_wrap_obj->Set(env->domain_string(), env->domain_array()->Get(0)); - } - - ~QueryWrap() override { - CHECK_EQ(false, persistent().IsEmpty()); - ClearWrap(object()); - persistent().Reset(); - } - - // Subclasses should implement the appropriate Send method. - virtual int Send(const char* name) { - UNREACHABLE(); - return 0; - } - - virtual int Send(const char* name, int family) { - UNREACHABLE(); - return 0; - } - - protected: - void* GetQueryArg() { - return static_cast(this); - } - - static void Callback(void *arg, int status, int timeouts, - unsigned char* answer_buf, int answer_len) { - QueryWrap* wrap = static_cast(arg); - - if (status != ARES_SUCCESS) { - wrap->ParseError(status); - } else { - wrap->Parse(answer_buf, answer_len); + return scope.Escape(addresses); } - delete wrap; - } + static Local HostentToNames(Environment* env, struct hostent* host) + { + EscapableHandleScope scope(env->isolate()); + Local names = Array::New(env->isolate()); - static void Callback(void *arg, int status, int timeouts, - struct hostent* host) { - QueryWrap* wrap = static_cast(arg); + for (uint32_t i = 0; host->h_aliases[i] != nullptr; ++i) { + Local address = OneByteString(env->isolate(), host->h_aliases[i]); + names->Set(i, address); + } - if (status != ARES_SUCCESS) { - wrap->ParseError(status); - } else { - wrap->Parse(host); + return scope.Escape(names); } - delete wrap; - } + class QueryWrap : public AsyncWrap { + public: + QueryWrap(Environment* env, Local req_wrap_obj) + : AsyncWrap(env, req_wrap_obj, AsyncWrap::PROVIDER_QUERYWRAP) + { + if (env->in_domain()) + req_wrap_obj->Set(env->domain_string(), env->domain_array()->Get(0)); + } + + ~QueryWrap() override + { + NODE_CHECK_EQ(false, persistent().IsEmpty()); + ClearWrap(object()); + persistent().Reset(); + } + + // Subclasses should implement the appropriate Send method. + virtual int Send(const char* name) + { + UNREACHABLE(); + return 0; + } + + virtual int Send(const char* name, int family) + { + UNREACHABLE(); + return 0; + } + + protected: + void* GetQueryArg() + { + return static_cast(this); + } + + static void Callback(void* arg, int status, int timeouts, + unsigned char* answer_buf, int answer_len) + { + QueryWrap* wrap = static_cast(arg); + + if (status != ARES_SUCCESS) { + wrap->ParseError(status); + } else { + wrap->Parse(answer_buf, answer_len); + } + + delete wrap; + } + + static void Callback(void* arg, int status, int timeouts, + struct hostent* host) + { + QueryWrap* wrap = static_cast(arg); + + if (status != ARES_SUCCESS) { + wrap->ParseError(status); + } else { + wrap->Parse(host); + } + + delete wrap; + } + + void CallOnComplete(Local answer) + { + HandleScope handle_scope(env()->isolate()); + Context::Scope context_scope(env()->context()); + Local argv[] = { + Integer::New(env()->isolate(), 0), + answer + }; + MakeCallback(env()->oncomplete_string(), arraysize(argv), argv); + } + + void CallOnComplete(Local answer, Local family) + { + HandleScope handle_scope(env()->isolate()); + Context::Scope context_scope(env()->context()); + Local argv[] = { + Integer::New(env()->isolate(), 0), + answer, + family + }; + MakeCallback(env()->oncomplete_string(), arraysize(argv), argv); + } + + void ParseError(int status) + { + NODE_CHECK_NE(status, ARES_SUCCESS); + HandleScope handle_scope(env()->isolate()); + Context::Scope context_scope(env()->context()); + const char* code = ToErrorCodeString(status); + Local arg = OneByteString(env()->isolate(), code); + MakeCallback(env()->oncomplete_string(), 1, &arg); + } + + // Subclasses should implement the appropriate Parse method. + virtual void Parse(unsigned char* buf, int len) + { + UNREACHABLE(); + } + + virtual void Parse(struct hostent* host) + { + UNREACHABLE(); + } + }; - void CallOnComplete(Local answer) { - HandleScope handle_scope(env()->isolate()); - Context::Scope context_scope(env()->context()); - Local argv[] = { - Integer::New(env()->isolate(), 0), - answer + class QueryAWrap : public QueryWrap { + public: + QueryAWrap(Environment* env, Local req_wrap_obj) + : QueryWrap(env, req_wrap_obj) + { + } + + int Send(const char* name) override + { + ares_query(env()->cares_channel(), + name, + ns_c_in, + ns_t_a, + Callback, + GetQueryArg()); + return 0; + } + + size_t self_size() const override { return sizeof(*this); } + + protected: + void Parse(unsigned char* buf, int len) override + { + HandleScope handle_scope(env()->isolate()); + Context::Scope context_scope(env()->context()); + + struct hostent* host; + + int status = ares_parse_a_reply(buf, len, &host, nullptr, nullptr); + if (status != ARES_SUCCESS) { + ParseError(status); + return; + } + + Local addresses = HostentToAddresses(env(), host); + ares_free_hostent(host); + + this->CallOnComplete(addresses); + } }; - MakeCallback(env()->oncomplete_string(), arraysize(argv), argv); - } - - void CallOnComplete(Local answer, Local family) { - HandleScope handle_scope(env()->isolate()); - Context::Scope context_scope(env()->context()); - Local argv[] = { - Integer::New(env()->isolate(), 0), - answer, - family + + class QueryAaaaWrap : public QueryWrap { + public: + QueryAaaaWrap(Environment* env, Local req_wrap_obj) + : QueryWrap(env, req_wrap_obj) + { + } + + int Send(const char* name) override + { + ares_query(env()->cares_channel(), + name, + ns_c_in, + ns_t_aaaa, + Callback, + GetQueryArg()); + return 0; + } + + size_t self_size() const override { return sizeof(*this); } + + protected: + void Parse(unsigned char* buf, int len) override + { + HandleScope handle_scope(env()->isolate()); + Context::Scope context_scope(env()->context()); + + struct hostent* host; + + int status = ares_parse_aaaa_reply(buf, len, &host, nullptr, nullptr); + if (status != ARES_SUCCESS) { + ParseError(status); + return; + } + + Local addresses = HostentToAddresses(env(), host); + ares_free_hostent(host); + + this->CallOnComplete(addresses); + } }; - MakeCallback(env()->oncomplete_string(), arraysize(argv), argv); - } - - void ParseError(int status) { - CHECK_NE(status, ARES_SUCCESS); - HandleScope handle_scope(env()->isolate()); - Context::Scope context_scope(env()->context()); - const char* code = ToErrorCodeString(status); - Local arg = OneByteString(env()->isolate(), code); - MakeCallback(env()->oncomplete_string(), 1, &arg); - } - - // Subclasses should implement the appropriate Parse method. - virtual void Parse(unsigned char* buf, int len) { - UNREACHABLE(); - } - - virtual void Parse(struct hostent* host) { - UNREACHABLE(); - } -}; - - -class QueryAWrap: public QueryWrap { - public: - QueryAWrap(Environment* env, Local req_wrap_obj) - : QueryWrap(env, req_wrap_obj) { - } - - int Send(const char* name) override { - ares_query(env()->cares_channel(), - name, - ns_c_in, - ns_t_a, - Callback, - GetQueryArg()); - return 0; - } - - size_t self_size() const override { return sizeof(*this); } - - protected: - void Parse(unsigned char* buf, int len) override { - HandleScope handle_scope(env()->isolate()); - Context::Scope context_scope(env()->context()); - - struct hostent* host; - - int status = ares_parse_a_reply(buf, len, &host, nullptr, nullptr); - if (status != ARES_SUCCESS) { - ParseError(status); - return; - } - Local addresses = HostentToAddresses(env(), host); - ares_free_hostent(host); + class QueryCnameWrap : public QueryWrap { + public: + QueryCnameWrap(Environment* env, Local req_wrap_obj) + : QueryWrap(env, req_wrap_obj) + { + } + + int Send(const char* name) override + { + ares_query(env()->cares_channel(), + name, + ns_c_in, + ns_t_cname, + Callback, + GetQueryArg()); + return 0; + } + + size_t self_size() const override { return sizeof(*this); } + + protected: + void Parse(unsigned char* buf, int len) override + { + HandleScope handle_scope(env()->isolate()); + Context::Scope context_scope(env()->context()); + struct hostent* host; + + int status = ares_parse_a_reply(buf, len, &host, nullptr, nullptr); + if (status != ARES_SUCCESS) { + ParseError(status); + return; + } + + // A cname lookup always returns a single record but we follow the + // common API here. + Local result = Array::New(env()->isolate(), 1); + result->Set(0, OneByteString(env()->isolate(), host->h_name)); + ares_free_hostent(host); + + this->CallOnComplete(result); + } + }; - this->CallOnComplete(addresses); - } -}; + class QueryMxWrap : public QueryWrap { + public: + QueryMxWrap(Environment* env, Local req_wrap_obj) + : QueryWrap(env, req_wrap_obj) + { + } + + int Send(const char* name) override + { + ares_query(env()->cares_channel(), + name, + ns_c_in, + ns_t_mx, + Callback, + GetQueryArg()); + return 0; + } + + size_t self_size() const override { return sizeof(*this); } + + protected: + void Parse(unsigned char* buf, int len) override + { + HandleScope handle_scope(env()->isolate()); + Context::Scope context_scope(env()->context()); + + struct ares_mx_reply* mx_start; + int status = ares_parse_mx_reply(buf, len, &mx_start); + if (status != ARES_SUCCESS) { + ParseError(status); + return; + } + + Local mx_records = Array::New(env()->isolate()); + Local exchange_symbol = env()->exchange_string(); + Local priority_symbol = env()->priority_string(); + + ares_mx_reply* current = mx_start; + for (uint32_t i = 0; current != nullptr; ++i, current = current->next) { + Local mx_record = Object::New(env()->isolate()); + mx_record->Set(exchange_symbol, + OneByteString(env()->isolate(), current->host)); + mx_record->Set(priority_symbol, + Integer::New(env()->isolate(), current->priority)); + mx_records->Set(i, mx_record); + } + + ares_free_data(mx_start); + + this->CallOnComplete(mx_records); + } + }; + class QueryNsWrap : public QueryWrap { + public: + QueryNsWrap(Environment* env, Local req_wrap_obj) + : QueryWrap(env, req_wrap_obj) + { + } + + int Send(const char* name) override + { + ares_query(env()->cares_channel(), + name, + ns_c_in, + ns_t_ns, + Callback, + GetQueryArg()); + return 0; + } + + size_t self_size() const override { return sizeof(*this); } + + protected: + void Parse(unsigned char* buf, int len) override + { + HandleScope handle_scope(env()->isolate()); + Context::Scope context_scope(env()->context()); + struct hostent* host; + + int status = ares_parse_ns_reply(buf, len, &host); + if (status != ARES_SUCCESS) { + ParseError(status); + return; + } + + Local names = HostentToNames(env(), host); + ares_free_hostent(host); + + this->CallOnComplete(names); + } + }; -class QueryAaaaWrap: public QueryWrap { - public: - QueryAaaaWrap(Environment* env, Local req_wrap_obj) - : QueryWrap(env, req_wrap_obj) { - } + class QueryTxtWrap : public QueryWrap { + public: + QueryTxtWrap(Environment* env, Local req_wrap_obj) + : QueryWrap(env, req_wrap_obj) + { + } + + int Send(const char* name) override + { + ares_query(env()->cares_channel(), + name, + ns_c_in, + ns_t_txt, + Callback, + GetQueryArg()); + return 0; + } + + size_t self_size() const override { return sizeof(*this); } + + protected: + void Parse(unsigned char* buf, int len) override + { + HandleScope handle_scope(env()->isolate()); + Context::Scope context_scope(env()->context()); + struct ares_txt_ext* txt_out; + + int status = ares_parse_txt_reply_ext(buf, len, &txt_out); + if (status != ARES_SUCCESS) { + ParseError(status); + return; + } + + Local txt_records = Array::New(env()->isolate()); + Local txt_chunk; + + struct ares_txt_ext* current = txt_out; + uint32_t i = 0; + for (uint32_t j = 0; current != nullptr; current = current->next) { + Local txt = OneByteString(env()->isolate(), current->txt); + // New record found - write out the current chunk + if (current->record_start) { + if (!txt_chunk.IsEmpty()) + txt_records->Set(i++, txt_chunk); + txt_chunk = Array::New(env()->isolate()); + j = 0; + } + txt_chunk->Set(j++, txt); + } + // Push last chunk if it isn't empty + if (!txt_chunk.IsEmpty()) + txt_records->Set(i, txt_chunk); + + ares_free_data(txt_out); + + this->CallOnComplete(txt_records); + } + }; - int Send(const char* name) override { - ares_query(env()->cares_channel(), - name, - ns_c_in, - ns_t_aaaa, - Callback, - GetQueryArg()); - return 0; - } + class QuerySrvWrap : public QueryWrap { + public: + explicit QuerySrvWrap(Environment* env, Local req_wrap_obj) + : QueryWrap(env, req_wrap_obj) + { + } + + int Send(const char* name) override + { + ares_query(env()->cares_channel(), + name, + ns_c_in, + ns_t_srv, + Callback, + GetQueryArg()); + return 0; + } + + size_t self_size() const override { return sizeof(*this); } + + protected: + void Parse(unsigned char* buf, int len) override + { + HandleScope handle_scope(env()->isolate()); + Context::Scope context_scope(env()->context()); + + struct ares_srv_reply* srv_start; + int status = ares_parse_srv_reply(buf, len, &srv_start); + if (status != ARES_SUCCESS) { + ParseError(status); + return; + } + + Local srv_records = Array::New(env()->isolate()); + Local name_symbol = env()->name_string(); + Local port_symbol = env()->port_string(); + Local priority_symbol = env()->priority_string(); + Local weight_symbol = env()->weight_string(); + + ares_srv_reply* current = srv_start; + for (uint32_t i = 0; current != nullptr; ++i, current = current->next) { + Local srv_record = Object::New(env()->isolate()); + srv_record->Set(name_symbol, + OneByteString(env()->isolate(), current->host)); + srv_record->Set(port_symbol, + Integer::New(env()->isolate(), current->port)); + srv_record->Set(priority_symbol, + Integer::New(env()->isolate(), current->priority)); + srv_record->Set(weight_symbol, + Integer::New(env()->isolate(), current->weight)); + srv_records->Set(i, srv_record); + } + + ares_free_data(srv_start); + + this->CallOnComplete(srv_records); + } + }; - size_t self_size() const override { return sizeof(*this); } + class QueryPtrWrap : public QueryWrap { + public: + explicit QueryPtrWrap(Environment* env, Local req_wrap_obj) + : QueryWrap(env, req_wrap_obj) + { + } + + int Send(const char* name) override + { + ares_query(env()->cares_channel(), + name, + ns_c_in, + ns_t_ptr, + Callback, + GetQueryArg()); + return 0; + } + + size_t self_size() const override { return sizeof(*this); } + + protected: + void Parse(unsigned char* buf, int len) override + { + HandleScope handle_scope(env()->isolate()); + Context::Scope context_scope(env()->context()); + + struct hostent* host; + + int status = ares_parse_ptr_reply(buf, len, NULL, 0, AF_INET, &host); + if (status != ARES_SUCCESS) { + ParseError(status); + return; + } + + Local aliases = Array::New(env()->isolate()); + + for (uint32_t i = 0; host->h_aliases[i] != NULL; i++) { + aliases->Set(i, OneByteString(env()->isolate(), host->h_aliases[i])); + } + + ares_free_hostent(host); + + this->CallOnComplete(aliases); + } + }; - protected: - void Parse(unsigned char* buf, int len) override { - HandleScope handle_scope(env()->isolate()); - Context::Scope context_scope(env()->context()); + class QueryNaptrWrap : public QueryWrap { + public: + explicit QueryNaptrWrap(Environment* env, Local req_wrap_obj) + : QueryWrap(env, req_wrap_obj) + { + } + + int Send(const char* name) override + { + ares_query(env()->cares_channel(), + name, + ns_c_in, + ns_t_naptr, + Callback, + GetQueryArg()); + return 0; + } + + size_t self_size() const override { return sizeof(*this); } + + protected: + void Parse(unsigned char* buf, int len) override + { + HandleScope handle_scope(env()->isolate()); + Context::Scope context_scope(env()->context()); + + ares_naptr_reply* naptr_start; + int status = ares_parse_naptr_reply(buf, len, &naptr_start); + + if (status != ARES_SUCCESS) { + ParseError(status); + return; + } + + Local naptr_records = Array::New(env()->isolate()); + Local flags_symbol = env()->flags_string(); + Local service_symbol = env()->service_string(); + Local regexp_symbol = env()->regexp_string(); + Local replacement_symbol = env()->replacement_string(); + Local order_symbol = env()->order_string(); + Local preference_symbol = env()->preference_string(); + + ares_naptr_reply* current = naptr_start; + for (uint32_t i = 0; current != nullptr; ++i, current = current->next) { + Local naptr_record = Object::New(env()->isolate()); + naptr_record->Set(flags_symbol, + OneByteString(env()->isolate(), current->flags)); + naptr_record->Set(service_symbol, + OneByteString(env()->isolate(), current->service)); + naptr_record->Set(regexp_symbol, + OneByteString(env()->isolate(), current->regexp)); + naptr_record->Set(replacement_symbol, + OneByteString(env()->isolate(), current->replacement)); + naptr_record->Set(order_symbol, + Integer::New(env()->isolate(), current->order)); + naptr_record->Set(preference_symbol, + Integer::New(env()->isolate(), current->preference)); + naptr_records->Set(i, naptr_record); + } + + ares_free_data(naptr_start); + + this->CallOnComplete(naptr_records); + } + }; - struct hostent* host; + class QuerySoaWrap : public QueryWrap { + public: + QuerySoaWrap(Environment* env, Local req_wrap_obj) + : QueryWrap(env, req_wrap_obj) + { + } + + int Send(const char* name) override + { + ares_query(env()->cares_channel(), + name, + ns_c_in, + ns_t_soa, + Callback, + GetQueryArg()); + return 0; + } + + size_t self_size() const override { return sizeof(*this); } + + protected: + void Parse(unsigned char* buf, int len) override + { + HandleScope handle_scope(env()->isolate()); + Context::Scope context_scope(env()->context()); + + ares_soa_reply* soa_out; + int status = ares_parse_soa_reply(buf, len, &soa_out); + + if (status != ARES_SUCCESS) { + ParseError(status); + return; + } + + Local soa_record = Object::New(env()->isolate()); + + soa_record->Set(env()->nsname_string(), + OneByteString(env()->isolate(), soa_out->nsname)); + soa_record->Set(env()->hostmaster_string(), + OneByteString(env()->isolate(), soa_out->hostmaster)); + soa_record->Set(env()->serial_string(), + Integer::New(env()->isolate(), soa_out->serial)); + soa_record->Set(env()->refresh_string(), + Integer::New(env()->isolate(), soa_out->refresh)); + soa_record->Set(env()->retry_string(), + Integer::New(env()->isolate(), soa_out->retry)); + soa_record->Set(env()->expire_string(), + Integer::New(env()->isolate(), soa_out->expire)); + soa_record->Set(env()->minttl_string(), + Integer::New(env()->isolate(), soa_out->minttl)); + + ares_free_data(soa_out); + + this->CallOnComplete(soa_record); + } + }; - int status = ares_parse_aaaa_reply(buf, len, &host, nullptr, nullptr); - if (status != ARES_SUCCESS) { - ParseError(status); - return; - } + class GetHostByAddrWrap : public QueryWrap { + public: + explicit GetHostByAddrWrap(Environment* env, Local req_wrap_obj) + : QueryWrap(env, req_wrap_obj) + { + } + + int Send(const char* name) override + { + int length, family; + char address_buffer[sizeof(struct in6_addr)]; + + if (uv_inet_pton(AF_INET, name, &address_buffer) == 0) { + length = sizeof(struct in_addr); + family = AF_INET; + } else if (uv_inet_pton(AF_INET6, name, &address_buffer) == 0) { + length = sizeof(struct in6_addr); + family = AF_INET6; + } else { + return UV_EINVAL; // So errnoException() reports a proper error. + } + + ares_gethostbyaddr(env()->cares_channel(), + address_buffer, + length, + family, + Callback, + GetQueryArg()); + return 0; + } + + size_t self_size() const override { return sizeof(*this); } + + protected: + void Parse(struct hostent* host) override + { + HandleScope handle_scope(env()->isolate()); + Context::Scope context_scope(env()->context()); + this->CallOnComplete(HostentToNames(env(), host)); + } + }; - Local addresses = HostentToAddresses(env(), host); - ares_free_hostent(host); - - this->CallOnComplete(addresses); - } -}; - - -class QueryCnameWrap: public QueryWrap { - public: - QueryCnameWrap(Environment* env, Local req_wrap_obj) - : QueryWrap(env, req_wrap_obj) { - } - - int Send(const char* name) override { - ares_query(env()->cares_channel(), - name, - ns_c_in, - ns_t_cname, - Callback, - GetQueryArg()); - return 0; - } - - size_t self_size() const override { return sizeof(*this); } - - protected: - void Parse(unsigned char* buf, int len) override { - HandleScope handle_scope(env()->isolate()); - Context::Scope context_scope(env()->context()); - struct hostent* host; - - int status = ares_parse_a_reply(buf, len, &host, nullptr, nullptr); - if (status != ARES_SUCCESS) { - ParseError(status); - return; - } + class GetHostByNameWrap : public QueryWrap { + public: + explicit GetHostByNameWrap(Environment* env, Local req_wrap_obj) + : QueryWrap(env, req_wrap_obj) + { + } + + int Send(const char* name, int family) override + { + ares_gethostbyname(env()->cares_channel(), + name, + family, + Callback, + GetQueryArg()); + return 0; + } + + protected: + void Parse(struct hostent* host) override + { + HandleScope scope(env()->isolate()); + + Local addresses = HostentToAddresses(env(), host); + Local family = Integer::New(env()->isolate(), host->h_addrtype); + + this->CallOnComplete(addresses, family); + } + }; - // A cname lookup always returns a single record but we follow the - // common API here. - Local result = Array::New(env()->isolate(), 1); - result->Set(0, OneByteString(env()->isolate(), host->h_name)); - ares_free_hostent(host); - - this->CallOnComplete(result); - } -}; - - -class QueryMxWrap: public QueryWrap { - public: - QueryMxWrap(Environment* env, Local req_wrap_obj) - : QueryWrap(env, req_wrap_obj) { - } - - int Send(const char* name) override { - ares_query(env()->cares_channel(), - name, - ns_c_in, - ns_t_mx, - Callback, - GetQueryArg()); - return 0; - } - - size_t self_size() const override { return sizeof(*this); } - - protected: - void Parse(unsigned char* buf, int len) override { - HandleScope handle_scope(env()->isolate()); - Context::Scope context_scope(env()->context()); - - struct ares_mx_reply* mx_start; - int status = ares_parse_mx_reply(buf, len, &mx_start); - if (status != ARES_SUCCESS) { - ParseError(status); - return; - } + template + static void Query(const FunctionCallbackInfo& args) + { + Environment* env = Environment::GetCurrent(args); - Local mx_records = Array::New(env()->isolate()); - Local exchange_symbol = env()->exchange_string(); - Local priority_symbol = env()->priority_string(); - - ares_mx_reply* current = mx_start; - for (uint32_t i = 0; current != nullptr; ++i, current = current->next) { - Local mx_record = Object::New(env()->isolate()); - mx_record->Set(exchange_symbol, - OneByteString(env()->isolate(), current->host)); - mx_record->Set(priority_symbol, - Integer::New(env()->isolate(), current->priority)); - mx_records->Set(i, mx_record); - } + NODE_CHECK_EQ(false, args.IsConstructCall()); + NODE_CHECK(args[0]->IsObject()); + NODE_CHECK(args[1]->IsString()); - ares_free_data(mx_start); - - this->CallOnComplete(mx_records); - } -}; - - -class QueryNsWrap: public QueryWrap { - public: - QueryNsWrap(Environment* env, Local req_wrap_obj) - : QueryWrap(env, req_wrap_obj) { - } - - int Send(const char* name) override { - ares_query(env()->cares_channel(), - name, - ns_c_in, - ns_t_ns, - Callback, - GetQueryArg()); - return 0; - } - - size_t self_size() const override { return sizeof(*this); } - - protected: - void Parse(unsigned char* buf, int len) override { - HandleScope handle_scope(env()->isolate()); - Context::Scope context_scope(env()->context()); - struct hostent* host; - - int status = ares_parse_ns_reply(buf, len, &host); - if (status != ARES_SUCCESS) { - ParseError(status); - return; - } + Local req_wrap_obj = args[0].As(); + Local string = args[1].As(); + Wrap* wrap = new Wrap(env, req_wrap_obj); - Local names = HostentToNames(env(), host); - ares_free_hostent(host); - - this->CallOnComplete(names); - } -}; - - -class QueryTxtWrap: public QueryWrap { - public: - QueryTxtWrap(Environment* env, Local req_wrap_obj) - : QueryWrap(env, req_wrap_obj) { - } - - int Send(const char* name) override { - ares_query(env()->cares_channel(), - name, - ns_c_in, - ns_t_txt, - Callback, - GetQueryArg()); - return 0; - } - - size_t self_size() const override { return sizeof(*this); } - - protected: - void Parse(unsigned char* buf, int len) override { - HandleScope handle_scope(env()->isolate()); - Context::Scope context_scope(env()->context()); - struct ares_txt_ext* txt_out; - - int status = ares_parse_txt_reply_ext(buf, len, &txt_out); - if (status != ARES_SUCCESS) { - ParseError(status); - return; - } + node::Utf8Value name(env->isolate(), string); + int err = wrap->Send(*name); + if (err) + delete wrap; - Local txt_records = Array::New(env()->isolate()); - Local txt_chunk; - - struct ares_txt_ext* current = txt_out; - uint32_t i = 0; - for (uint32_t j = 0; current != nullptr; current = current->next) { - Local txt = OneByteString(env()->isolate(), current->txt); - // New record found - write out the current chunk - if (current->record_start) { - if (!txt_chunk.IsEmpty()) - txt_records->Set(i++, txt_chunk); - txt_chunk = Array::New(env()->isolate()); - j = 0; - } - txt_chunk->Set(j++, txt); - } - // Push last chunk if it isn't empty - if (!txt_chunk.IsEmpty()) - txt_records->Set(i, txt_chunk); - - ares_free_data(txt_out); - - this->CallOnComplete(txt_records); - } -}; - - -class QuerySrvWrap: public QueryWrap { - public: - explicit QuerySrvWrap(Environment* env, Local req_wrap_obj) - : QueryWrap(env, req_wrap_obj) { - } - - int Send(const char* name) override { - ares_query(env()->cares_channel(), - name, - ns_c_in, - ns_t_srv, - Callback, - GetQueryArg()); - return 0; - } - - size_t self_size() const override { return sizeof(*this); } - - protected: - void Parse(unsigned char* buf, int len) override { - HandleScope handle_scope(env()->isolate()); - Context::Scope context_scope(env()->context()); - - struct ares_srv_reply* srv_start; - int status = ares_parse_srv_reply(buf, len, &srv_start); - if (status != ARES_SUCCESS) { - ParseError(status); - return; + args.GetReturnValue().Set(err); } - Local srv_records = Array::New(env()->isolate()); - Local name_symbol = env()->name_string(); - Local port_symbol = env()->port_string(); - Local priority_symbol = env()->priority_string(); - Local weight_symbol = env()->weight_string(); - - ares_srv_reply* current = srv_start; - for (uint32_t i = 0; current != nullptr; ++i, current = current->next) { - Local srv_record = Object::New(env()->isolate()); - srv_record->Set(name_symbol, - OneByteString(env()->isolate(), current->host)); - srv_record->Set(port_symbol, - Integer::New(env()->isolate(), current->port)); - srv_record->Set(priority_symbol, - Integer::New(env()->isolate(), current->priority)); - srv_record->Set(weight_symbol, - Integer::New(env()->isolate(), current->weight)); - srv_records->Set(i, srv_record); + void AfterGetAddrInfo(uv_getaddrinfo_t* req, int status, struct addrinfo* res) + { + GetAddrInfoReqWrap* req_wrap = static_cast(req->data); + Environment* env = req_wrap->env(); + + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); + + Local argv[] = { + Integer::New(env->isolate(), status), + Null(env->isolate()) + }; + + if (status == 0) { + // Success + struct addrinfo* address; + int n = 0; + + // Create the response array. + Local results = Array::New(env->isolate()); + + char ip[INET6_ADDRSTRLEN]; + const char* addr; + + // Iterate over the IPv4 responses again this time creating javascript + // strings for each IP and filling the results array. + address = res; + while (address) { + NODE_CHECK_EQ(address->ai_socktype, SOCK_STREAM); + + // Ignore random ai_family types. + if (address->ai_family == AF_INET) { + // Juggle pointers + addr = reinterpret_cast(&(reinterpret_cast( + address->ai_addr) + ->sin_addr)); + int err = uv_inet_ntop(address->ai_family, + addr, + ip, + INET6_ADDRSTRLEN); + if (err) + continue; + + // Create JavaScript string + Local s = OneByteString(env->isolate(), ip); + results->Set(n, s); + n++; + } + + // Increment + address = address->ai_next; + } + + // Iterate over the IPv6 responses putting them in the array. + address = res; + while (address) { + NODE_CHECK_EQ(address->ai_socktype, SOCK_STREAM); + + // Ignore random ai_family types. + if (address->ai_family == AF_INET6) { + // Juggle pointers + addr = reinterpret_cast(&(reinterpret_cast( + address->ai_addr) + ->sin6_addr)); + int err = uv_inet_ntop(address->ai_family, + addr, + ip, + INET6_ADDRSTRLEN); + if (err) + continue; + + // Create JavaScript string + Local s = OneByteString(env->isolate(), ip); + results->Set(n, s); + n++; + } + + // Increment + address = address->ai_next; + } + + // No responses were found to return + if (n == 0) { + argv[0] = Integer::New(env->isolate(), UV_EAI_NODATA); + } + + argv[1] = results; + } + + uv_freeaddrinfo(res); + + // Make the callback into JavaScript + req_wrap->MakeCallback(env->oncomplete_string(), arraysize(argv), argv); + + delete req_wrap; } - ares_free_data(srv_start); - - this->CallOnComplete(srv_records); - } -}; - -class QueryPtrWrap: public QueryWrap { - public: - explicit QueryPtrWrap(Environment* env, Local req_wrap_obj) - : QueryWrap(env, req_wrap_obj) { - } - - int Send(const char* name) override { - ares_query(env()->cares_channel(), - name, - ns_c_in, - ns_t_ptr, - Callback, - GetQueryArg()); - return 0; - } - - size_t self_size() const override { return sizeof(*this); } - - protected: - void Parse(unsigned char* buf, int len) override { - HandleScope handle_scope(env()->isolate()); - Context::Scope context_scope(env()->context()); - - struct hostent* host; - - int status = ares_parse_ptr_reply(buf, len, NULL, 0, AF_INET, &host); - if (status != ARES_SUCCESS) { - ParseError(status); - return; + void AfterGetNameInfo(uv_getnameinfo_t* req, + int status, + const char* hostname, + const char* service) + { + GetNameInfoReqWrap* req_wrap = static_cast(req->data); + Environment* env = req_wrap->env(); + + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); + + Local argv[] = { + Integer::New(env->isolate(), status), + Null(env->isolate()), + Null(env->isolate()) + }; + + if (status == 0) { + // Success + Local js_hostname = OneByteString(env->isolate(), hostname); + Local js_service = OneByteString(env->isolate(), service); + argv[1] = js_hostname; + argv[2] = js_service; + } + + // Make the callback into JavaScript + req_wrap->MakeCallback(env->oncomplete_string(), arraysize(argv), argv); + + delete req_wrap; } - Local aliases = Array::New(env()->isolate()); + static void IsIP(const FunctionCallbackInfo& args) + { + node::Utf8Value ip(args.GetIsolate(), args[0]); + char address_buffer[sizeof(struct in6_addr)]; - for (uint32_t i = 0; host->h_aliases[i] != NULL; i++) { - aliases->Set(i, OneByteString(env()->isolate(), host->h_aliases[i])); - } - - ares_free_hostent(host); - - this->CallOnComplete(aliases); - } -}; - -class QueryNaptrWrap: public QueryWrap { - public: - explicit QueryNaptrWrap(Environment* env, Local req_wrap_obj) - : QueryWrap(env, req_wrap_obj) { - } - - int Send(const char* name) override { - ares_query(env()->cares_channel(), - name, - ns_c_in, - ns_t_naptr, - Callback, - GetQueryArg()); - return 0; - } - - size_t self_size() const override { return sizeof(*this); } - - protected: - void Parse(unsigned char* buf, int len) override { - HandleScope handle_scope(env()->isolate()); - Context::Scope context_scope(env()->context()); - - ares_naptr_reply* naptr_start; - int status = ares_parse_naptr_reply(buf, len, &naptr_start); - - if (status != ARES_SUCCESS) { - ParseError(status); - return; - } + int rc = 0; + if (uv_inet_pton(AF_INET, *ip, &address_buffer) == 0) + rc = 4; + else if (uv_inet_pton(AF_INET6, *ip, &address_buffer) == 0) + rc = 6; - Local naptr_records = Array::New(env()->isolate()); - Local flags_symbol = env()->flags_string(); - Local service_symbol = env()->service_string(); - Local regexp_symbol = env()->regexp_string(); - Local replacement_symbol = env()->replacement_string(); - Local order_symbol = env()->order_string(); - Local preference_symbol = env()->preference_string(); - - ares_naptr_reply* current = naptr_start; - for (uint32_t i = 0; current != nullptr; ++i, current = current->next) { - Local naptr_record = Object::New(env()->isolate()); - naptr_record->Set(flags_symbol, - OneByteString(env()->isolate(), current->flags)); - naptr_record->Set(service_symbol, - OneByteString(env()->isolate(), current->service)); - naptr_record->Set(regexp_symbol, - OneByteString(env()->isolate(), current->regexp)); - naptr_record->Set(replacement_symbol, - OneByteString(env()->isolate(), current->replacement)); - naptr_record->Set(order_symbol, - Integer::New(env()->isolate(), current->order)); - naptr_record->Set(preference_symbol, - Integer::New(env()->isolate(), current->preference)); - naptr_records->Set(i, naptr_record); + args.GetReturnValue().Set(rc); } - ares_free_data(naptr_start); - - this->CallOnComplete(naptr_records); - } -}; - + static void IsIPv4(const FunctionCallbackInfo& args) + { + node::Utf8Value ip(args.GetIsolate(), args[0]); + char address_buffer[sizeof(struct in_addr)]; -class QuerySoaWrap: public QueryWrap { - public: - QuerySoaWrap(Environment* env, Local req_wrap_obj) - : QueryWrap(env, req_wrap_obj) { - } - - int Send(const char* name) override { - ares_query(env()->cares_channel(), - name, - ns_c_in, - ns_t_soa, - Callback, - GetQueryArg()); - return 0; - } - - size_t self_size() const override { return sizeof(*this); } - - protected: - void Parse(unsigned char* buf, int len) override { - HandleScope handle_scope(env()->isolate()); - Context::Scope context_scope(env()->context()); - - ares_soa_reply* soa_out; - int status = ares_parse_soa_reply(buf, len, &soa_out); - - if (status != ARES_SUCCESS) { - ParseError(status); - return; + if (uv_inet_pton(AF_INET, *ip, &address_buffer) == 0) { + args.GetReturnValue().Set(true); + } else { + args.GetReturnValue().Set(false); + } } - Local soa_record = Object::New(env()->isolate()); - - soa_record->Set(env()->nsname_string(), - OneByteString(env()->isolate(), soa_out->nsname)); - soa_record->Set(env()->hostmaster_string(), - OneByteString(env()->isolate(), soa_out->hostmaster)); - soa_record->Set(env()->serial_string(), - Integer::New(env()->isolate(), soa_out->serial)); - soa_record->Set(env()->refresh_string(), - Integer::New(env()->isolate(), soa_out->refresh)); - soa_record->Set(env()->retry_string(), - Integer::New(env()->isolate(), soa_out->retry)); - soa_record->Set(env()->expire_string(), - Integer::New(env()->isolate(), soa_out->expire)); - soa_record->Set(env()->minttl_string(), - Integer::New(env()->isolate(), soa_out->minttl)); - - ares_free_data(soa_out); - - this->CallOnComplete(soa_record); - } -}; - - -class GetHostByAddrWrap: public QueryWrap { - public: - explicit GetHostByAddrWrap(Environment* env, Local req_wrap_obj) - : QueryWrap(env, req_wrap_obj) { - } - - int Send(const char* name) override { - int length, family; - char address_buffer[sizeof(struct in6_addr)]; - - if (uv_inet_pton(AF_INET, name, &address_buffer) == 0) { - length = sizeof(struct in_addr); - family = AF_INET; - } else if (uv_inet_pton(AF_INET6, name, &address_buffer) == 0) { - length = sizeof(struct in6_addr); - family = AF_INET6; - } else { - return UV_EINVAL; // So errnoException() reports a proper error. - } - - ares_gethostbyaddr(env()->cares_channel(), - address_buffer, - length, - family, - Callback, - GetQueryArg()); - return 0; - } - - size_t self_size() const override { return sizeof(*this); } - - protected: - void Parse(struct hostent* host) override { - HandleScope handle_scope(env()->isolate()); - Context::Scope context_scope(env()->context()); - this->CallOnComplete(HostentToNames(env(), host)); - } -}; - - -class GetHostByNameWrap: public QueryWrap { - public: - explicit GetHostByNameWrap(Environment* env, Local req_wrap_obj) - : QueryWrap(env, req_wrap_obj) { - } - - int Send(const char* name, int family) override { - ares_gethostbyname(env()->cares_channel(), - name, - family, - Callback, - GetQueryArg()); - return 0; - } - - protected: - void Parse(struct hostent* host) override { - HandleScope scope(env()->isolate()); - - Local addresses = HostentToAddresses(env(), host); - Local family = Integer::New(env()->isolate(), host->h_addrtype); - - this->CallOnComplete(addresses, family); - } -}; - - -template -static void Query(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - - CHECK_EQ(false, args.IsConstructCall()); - CHECK(args[0]->IsObject()); - CHECK(args[1]->IsString()); - - Local req_wrap_obj = args[0].As(); - Local string = args[1].As(); - Wrap* wrap = new Wrap(env, req_wrap_obj); - - node::Utf8Value name(env->isolate(), string); - int err = wrap->Send(*name); - if (err) - delete wrap; - - args.GetReturnValue().Set(err); -} - - -void AfterGetAddrInfo(uv_getaddrinfo_t* req, int status, struct addrinfo* res) { - GetAddrInfoReqWrap* req_wrap = static_cast(req->data); - Environment* env = req_wrap->env(); - - HandleScope handle_scope(env->isolate()); - Context::Scope context_scope(env->context()); - - Local argv[] = { - Integer::New(env->isolate(), status), - Null(env->isolate()) - }; - - if (status == 0) { - // Success - struct addrinfo *address; - int n = 0; - - // Create the response array. - Local results = Array::New(env->isolate()); - - char ip[INET6_ADDRSTRLEN]; - const char *addr; - - // Iterate over the IPv4 responses again this time creating javascript - // strings for each IP and filling the results array. - address = res; - while (address) { - CHECK_EQ(address->ai_socktype, SOCK_STREAM); - - // Ignore random ai_family types. - if (address->ai_family == AF_INET) { - // Juggle pointers - addr = reinterpret_cast(&(reinterpret_cast( - address->ai_addr)->sin_addr)); - int err = uv_inet_ntop(address->ai_family, - addr, - ip, - INET6_ADDRSTRLEN); - if (err) - continue; + static void IsIPv6(const FunctionCallbackInfo& args) + { + node::Utf8Value ip(args.GetIsolate(), args[0]); + char address_buffer[sizeof(struct in6_addr)]; - // Create JavaScript string - Local s = OneByteString(env->isolate(), ip); - results->Set(n, s); - n++; - } - - // Increment - address = address->ai_next; + if (uv_inet_pton(AF_INET6, *ip, &address_buffer) == 0) { + args.GetReturnValue().Set(true); + } else { + args.GetReturnValue().Set(false); + } } - // Iterate over the IPv6 responses putting them in the array. - address = res; - while (address) { - CHECK_EQ(address->ai_socktype, SOCK_STREAM); - - // Ignore random ai_family types. - if (address->ai_family == AF_INET6) { - // Juggle pointers - addr = reinterpret_cast(&(reinterpret_cast( - address->ai_addr)->sin6_addr)); - int err = uv_inet_ntop(address->ai_family, - addr, - ip, - INET6_ADDRSTRLEN); + static void GetAddrInfo(const FunctionCallbackInfo& args) + { + Environment* env = Environment::GetCurrent(args); + + NODE_CHECK(args[0]->IsObject()); + NODE_CHECK(args[1]->IsString()); + NODE_CHECK(args[2]->IsInt32()); + Local req_wrap_obj = args[0].As(); + node::Utf8Value hostname(env->isolate(), args[1]); + + int32_t flags = (args[3]->IsInt32()) ? args[3]->Int32Value() : 0; + int family; + + switch (args[2]->Int32Value()) { + case 0: + family = AF_UNSPEC; + break; + case 4: + family = AF_INET; + break; + case 6: + family = AF_INET6; + break; + default: + NODE_CHECK(0 && "bad address family"); + } + + GetAddrInfoReqWrap* req_wrap = new GetAddrInfoReqWrap(env, req_wrap_obj); + + struct addrinfo hints; + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = family; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = flags; + + int err = uv_getaddrinfo(env->event_loop(), + req_wrap->req(), + AfterGetAddrInfo, + *hostname, + nullptr, + &hints); + req_wrap->Dispatched(); if (err) - continue; - - // Create JavaScript string - Local s = OneByteString(env->isolate(), ip); - results->Set(n, s); - n++; - } + delete req_wrap; - // Increment - address = address->ai_next; + args.GetReturnValue().Set(err); } - // No responses were found to return - if (n == 0) { - argv[0] = Integer::New(env->isolate(), UV_EAI_NODATA); - } - - argv[1] = results; - } - - uv_freeaddrinfo(res); - - // Make the callback into JavaScript - req_wrap->MakeCallback(env->oncomplete_string(), arraysize(argv), argv); - - delete req_wrap; -} - - -void AfterGetNameInfo(uv_getnameinfo_t* req, - int status, - const char* hostname, - const char* service) { - GetNameInfoReqWrap* req_wrap = static_cast(req->data); - Environment* env = req_wrap->env(); - - HandleScope handle_scope(env->isolate()); - Context::Scope context_scope(env->context()); - - Local argv[] = { - Integer::New(env->isolate(), status), - Null(env->isolate()), - Null(env->isolate()) - }; - - if (status == 0) { - // Success - Local js_hostname = OneByteString(env->isolate(), hostname); - Local js_service = OneByteString(env->isolate(), service); - argv[1] = js_hostname; - argv[2] = js_service; - } - - // Make the callback into JavaScript - req_wrap->MakeCallback(env->oncomplete_string(), arraysize(argv), argv); - - delete req_wrap; -} - - -static void IsIP(const FunctionCallbackInfo& args) { - node::Utf8Value ip(args.GetIsolate(), args[0]); - char address_buffer[sizeof(struct in6_addr)]; - - int rc = 0; - if (uv_inet_pton(AF_INET, *ip, &address_buffer) == 0) - rc = 4; - else if (uv_inet_pton(AF_INET6, *ip, &address_buffer) == 0) - rc = 6; - - args.GetReturnValue().Set(rc); -} - -static void IsIPv4(const FunctionCallbackInfo& args) { - node::Utf8Value ip(args.GetIsolate(), args[0]); - char address_buffer[sizeof(struct in_addr)]; - - if (uv_inet_pton(AF_INET, *ip, &address_buffer) == 0) { - args.GetReturnValue().Set(true); - } else { - args.GetReturnValue().Set(false); - } -} - -static void IsIPv6(const FunctionCallbackInfo& args) { - node::Utf8Value ip(args.GetIsolate(), args[0]); - char address_buffer[sizeof(struct in6_addr)]; - - if (uv_inet_pton(AF_INET6, *ip, &address_buffer) == 0) { - args.GetReturnValue().Set(true); - } else { - args.GetReturnValue().Set(false); - } -} - -static void GetAddrInfo(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); + static void GetNameInfo(const FunctionCallbackInfo& args) + { + Environment* env = Environment::GetCurrent(args); - CHECK(args[0]->IsObject()); - CHECK(args[1]->IsString()); - CHECK(args[2]->IsInt32()); - Local req_wrap_obj = args[0].As(); - node::Utf8Value hostname(env->isolate(), args[1]); + NODE_CHECK(args[0]->IsObject()); + NODE_CHECK(args[1]->IsString()); + NODE_CHECK(args[2]->IsUint32()); + Local req_wrap_obj = args[0].As(); + node::Utf8Value ip(env->isolate(), args[1]); + const unsigned port = args[2]->Uint32Value(); + struct sockaddr_storage addr; - int32_t flags = (args[3]->IsInt32()) ? args[3]->Int32Value() : 0; - int family; + NODE_CHECK(uv_ip4_addr(*ip, port, reinterpret_cast(&addr)) == 0 || uv_ip6_addr(*ip, port, reinterpret_cast(&addr)) == 0); - switch (args[2]->Int32Value()) { - case 0: - family = AF_UNSPEC; - break; - case 4: - family = AF_INET; - break; - case 6: - family = AF_INET6; - break; - default: - CHECK(0 && "bad address family"); - } + GetNameInfoReqWrap* req_wrap = new GetNameInfoReqWrap(env, req_wrap_obj); - GetAddrInfoReqWrap* req_wrap = new GetAddrInfoReqWrap(env, req_wrap_obj); + int err = uv_getnameinfo(env->event_loop(), + req_wrap->req(), + AfterGetNameInfo, + (struct sockaddr*)&addr, + NI_NAMEREQD); + req_wrap->Dispatched(); + if (err) + delete req_wrap; - struct addrinfo hints; - memset(&hints, 0, sizeof(struct addrinfo)); - hints.ai_family = family; - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = flags; + args.GetReturnValue().Set(err); + } - int err = uv_getaddrinfo(env->event_loop(), - req_wrap->req(), - AfterGetAddrInfo, - *hostname, - nullptr, - &hints); - req_wrap->Dispatched(); - if (err) - delete req_wrap; + static void GetServers(const FunctionCallbackInfo& args) + { + Environment* env = Environment::GetCurrent(args); - args.GetReturnValue().Set(err); -} + Local server_array = Array::New(env->isolate()); + ares_addr_node* servers; -static void GetNameInfo(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); + int r = ares_get_servers(env->cares_channel(), &servers); + NODE_CHECK_EQ(r, ARES_SUCCESS); - CHECK(args[0]->IsObject()); - CHECK(args[1]->IsString()); - CHECK(args[2]->IsUint32()); - Local req_wrap_obj = args[0].As(); - node::Utf8Value ip(env->isolate(), args[1]); - const unsigned port = args[2]->Uint32Value(); - struct sockaddr_storage addr; + ares_addr_node* cur = servers; - CHECK(uv_ip4_addr(*ip, port, reinterpret_cast(&addr)) == 0 || - uv_ip6_addr(*ip, port, reinterpret_cast(&addr)) == 0); + for (uint32_t i = 0; cur != nullptr; ++i, cur = cur->next) { + char ip[INET6_ADDRSTRLEN]; - GetNameInfoReqWrap* req_wrap = new GetNameInfoReqWrap(env, req_wrap_obj); + const void* caddr = static_cast(&cur->addr); + int err = uv_inet_ntop(cur->family, caddr, ip, sizeof(ip)); + NODE_CHECK_EQ(err, 0); - int err = uv_getnameinfo(env->event_loop(), - req_wrap->req(), - AfterGetNameInfo, - (struct sockaddr*)&addr, - NI_NAMEREQD); - req_wrap->Dispatched(); - if (err) - delete req_wrap; + Local addr = OneByteString(env->isolate(), ip); + server_array->Set(i, addr); + } - args.GetReturnValue().Set(err); -} + ares_free_data(servers); + args.GetReturnValue().Set(server_array); + } -static void GetServers(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); + static void SetServers(const FunctionCallbackInfo& args) + { + Environment* env = Environment::GetCurrent(args); - Local server_array = Array::New(env->isolate()); + NODE_CHECK(args[0]->IsArray()); - ares_addr_node* servers; + Local arr = Local::Cast(args[0]); - int r = ares_get_servers(env->cares_channel(), &servers); - CHECK_EQ(r, ARES_SUCCESS); + uint32_t len = arr->Length(); - ares_addr_node* cur = servers; + if (len == 0) { + int rv = ares_set_servers(env->cares_channel(), nullptr); + return args.GetReturnValue().Set(rv); + } - for (uint32_t i = 0; cur != nullptr; ++i, cur = cur->next) { - char ip[INET6_ADDRSTRLEN]; + ares_addr_node* servers = new ares_addr_node[len]; + ares_addr_node* last = nullptr; - const void* caddr = static_cast(&cur->addr); - int err = uv_inet_ntop(cur->family, caddr, ip, sizeof(ip)); - CHECK_EQ(err, 0); + int err; - Local addr = OneByteString(env->isolate(), ip); - server_array->Set(i, addr); - } + for (uint32_t i = 0; i < len; i++) { + NODE_CHECK(arr->Get(i)->IsArray()); - ares_free_data(servers); + Local elm = Local::Cast(arr->Get(i)); - args.GetReturnValue().Set(server_array); -} + NODE_CHECK(elm->Get(0)->Int32Value()); + NODE_CHECK(elm->Get(1)->IsString()); + int fam = elm->Get(0)->Int32Value(); + node::Utf8Value ip(env->isolate(), elm->Get(1)); -static void SetServers(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); + ares_addr_node* cur = &servers[i]; - CHECK(args[0]->IsArray()); + switch (fam) { + case 4: + cur->family = AF_INET; + err = uv_inet_pton(AF_INET, *ip, &cur->addr); + break; + case 6: + cur->family = AF_INET6; + err = uv_inet_pton(AF_INET6, *ip, &cur->addr); + break; + default: + NODE_CHECK(0 && "Bad address family."); + } - Local arr = Local::Cast(args[0]); + if (err) + break; - uint32_t len = arr->Length(); + cur->next = nullptr; - if (len == 0) { - int rv = ares_set_servers(env->cares_channel(), nullptr); - return args.GetReturnValue().Set(rv); - } + if (last != nullptr) + last->next = cur; - ares_addr_node* servers = new ares_addr_node[len]; - ares_addr_node* last = nullptr; + last = cur; + } - int err; + if (err == 0) + err = ares_set_servers(env->cares_channel(), &servers[0]); + else + err = ARES_EBADSTR; - for (uint32_t i = 0; i < len; i++) { - CHECK(arr->Get(i)->IsArray()); + delete[] servers; - Local elm = Local::Cast(arr->Get(i)); + args.GetReturnValue().Set(err); + } - CHECK(elm->Get(0)->Int32Value()); - CHECK(elm->Get(1)->IsString()); + static void StrError(const FunctionCallbackInfo& args) + { + Environment* env = Environment::GetCurrent(args); + const char* errmsg = ares_strerror(args[0]->Int32Value()); + args.GetReturnValue().Set(OneByteString(env->isolate(), errmsg)); + } - int fam = elm->Get(0)->Int32Value(); - node::Utf8Value ip(env->isolate(), elm->Get(1)); + static void CaresTimerCloseCb(uv_handle_t* handle) + { + Environment* env = Environment::from_cares_timer_handle( + reinterpret_cast(handle)); + env->FinishHandleCleanup(handle); + } - ares_addr_node* cur = &servers[i]; + static void CaresTimerClose(Environment* env, + uv_handle_t* handle, + void* arg) + { + uv_close(handle, CaresTimerCloseCb); + } - switch (fam) { - case 4: - cur->family = AF_INET; - err = uv_inet_pton(AF_INET, *ip, &cur->addr); - break; - case 6: - cur->family = AF_INET6; - err = uv_inet_pton(AF_INET6, *ip, &cur->addr); - break; - default: - CHECK(0 && "Bad address family."); + static void Initialize(Local target, + Local unused, + Local context) + { + Environment* env = Environment::GetCurrent(context); + + int r = ares_library_init(ARES_LIB_INIT_ALL); + if (r != ARES_SUCCESS) + return env->ThrowError(ToErrorCodeString(r)); + + struct ares_options options; + memset(&options, 0, sizeof(options)); + options.flags = ARES_FLAG_NOCHECKRESP; + options.sock_state_cb = ares_sockstate_cb; + options.sock_state_cb_data = env; + + /* We do the call to ares_init_option for caller. */ + r = ares_init_options(env->cares_channel_ptr(), + &options, + ARES_OPT_FLAGS | ARES_OPT_SOCK_STATE_CB); + if (r != ARES_SUCCESS) { + ares_library_cleanup(); + return env->ThrowError(ToErrorCodeString(r)); + } + + /* Initialize the timeout timer. The timer won't be started until the */ + /* first socket is opened. */ + uv_timer_init(env->event_loop(), env->cares_timer_handle()); + env->RegisterHandleCleanup( + reinterpret_cast(env->cares_timer_handle()), + CaresTimerClose, + nullptr); + + env->SetMethod(target, "queryA", Query); + env->SetMethod(target, "queryAaaa", Query); + env->SetMethod(target, "queryCname", Query); + env->SetMethod(target, "queryMx", Query); + env->SetMethod(target, "queryNs", Query); + env->SetMethod(target, "queryTxt", Query); + env->SetMethod(target, "querySrv", Query); + env->SetMethod(target, "queryPtr", Query); + env->SetMethod(target, "queryNaptr", Query); + env->SetMethod(target, "querySoa", Query); + env->SetMethod(target, "getHostByAddr", Query); + + env->SetMethod(target, "getaddrinfo", GetAddrInfo); + env->SetMethod(target, "getnameinfo", GetNameInfo); + env->SetMethod(target, "isIP", IsIP); + env->SetMethod(target, "isIPv4", IsIPv4); + env->SetMethod(target, "isIPv6", IsIPv6); + + env->SetMethod(target, "strerror", StrError); + env->SetMethod(target, "getServers", GetServers); + env->SetMethod(target, "setServers", SetServers); + + target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "AF_INET"), + Integer::New(env->isolate(), AF_INET)); + target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "AF_INET6"), + Integer::New(env->isolate(), AF_INET6)); + target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "AF_UNSPEC"), + Integer::New(env->isolate(), AF_UNSPEC)); + target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "AI_ADDRCONFIG"), + Integer::New(env->isolate(), AI_ADDRCONFIG)); + target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "AI_V4MAPPED"), + Integer::New(env->isolate(), AI_V4MAPPED)); + + Local aiw = FunctionTemplate::New(env->isolate(), NewGetAddrInfoReqWrap); + aiw->InstanceTemplate()->SetInternalFieldCount(1); + aiw->SetClassName( + FIXED_ONE_BYTE_STRING(env->isolate(), "GetAddrInfoReqWrap")); + target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "GetAddrInfoReqWrap"), + aiw->GetFunction()); + + Local niw = FunctionTemplate::New(env->isolate(), NewGetNameInfoReqWrap); + niw->InstanceTemplate()->SetInternalFieldCount(1); + niw->SetClassName( + FIXED_ONE_BYTE_STRING(env->isolate(), "GetNameInfoReqWrap")); + target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "GetNameInfoReqWrap"), + niw->GetFunction()); + + Local qrw = FunctionTemplate::New(env->isolate(), NewQueryReqWrap); + qrw->InstanceTemplate()->SetInternalFieldCount(1); + qrw->SetClassName( + FIXED_ONE_BYTE_STRING(env->isolate(), "QueryReqWrap")); + target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "QueryReqWrap"), + qrw->GetFunction()); } - if (err) - break; - - cur->next = nullptr; - - if (last != nullptr) - last->next = cur; - - last = cur; - } - - if (err == 0) - err = ares_set_servers(env->cares_channel(), &servers[0]); - else - err = ARES_EBADSTR; - - delete[] servers; - - args.GetReturnValue().Set(err); -} - - -static void StrError(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - const char* errmsg = ares_strerror(args[0]->Int32Value()); - args.GetReturnValue().Set(OneByteString(env->isolate(), errmsg)); -} - - -static void CaresTimerCloseCb(uv_handle_t* handle) { - Environment* env = Environment::from_cares_timer_handle( - reinterpret_cast(handle)); - env->FinishHandleCleanup(handle); -} - - -static void CaresTimerClose(Environment* env, - uv_handle_t* handle, - void* arg) { - uv_close(handle, CaresTimerCloseCb); -} - - -static void Initialize(Local target, - Local unused, - Local context) { - Environment* env = Environment::GetCurrent(context); - - int r = ares_library_init(ARES_LIB_INIT_ALL); - if (r != ARES_SUCCESS) - return env->ThrowError(ToErrorCodeString(r)); - - struct ares_options options; - memset(&options, 0, sizeof(options)); - options.flags = ARES_FLAG_NOCHECKRESP; - options.sock_state_cb = ares_sockstate_cb; - options.sock_state_cb_data = env; - - /* We do the call to ares_init_option for caller. */ - r = ares_init_options(env->cares_channel_ptr(), - &options, - ARES_OPT_FLAGS | ARES_OPT_SOCK_STATE_CB); - if (r != ARES_SUCCESS) { - ares_library_cleanup(); - return env->ThrowError(ToErrorCodeString(r)); - } - - /* Initialize the timeout timer. The timer won't be started until the */ - /* first socket is opened. */ - uv_timer_init(env->event_loop(), env->cares_timer_handle()); - env->RegisterHandleCleanup( - reinterpret_cast(env->cares_timer_handle()), - CaresTimerClose, - nullptr); - - env->SetMethod(target, "queryA", Query); - env->SetMethod(target, "queryAaaa", Query); - env->SetMethod(target, "queryCname", Query); - env->SetMethod(target, "queryMx", Query); - env->SetMethod(target, "queryNs", Query); - env->SetMethod(target, "queryTxt", Query); - env->SetMethod(target, "querySrv", Query); - env->SetMethod(target, "queryPtr", Query); - env->SetMethod(target, "queryNaptr", Query); - env->SetMethod(target, "querySoa", Query); - env->SetMethod(target, "getHostByAddr", Query); - - env->SetMethod(target, "getaddrinfo", GetAddrInfo); - env->SetMethod(target, "getnameinfo", GetNameInfo); - env->SetMethod(target, "isIP", IsIP); - env->SetMethod(target, "isIPv4", IsIPv4); - env->SetMethod(target, "isIPv6", IsIPv6); - - env->SetMethod(target, "strerror", StrError); - env->SetMethod(target, "getServers", GetServers); - env->SetMethod(target, "setServers", SetServers); - - target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "AF_INET"), - Integer::New(env->isolate(), AF_INET)); - target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "AF_INET6"), - Integer::New(env->isolate(), AF_INET6)); - target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "AF_UNSPEC"), - Integer::New(env->isolate(), AF_UNSPEC)); - target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "AI_ADDRCONFIG"), - Integer::New(env->isolate(), AI_ADDRCONFIG)); - target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "AI_V4MAPPED"), - Integer::New(env->isolate(), AI_V4MAPPED)); - - Local aiw = - FunctionTemplate::New(env->isolate(), NewGetAddrInfoReqWrap); - aiw->InstanceTemplate()->SetInternalFieldCount(1); - aiw->SetClassName( - FIXED_ONE_BYTE_STRING(env->isolate(), "GetAddrInfoReqWrap")); - target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "GetAddrInfoReqWrap"), - aiw->GetFunction()); - - Local niw = - FunctionTemplate::New(env->isolate(), NewGetNameInfoReqWrap); - niw->InstanceTemplate()->SetInternalFieldCount(1); - niw->SetClassName( - FIXED_ONE_BYTE_STRING(env->isolate(), "GetNameInfoReqWrap")); - target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "GetNameInfoReqWrap"), - niw->GetFunction()); - - Local qrw = - FunctionTemplate::New(env->isolate(), NewQueryReqWrap); - qrw->InstanceTemplate()->SetInternalFieldCount(1); - qrw->SetClassName( - FIXED_ONE_BYTE_STRING(env->isolate(), "QueryReqWrap")); - target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "QueryReqWrap"), - qrw->GetFunction()); -} - -} // namespace cares_wrap -} // namespace node +} // namespace cares_wrap +} // namespace node NODE_MODULE_CONTEXT_AWARE_BUILTIN(cares_wrap, node::cares_wrap::Initialize) diff --git a/node/src/connect_wrap.cc b/node/src/connect_wrap.cc index df3f093e73..06b82d5430 100644 --- a/node/src/connect_wrap.cc +++ b/node/src/connect_wrap.cc @@ -12,11 +12,12 @@ namespace node { using v8::Local; using v8::Object; - ConnectWrap::ConnectWrap(Environment* env, Local req_wrap_obj, - AsyncWrap::ProviderType provider) : ReqWrap(env, req_wrap_obj, provider) { - Wrap(req_wrap_obj, this); + AsyncWrap::ProviderType provider) + : ReqWrap(env, req_wrap_obj, provider) +{ + Wrap(req_wrap_obj, this); } -} // namespace node +} // namespace node diff --git a/node/src/connect_wrap.h b/node/src/connect_wrap.h index 28d4872d7e..f1c1d885e9 100644 --- a/node/src/connect_wrap.h +++ b/node/src/connect_wrap.h @@ -11,16 +11,16 @@ namespace node { class ConnectWrap : public ReqWrap { - public: - ConnectWrap(Environment* env, - v8::Local req_wrap_obj, - AsyncWrap::ProviderType provider); +public: + ConnectWrap(Environment* env, + v8::Local req_wrap_obj, + AsyncWrap::ProviderType provider); - size_t self_size() const override { return sizeof(*this); } + size_t self_size() const override { return sizeof(*this); } }; -} // namespace node +} // namespace node -#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS +#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS -#endif // SRC_CONNECT_WRAP_H_ +#endif // SRC_CONNECT_WRAP_H_ diff --git a/node/src/connection_wrap.cc b/node/src/connection_wrap.cc index 41571946b2..da87a7d632 100644 --- a/node/src/connection_wrap.cc +++ b/node/src/connection_wrap.cc @@ -19,98 +19,98 @@ using v8::Local; using v8::Object; using v8::Value; - template ConnectionWrap::ConnectionWrap(Environment* env, - Local object, - ProviderType provider, - AsyncWrap* parent) + Local object, + ProviderType provider, + AsyncWrap* parent) : StreamWrap(env, - object, - reinterpret_cast(&handle_), - provider, - parent) {} - + object, + reinterpret_cast(&handle_), + provider, + parent) +{ +} template void ConnectionWrap::OnConnection(uv_stream_t* handle, - int status) { - WrapType* wrap_data = static_cast(handle->data); - CHECK_NE(wrap_data, nullptr); - CHECK_EQ(&wrap_data->handle_, reinterpret_cast(handle)); - - Environment* env = wrap_data->env(); - HandleScope handle_scope(env->isolate()); - Context::Scope context_scope(env->context()); - - // We should not be getting this callback if someone has already called - // uv_close() on the handle. - CHECK_EQ(wrap_data->persistent().IsEmpty(), false); - - Local argv[] = { - Integer::New(env->isolate(), status), - Undefined(env->isolate()) - }; - - if (status == 0) { - // Instantiate the client javascript object and handle. - Local client_obj = WrapType::Instantiate(env, wrap_data); - - // Unwrap the client javascript object. - WrapType* wrap; - ASSIGN_OR_RETURN_UNWRAP(&wrap, client_obj); - uv_stream_t* client_handle = - reinterpret_cast(&wrap->handle_); - // uv_accept can fail if the new connection has already been closed, in - // which case an EAGAIN (resource temporarily unavailable) will be - // returned. - if (uv_accept(handle, client_handle)) - return; - - // Successful accept. Call the onconnection callback in JavaScript land. - argv[1] = client_obj; - } - wrap_data->MakeCallback(env->onconnection_string(), arraysize(argv), argv); + int status) +{ + WrapType* wrap_data = static_cast(handle->data); + NODE_CHECK_NE(wrap_data, nullptr); + NODE_CHECK_EQ(&wrap_data->handle_, reinterpret_cast(handle)); + + Environment* env = wrap_data->env(); + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); + + // We should not be getting this callback if someone has already called + // uv_close() on the handle. + NODE_CHECK_EQ(wrap_data->persistent().IsEmpty(), false); + + Local argv[] = { + Integer::New(env->isolate(), status), + Undefined(env->isolate()) + }; + + if (status == 0) { + // Instantiate the client javascript object and handle. + Local client_obj = WrapType::Instantiate(env, wrap_data); + + // Unwrap the client javascript object. + WrapType* wrap; + ASSIGN_OR_RETURN_UNWRAP(&wrap, client_obj); + uv_stream_t* client_handle = reinterpret_cast(&wrap->handle_); + // uv_accept can fail if the new connection has already been closed, in + // which case an EAGAIN (resource temporarily unavailable) will be + // returned. + if (uv_accept(handle, client_handle)) + return; + + // Successful accept. Call the onconnection callback in JavaScript land. + argv[1] = client_obj; + } + wrap_data->MakeCallback(env->onconnection_string(), arraysize(argv), argv); } - template void ConnectionWrap::AfterConnect(uv_connect_t* req, - int status) { - ConnectWrap* req_wrap = static_cast(req->data); - CHECK_NE(req_wrap, nullptr); - WrapType* wrap = static_cast(req->handle->data); - CHECK_EQ(req_wrap->env(), wrap->env()); - Environment* env = wrap->env(); - - HandleScope handle_scope(env->isolate()); - Context::Scope context_scope(env->context()); - - // The wrap and request objects should still be there. - CHECK_EQ(req_wrap->persistent().IsEmpty(), false); - CHECK_EQ(wrap->persistent().IsEmpty(), false); - - bool readable, writable; - - if (status) { - readable = writable = 0; - } else { - readable = uv_is_readable(req->handle) != 0; - writable = uv_is_writable(req->handle) != 0; - } - - Local req_wrap_obj = req_wrap->object(); - Local argv[5] = { - Integer::New(env->isolate(), status), - wrap->object(), - req_wrap_obj, - Boolean::New(env->isolate(), readable), - Boolean::New(env->isolate(), writable) - }; - - req_wrap->MakeCallback(env->oncomplete_string(), arraysize(argv), argv); - - delete req_wrap; + int status) +{ + ConnectWrap* req_wrap = static_cast(req->data); + NODE_CHECK_NE(req_wrap, nullptr); + WrapType* wrap = static_cast(req->handle->data); + NODE_CHECK_EQ(req_wrap->env(), wrap->env()); + Environment* env = wrap->env(); + + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); + + // The wrap and request objects should still be there. + NODE_CHECK_EQ(req_wrap->persistent().IsEmpty(), false); + NODE_CHECK_EQ(wrap->persistent().IsEmpty(), false); + + bool readable, writable; + + if (status) { + readable = writable = 0; + } else { + readable = uv_is_readable(req->handle) != 0; + writable = uv_is_writable(req->handle) != 0; + } + + Local req_wrap_obj = req_wrap->object(); + Local argv[5] = { + Integer::New(env->isolate(), status), + wrap->object(), + req_wrap_obj, + Boolean::New(env->isolate(), readable), + Boolean::New(env->isolate(), writable) + }; + + req_wrap->MakeCallback(env->oncomplete_string(), arraysize(argv), argv); + + delete req_wrap; } template ConnectionWrap::ConnectionWrap( @@ -137,5 +137,4 @@ template void ConnectionWrap::AfterConnect( template void ConnectionWrap::AfterConnect( uv_connect_t* handle, int status); - -} // namespace node +} // namespace node diff --git a/node/src/connection_wrap.h b/node/src/connection_wrap.h index 7af97fd3f0..38b2b0701c 100644 --- a/node/src/connection_wrap.h +++ b/node/src/connection_wrap.h @@ -11,28 +11,29 @@ namespace node { template class ConnectionWrap : public StreamWrap { - public: - UVType* UVHandle() { - return &handle_; - } - - static void OnConnection(uv_stream_t* handle, int status); - static void AfterConnect(uv_connect_t* req, int status); - - protected: - ConnectionWrap(Environment* env, - v8::Local object, - ProviderType provider, - AsyncWrap* parent); - ~ConnectionWrap() { - } - - UVType handle_; +public: + UVType* UVHandle() + { + return &handle_; + } + + static void OnConnection(uv_stream_t* handle, int status); + static void AfterConnect(uv_connect_t* req, int status); + +protected: + ConnectionWrap(Environment* env, + v8::Local object, + ProviderType provider, + AsyncWrap* parent); + ~ConnectionWrap() + { + } + + UVType handle_; }; +} // namespace node -} // namespace node +#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS -#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS - -#endif // SRC_CONNECTION_WRAP_H_ +#endif // SRC_CONNECTION_WRAP_H_ diff --git a/node/src/debug-agent.cc b/node/src/debug-agent.cc index c5ff040146..eb1c5e4aec 100644 --- a/node/src/debug-agent.cc +++ b/node/src/debug-agent.cc @@ -22,7 +22,7 @@ #include "debug-agent.h" #include "node.h" -#include "node_internals.h" // arraysize +#include "node_internals.h" // arraysize #include "env.h" #include "env-inl.h" #include "v8.h" @@ -35,314 +35,316 @@ namespace node { namespace debugger { -using v8::Context; -using v8::FunctionCallbackInfo; -using v8::FunctionTemplate; -using v8::HandleScope; -using v8::Integer; -using v8::Isolate; -using v8::Local; -using v8::Locker; -using v8::NewStringType; -using v8::Object; -using v8::String; -using v8::Value; - - -Agent::Agent(Environment* env) : state_(kNone), - port_(5858), - wait_(false), - parent_env_(env), - child_env_(nullptr), - dispatch_handler_(nullptr) { - CHECK_EQ(0, uv_sem_init(&start_sem_, 0)); -} - - -Agent::~Agent() { - Stop(); - - uv_sem_destroy(&start_sem_); + using v8::Context; + using v8::FunctionCallbackInfo; + using v8::FunctionTemplate; + using v8::HandleScope; + using v8::Integer; + using v8::Isolate; + using v8::Local; + using v8::Locker; + using v8::NewStringType; + using v8::Object; + using v8::String; + using v8::Value; + + Agent::Agent(Environment* env) + : state_(kNone) + , port_(5858) + , wait_(false) + , parent_env_(env) + , child_env_(nullptr) + , dispatch_handler_(nullptr) + { + NODE_CHECK_EQ(0, uv_sem_init(&start_sem_, 0)); + } - while (AgentMessage* msg = messages_.PopFront()) - delete msg; -} + Agent::~Agent() + { + Stop(); + uv_sem_destroy(&start_sem_); -bool Agent::Start(const std::string& host, int port, bool wait) { - int err; + while (AgentMessage* msg = messages_.PopFront()) + delete msg; + } - if (state_ == kRunning) - return false; + bool Agent::Start(const std::string& host, int port, bool wait) + { + int err; - err = uv_loop_init(&child_loop_); - if (err != 0) - goto loop_init_failed; + if (state_ == kRunning) + return false; - // Interruption signal handler - err = uv_async_init(&child_loop_, &child_signal_, ChildSignalCb); - if (err != 0) - goto async_init_failed; - uv_unref(reinterpret_cast(&child_signal_)); + err = uv_loop_init(&child_loop_); + if (err != 0) + goto loop_init_failed; - host_ = host; - port_ = port; - wait_ = wait; + // Interruption signal handler + err = uv_async_init(&child_loop_, &child_signal_, ChildSignalCb); + if (err != 0) + goto async_init_failed; + uv_unref(reinterpret_cast(&child_signal_)); - err = uv_thread_create(&thread_, - reinterpret_cast(ThreadCb), - this); - if (err != 0) - goto thread_create_failed; + host_ = host; + port_ = port; + wait_ = wait; - uv_sem_wait(&start_sem_); + err = uv_thread_create(&thread_, + reinterpret_cast(ThreadCb), + this); + if (err != 0) + goto thread_create_failed; - state_ = kRunning; + uv_sem_wait(&start_sem_); - return true; + state_ = kRunning; - thread_create_failed: - uv_close(reinterpret_cast(&child_signal_), nullptr); + return true; - async_init_failed: - err = uv_loop_close(&child_loop_); - CHECK_EQ(err, 0); + thread_create_failed: + uv_close(reinterpret_cast(&child_signal_), nullptr); - loop_init_failed: - return false; -} + async_init_failed: + err = uv_loop_close(&child_loop_); + NODE_CHECK_EQ(err, 0); + loop_init_failed: + return false; + } -void Agent::Enable() { - v8::Debug::SetMessageHandler( + void Agent::Enable() + { + v8::Debug::SetMessageHandler( #if !(V8_MAJOR_VERSION == 4 && V8_MINOR_VERSION == 8) - parent_env()->isolate(), + parent_env()->isolate(), #endif - MessageHandler); + MessageHandler); - // Assign environment to the debugger's context - // NOTE: The debugger context is created after `SetMessageHandler()` call - auto debug_context = v8::Debug::GetDebugContext( + // Assign environment to the debugger's context + // NOTE: The debugger context is created after `SetMessageHandler()` call + auto debug_context = v8::Debug::GetDebugContext( #if !(V8_MAJOR_VERSION == 4 && V8_MINOR_VERSION == 8) - parent_env()->isolate() + parent_env()->isolate() #endif - ); - parent_env()->AssignToContext(debug_context); -} - + ); + parent_env()->AssignToContext(debug_context); + } -void Agent::Stop() { - int err; + void Agent::Stop() + { + int err; - if (state_ != kRunning) { - return; - } + if (state_ != kRunning) { + return; + } - v8::Debug::SetMessageHandler( + v8::Debug::SetMessageHandler( #if !(V8_MAJOR_VERSION == 4 && V8_MINOR_VERSION == 8) - parent_env()->isolate(), + parent_env()->isolate(), #endif - nullptr); - - // Send empty message to terminate things - EnqueueMessage(new AgentMessage(nullptr, 0)); - - // Signal worker thread to make it stop - err = uv_async_send(&child_signal_); - CHECK_EQ(err, 0); - - err = uv_thread_join(&thread_); - CHECK_EQ(err, 0); - - uv_close(reinterpret_cast(&child_signal_), nullptr); - uv_run(&child_loop_, UV_RUN_NOWAIT); - - err = uv_loop_close(&child_loop_); - CHECK_EQ(err, 0); - - state_ = kNone; -} - - -void Agent::WorkerRun() { - static const char* argv[] = { "node", "--debug-agent" }; - Isolate::CreateParams params; - ArrayBufferAllocator array_buffer_allocator; - params.array_buffer_allocator = &array_buffer_allocator; - Isolate* isolate = Isolate::New(params); - { - Locker locker(isolate); - Isolate::Scope isolate_scope(isolate); - - HandleScope handle_scope(isolate); - Local context = Context::New(isolate); - - Context::Scope context_scope(context); - Environment* env = CreateEnvironment( - isolate, - &child_loop_, - context, - arraysize(argv), - argv, - arraysize(argv), - argv); - - child_env_ = env; + nullptr); - // Expose API - InitAdaptor(env); - LoadEnvironment(env); + // Send empty message to terminate things + EnqueueMessage(new AgentMessage(nullptr, 0)); - CHECK_EQ(&child_loop_, env->event_loop()); - uv_run(&child_loop_, UV_RUN_DEFAULT); + // Signal worker thread to make it stop + err = uv_async_send(&child_signal_); + NODE_CHECK_EQ(err, 0); - // Clean-up peristent - api_.Reset(); + err = uv_thread_join(&thread_); + NODE_CHECK_EQ(err, 0); - // Clean-up all running handles - env->CleanupHandles(); + uv_close(reinterpret_cast(&child_signal_), nullptr); + uv_run(&child_loop_, UV_RUN_NOWAIT); - env->Dispose(); - env = nullptr; - } - isolate->Dispose(); -} - - -void Agent::InitAdaptor(Environment* env) { - Isolate* isolate = env->isolate(); - HandleScope scope(isolate); - - // Create API adaptor - Local t = FunctionTemplate::New(isolate); - t->InstanceTemplate()->SetInternalFieldCount(1); - t->SetClassName(String::NewFromUtf8(isolate, "DebugAPI")); - - NODE_SET_PROTOTYPE_METHOD(t, "notifyListen", NotifyListen); - NODE_SET_PROTOTYPE_METHOD(t, "notifyWait", NotifyWait); - NODE_SET_PROTOTYPE_METHOD(t, "sendCommand", SendCommand); - - Local api = - t->GetFunction()->NewInstance(env->context()).ToLocalChecked(); - api->SetAlignedPointerInInternalField(0, this); - - api->Set(String::NewFromUtf8(isolate, "host", - NewStringType::kNormal).ToLocalChecked(), - String::NewFromUtf8(isolate, host_.data(), NewStringType::kNormal, - host_.size()).ToLocalChecked()); - api->Set(String::NewFromUtf8(isolate, "port"), Integer::New(isolate, port_)); - - env->process_object()->Set(String::NewFromUtf8(isolate, "_debugAPI"), api); - api_.Reset(env->isolate(), api); -} - - -Agent* Agent::Unwrap(const v8::FunctionCallbackInfo& args) { - void* ptr = args.Holder()->GetAlignedPointerFromInternalField(0); - return reinterpret_cast(ptr); -} - - -void Agent::NotifyListen(const FunctionCallbackInfo& args) { - Agent* a = Unwrap(args); - - // Notify other thread that we are ready to process events - uv_sem_post(&a->start_sem_); -} + err = uv_loop_close(&child_loop_); + NODE_CHECK_EQ(err, 0); + state_ = kNone; + } -void Agent::NotifyWait(const FunctionCallbackInfo& args) { - Agent* a = Unwrap(args); + void Agent::WorkerRun() + { + static const char* argv[] = { "node", "--debug-agent" }; + Isolate::CreateParams params; + ArrayBufferAllocator array_buffer_allocator; + params.array_buffer_allocator = &array_buffer_allocator; + Isolate* isolate = Isolate::New(params); + { + Locker locker(isolate); + Isolate::Scope isolate_scope(isolate); + + HandleScope handle_scope(isolate); + Local context = Context::New(isolate); + + Context::Scope context_scope(context); + Environment* env = CreateEnvironment( + isolate, + &child_loop_, + context, + arraysize(argv), + argv, + arraysize(argv), + argv); + + child_env_ = env; + + // Expose API + InitAdaptor(env); + LoadEnvironment(env); + + NODE_CHECK_EQ(&child_loop_, env->event_loop()); + uv_run(&child_loop_, UV_RUN_DEFAULT); + + // Clean-up peristent + api_.Reset(); + + // Clean-up all running handles + env->CleanupHandles(); + + env->Dispose(); + env = nullptr; + } + isolate->Dispose(); + } - a->wait_ = false; + void Agent::InitAdaptor(Environment* env) + { + Isolate* isolate = env->isolate(); + HandleScope scope(isolate); + + // Create API adaptor + Local t = FunctionTemplate::New(isolate); + t->InstanceTemplate()->SetInternalFieldCount(1); + t->SetClassName(String::NewFromUtf8(isolate, "DebugAPI")); + + NODE_SET_PROTOTYPE_METHOD(t, "notifyListen", NotifyListen); + NODE_SET_PROTOTYPE_METHOD(t, "notifyWait", NotifyWait); + NODE_SET_PROTOTYPE_METHOD(t, "sendCommand", SendCommand); + + Local api = t->GetFunction()->NewInstance(env->context()).ToLocalChecked(); + api->SetAlignedPointerInInternalField(0, this); + + api->Set(String::NewFromUtf8(isolate, "host", + NewStringType::kNormal) + .ToLocalChecked(), + String::NewFromUtf8(isolate, host_.data(), NewStringType::kNormal, + host_.size()) + .ToLocalChecked()); + api->Set(String::NewFromUtf8(isolate, "port"), Integer::New(isolate, port_)); + + env->process_object()->Set(String::NewFromUtf8(isolate, "_debugAPI"), api); + api_.Reset(env->isolate(), api); + } - int err = uv_async_send(&a->child_signal_); - CHECK_EQ(err, 0); -} + Agent* Agent::Unwrap(const v8::FunctionCallbackInfo& args) + { + void* ptr = args.Holder()->GetAlignedPointerFromInternalField(0); + return reinterpret_cast(ptr); + } + void Agent::NotifyListen(const FunctionCallbackInfo& args) + { + Agent* a = Unwrap(args); -void Agent::SendCommand(const FunctionCallbackInfo& args) { - Agent* a = Unwrap(args); - Environment* env = a->child_env(); - HandleScope scope(env->isolate()); + // Notify other thread that we are ready to process events + uv_sem_post(&a->start_sem_); + } - String::Value v(args[0]); + void Agent::NotifyWait(const FunctionCallbackInfo& args) + { + Agent* a = Unwrap(args); - v8::Debug::SendCommand(a->parent_env()->isolate(), *v, v.length()); - if (a->dispatch_handler_ != nullptr) - a->dispatch_handler_(a->parent_env()); -} + a->wait_ = false; + int err = uv_async_send(&a->child_signal_); + NODE_CHECK_EQ(err, 0); + } -void Agent::ThreadCb(Agent* agent) { - agent->WorkerRun(); -} + void Agent::SendCommand(const FunctionCallbackInfo& args) + { + Agent* a = Unwrap(args); + Environment* env = a->child_env(); + HandleScope scope(env->isolate()); + String::Value v(args.GetIsolate(), args[0]); -void Agent::ChildSignalCb(uv_async_t* signal) { - Agent* a = ContainerOf(&Agent::child_signal_, signal); - Isolate* isolate = a->child_env()->isolate(); + v8::Debug::SendCommand(a->parent_env()->isolate(), *v, v.length()); + if (a->dispatch_handler_ != nullptr) + a->dispatch_handler_(a->parent_env()); + } - HandleScope scope(isolate); - Local api = PersistentToLocal(isolate, a->api_); + void Agent::ThreadCb(Agent* agent) + { + agent->WorkerRun(); + } - Mutex::ScopedLock scoped_lock(a->message_mutex_); - while (AgentMessage* msg = a->messages_.PopFront()) { - // Time to close everything - if (msg->data() == nullptr) { - delete msg; + void Agent::ChildSignalCb(uv_async_t* signal) + { + Agent* a = ContainerOf(&Agent::child_signal_, signal); + Isolate* isolate = a->child_env()->isolate(); + + HandleScope scope(isolate); + Local api = PersistentToLocal(isolate, a->api_); + + Mutex::ScopedLock scoped_lock(a->message_mutex_); + while (AgentMessage* msg = a->messages_.PopFront()) { + // Time to close everything + if (msg->data() == nullptr) { + delete msg; + + MakeCallback(isolate, api, "onclose", 0, nullptr); + break; + } + + // Waiting for client, do not send anything just yet + if (a->wait_) { + a->messages_.PushFront(msg); // Push message back into the ready queue. + break; + } + + Local argv[] = { + String::NewFromTwoByte(isolate, + msg->data(), + String::kNormalString, + msg->length()) + }; + + // Emit message + MakeCallback(isolate, + api, + "onmessage", + arraysize(argv), + argv); + delete msg; + } + } - MakeCallback(isolate, api, "onclose", 0, nullptr); - break; + void Agent::EnqueueMessage(AgentMessage* message) + { + Mutex::ScopedLock scoped_lock(message_mutex_); + messages_.PushBack(message); + uv_async_send(&child_signal_); } - // Waiting for client, do not send anything just yet - if (a->wait_) { - a->messages_.PushFront(msg); // Push message back into the ready queue. - break; + void Agent::MessageHandler(const v8::Debug::Message& message) + { + Isolate* isolate = message.GetIsolate(); + Environment* env = Environment::GetCurrent(isolate); + if (env == nullptr) + return; // Called from a non-node context. + Agent* a = env->debugger_agent(); + NODE_CHECK_NE(a, nullptr); + NODE_CHECK_EQ(isolate, a->parent_env()->isolate()); + + HandleScope scope(isolate); + Local json = message.GetJSON(); + String::Value v(isolate, json); + + AgentMessage* msg = new AgentMessage(*v, v.length()); + a->EnqueueMessage(msg); } - Local argv[] = { - String::NewFromTwoByte(isolate, - msg->data(), - String::kNormalString, - msg->length()) - }; - - // Emit message - MakeCallback(isolate, - api, - "onmessage", - arraysize(argv), - argv); - delete msg; - } -} - - -void Agent::EnqueueMessage(AgentMessage* message) { - Mutex::ScopedLock scoped_lock(message_mutex_); - messages_.PushBack(message); - uv_async_send(&child_signal_); -} - - -void Agent::MessageHandler(const v8::Debug::Message& message) { - Isolate* isolate = message.GetIsolate(); - Environment* env = Environment::GetCurrent(isolate); - if (env == nullptr) - return; // Called from a non-node context. - Agent* a = env->debugger_agent(); - CHECK_NE(a, nullptr); - CHECK_EQ(isolate, a->parent_env()->isolate()); - - HandleScope scope(isolate); - Local json = message.GetJSON(); - String::Value v(json); - - AgentMessage* msg = new AgentMessage(*v, v.length()); - a->EnqueueMessage(msg); -} - -} // namespace debugger -} // namespace node +} // namespace debugger +} // namespace node diff --git a/node/src/debug-agent.h b/node/src/debug-agent.h index 504212653b..fd531468d6 100644 --- a/node/src/debug-agent.h +++ b/node/src/debug-agent.h @@ -37,106 +37,110 @@ // Forward declaration to break recursive dependency chain with src/env.h. namespace node { class Environment; -} // namespace node +} // namespace node namespace node { namespace debugger { -class AgentMessage { - public: - AgentMessage(uint16_t* val, int length) : length_(length) { - if (val == nullptr) { - data_ = val; - } else { - data_ = new uint16_t[length]; - memcpy(data_, val, length * sizeof(*data_)); - } - } + class AgentMessage { + public: + AgentMessage(uint16_t* val, int length) + : length_(length) + { + if (val == nullptr) { + data_ = val; + } else { + data_ = new uint16_t[length]; + memcpy(data_, val, length * sizeof(*data_)); + } + } - ~AgentMessage() { - delete[] data_; - data_ = nullptr; - } + ~AgentMessage() + { + delete[] data_; + data_ = nullptr; + } - inline const uint16_t* data() const { return data_; } - inline int length() const { return length_; } + inline const uint16_t* data() const { return data_; } + inline int length() const { return length_; } - ListNode member; + ListNode member; - private: - uint16_t* data_; - int length_; -}; + private: + uint16_t* data_; + int length_; + }; -class Agent { - public: - explicit Agent(node::Environment* env); - ~Agent(); + class Agent { + public: + explicit Agent(node::Environment* env); + ~Agent(); - typedef void (*DispatchHandler)(node::Environment* env); + typedef void (*DispatchHandler)(node::Environment* env); - // Start the debugger agent thread - bool Start(const std::string& host, int port, bool wait); - // Listen for debug events - void Enable(); - // Stop the debugger agent - void Stop(); + // Start the debugger agent thread + bool Start(const std::string& host, int port, bool wait); + // Listen for debug events + void Enable(); + // Stop the debugger agent + void Stop(); - inline void set_dispatch_handler(DispatchHandler handler) { - dispatch_handler_ = handler; - } + inline void set_dispatch_handler(DispatchHandler handler) + { + dispatch_handler_ = handler; + } - inline node::Environment* parent_env() const { return parent_env_; } - inline node::Environment* child_env() const { return child_env_; } + inline node::Environment* parent_env() const { return parent_env_; } + inline node::Environment* child_env() const { return child_env_; } - protected: - void InitAdaptor(Environment* env); + protected: + void InitAdaptor(Environment* env); - // Worker body - void WorkerRun(); + // Worker body + void WorkerRun(); - static void ThreadCb(Agent* agent); - static void ParentSignalCb(uv_async_t* signal); - static void ChildSignalCb(uv_async_t* signal); - static void MessageHandler(const v8::Debug::Message& message); + static void ThreadCb(Agent* agent); + static void ParentSignalCb(uv_async_t* signal); + static void ChildSignalCb(uv_async_t* signal); + static void MessageHandler(const v8::Debug::Message& message); - // V8 API - static Agent* Unwrap(const v8::FunctionCallbackInfo& args); - static void NotifyListen(const v8::FunctionCallbackInfo& args); - static void NotifyWait(const v8::FunctionCallbackInfo& args); - static void SendCommand(const v8::FunctionCallbackInfo& args); + // V8 API + static Agent* Unwrap(const v8::FunctionCallbackInfo& args); + static void NotifyListen(const v8::FunctionCallbackInfo& args); + static void NotifyWait(const v8::FunctionCallbackInfo& args); + static void SendCommand(const v8::FunctionCallbackInfo& args); - void EnqueueMessage(AgentMessage* message); + void EnqueueMessage(AgentMessage* message); - enum State { - kNone, - kRunning - }; + enum State { + kNone, + kRunning + }; - State state_; + State state_; - std::string host_; - int port_; - bool wait_; + std::string host_; + int port_; + bool wait_; - uv_sem_t start_sem_; - node::Mutex message_mutex_; - uv_async_t child_signal_; + uv_sem_t start_sem_; + node::Mutex message_mutex_; + uv_async_t child_signal_; - uv_thread_t thread_; - node::Environment* parent_env_; - node::Environment* child_env_; - uv_loop_t child_loop_; - v8::Persistent api_; + uv_thread_t thread_; + node::Environment* parent_env_; + node::Environment* child_env_; + uv_loop_t child_loop_; + v8::Persistent api_; - ListHead messages_; + ListHead messages_; - DispatchHandler dispatch_handler_; -}; + DispatchHandler dispatch_handler_; + }; -} // namespace debugger -} // namespace node +} // namespace debugger +} // namespace node -#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS - -#endif // SRC_DEBUG_AGENT_H_ +#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#endif // SRC_DEBUG_AGENT_H_ diff --git a/node/src/env-inl.h b/node/src/env-inl.h index e20546781c..44dfbd8b12 100644 --- a/node/src/env-inl.h +++ b/node/src/env-inl.h @@ -16,26 +16,29 @@ namespace node { inline Environment::IsolateData* Environment::IsolateData::Get( - v8::Isolate* isolate) { - return static_cast(isolate->GetData(kIsolateSlot)); + v8::Isolate* isolate) +{ + return static_cast(isolate->GetData(kIsolateSlot)); } inline Environment::IsolateData* Environment::IsolateData::GetOrCreate( - v8::Isolate* isolate, uv_loop_t* loop) { - IsolateData* isolate_data = Get(isolate); - if (isolate_data == nullptr) { - isolate_data = new IsolateData(isolate, loop); - isolate->SetData(kIsolateSlot, isolate_data); - } - isolate_data->ref_count_ += 1; - return isolate_data; -} - -inline void Environment::IsolateData::Put() { - if (--ref_count_ == 0) { - isolate()->SetData(kIsolateSlot, nullptr); - delete this; - } + v8::Isolate* isolate, uv_loop_t* loop) +{ + IsolateData* isolate_data = Get(isolate); + if (isolate_data == nullptr) { + isolate_data = new IsolateData(isolate, loop); + isolate->SetData(kIsolateSlot, isolate_data); + } + isolate_data->ref_count_ += 1; + return isolate_data; +} + +inline void Environment::IsolateData::Put() +{ + if (--ref_count_ == 0) { + isolate()->SetData(kIsolateSlot, nullptr); + delete this; + } } // Create string properties as internalized one byte strings. @@ -48,600 +51,698 @@ inline void Environment::IsolateData::Put() { // One byte because our strings are ASCII and we can safely skip V8's UTF-8 // decoding step. It's a one-time cost, but why pay it when you don't have to? inline Environment::IsolateData::IsolateData(v8::Isolate* isolate, - uv_loop_t* loop) - : event_loop_(loop), - isolate_(isolate), -#define V(PropertyName, StringValue) \ - PropertyName ## _( \ - isolate, \ - v8::Private::ForApi( \ - isolate, \ - v8::String::NewFromOneByte( \ - isolate, \ - reinterpret_cast(StringValue), \ - v8::NewStringType::kInternalized, \ - sizeof(StringValue) - 1).ToLocalChecked())), - PER_ISOLATE_PRIVATE_SYMBOL_PROPERTIES(V) + uv_loop_t* loop) + : event_loop_(loop) + , isolate_(isolate) + , +#define V(PropertyName, StringValue) \ + PropertyName##_( \ + isolate, \ + v8::Private::ForApi( \ + isolate, \ + v8::String::NewFromOneByte( \ + isolate, \ + reinterpret_cast(StringValue), \ + v8::NewStringType::kInternalized, \ + sizeof(StringValue) - 1) \ + .ToLocalChecked())), + PER_ISOLATE_PRIVATE_SYMBOL_PROPERTIES(V) #undef V -#define V(PropertyName, StringValue) \ - PropertyName ## _( \ - isolate, \ - v8::String::NewFromOneByte( \ - isolate, \ - reinterpret_cast(StringValue), \ - v8::NewStringType::kInternalized, \ - sizeof(StringValue) - 1).ToLocalChecked()), - PER_ISOLATE_STRING_PROPERTIES(V) +#define V(PropertyName, StringValue) \ + PropertyName##_( \ + isolate, \ + v8::String::NewFromOneByte( \ + isolate, \ + reinterpret_cast(StringValue), \ + v8::NewStringType::kInternalized, \ + sizeof(StringValue) - 1) \ + .ToLocalChecked()), + PER_ISOLATE_STRING_PROPERTIES(V) #undef V - ref_count_(0) {} + ref_count_(0) +{ +} -inline uv_loop_t* Environment::IsolateData::event_loop() const { - return event_loop_; +inline uv_loop_t* Environment::IsolateData::event_loop() const +{ + return event_loop_; } -inline v8::Isolate* Environment::IsolateData::isolate() const { - return isolate_; +inline v8::Isolate* Environment::IsolateData::isolate() const +{ + return isolate_; } -inline Environment::AsyncHooks::AsyncHooks() { - for (int i = 0; i < kFieldsCount; i++) fields_[i] = 0; +inline Environment::AsyncHooks::AsyncHooks() +{ + for (int i = 0; i < kFieldsCount; i++) + fields_[i] = 0; } -inline uint32_t* Environment::AsyncHooks::fields() { - return fields_; +inline uint32_t* Environment::AsyncHooks::fields() +{ + return fields_; } -inline int Environment::AsyncHooks::fields_count() const { - return kFieldsCount; +inline int Environment::AsyncHooks::fields_count() const +{ + return kFieldsCount; } -inline bool Environment::AsyncHooks::callbacks_enabled() { - return fields_[kEnableCallbacks] != 0; +inline bool Environment::AsyncHooks::callbacks_enabled() +{ + return fields_[kEnableCallbacks] != 0; } -inline void Environment::AsyncHooks::set_enable_callbacks(uint32_t flag) { - fields_[kEnableCallbacks] = flag; +inline void Environment::AsyncHooks::set_enable_callbacks(uint32_t flag) +{ + fields_[kEnableCallbacks] = flag; } inline Environment::AsyncCallbackScope::AsyncCallbackScope(Environment* env) - : env_(env) { - env_->makecallback_cntr_++; + : env_(env) +{ + env_->makecallback_cntr_++; #ifndef MINIBLINK_NOT_IMPLEMENTED - if (env->is_blink_core_) - env_->BlinkMicrotaskSuppressionEnter(env_); + if (env->is_blink_core_) + env_->BlinkMicrotaskSuppressionEnter(env_); #endif } -inline Environment::AsyncCallbackScope::~AsyncCallbackScope() { - env_->makecallback_cntr_--; +inline Environment::AsyncCallbackScope::~AsyncCallbackScope() +{ + env_->makecallback_cntr_--; #ifndef MINIBLINK_NOT_IMPLEMENTED - if (env_->is_blink_core_) - env_->BlinkMicrotaskSuppressionLeave(env_); + if (env_->is_blink_core_) + env_->BlinkMicrotaskSuppressionLeave(env_); #endif } -inline bool Environment::AsyncCallbackScope::in_makecallback() { - return env_->makecallback_cntr_ > 1; +inline bool Environment::AsyncCallbackScope::in_makecallback() +{ + return env_->makecallback_cntr_ > 1; } -inline Environment::DomainFlag::DomainFlag() { - for (int i = 0; i < kFieldsCount; ++i) fields_[i] = 0; +inline Environment::DomainFlag::DomainFlag() +{ + for (int i = 0; i < kFieldsCount; ++i) + fields_[i] = 0; } -inline uint32_t* Environment::DomainFlag::fields() { - return fields_; +inline uint32_t* Environment::DomainFlag::fields() +{ + return fields_; } -inline int Environment::DomainFlag::fields_count() const { - return kFieldsCount; +inline int Environment::DomainFlag::fields_count() const +{ + return kFieldsCount; } -inline uint32_t Environment::DomainFlag::count() const { - return fields_[kCount]; +inline uint32_t Environment::DomainFlag::count() const +{ + return fields_[kCount]; } -inline Environment::TickInfo::TickInfo() { - for (int i = 0; i < kFieldsCount; ++i) - fields_[i] = 0; +inline Environment::TickInfo::TickInfo() +{ + for (int i = 0; i < kFieldsCount; ++i) + fields_[i] = 0; } -inline uint32_t* Environment::TickInfo::fields() { - return fields_; +inline uint32_t* Environment::TickInfo::fields() +{ + return fields_; } -inline int Environment::TickInfo::fields_count() const { - return kFieldsCount; +inline int Environment::TickInfo::fields_count() const +{ + return kFieldsCount; } -inline uint32_t Environment::TickInfo::index() const { - return fields_[kIndex]; +inline uint32_t Environment::TickInfo::index() const +{ + return fields_[kIndex]; } -inline uint32_t Environment::TickInfo::length() const { - return fields_[kLength]; +inline uint32_t Environment::TickInfo::length() const +{ + return fields_[kLength]; } -inline void Environment::TickInfo::set_index(uint32_t value) { - fields_[kIndex] = value; +inline void Environment::TickInfo::set_index(uint32_t value) +{ + fields_[kIndex] = value; } -inline Environment::ArrayBufferAllocatorInfo::ArrayBufferAllocatorInfo() { - for (int i = 0; i < kFieldsCount; ++i) - fields_[i] = 0; +inline Environment::ArrayBufferAllocatorInfo::ArrayBufferAllocatorInfo() +{ + for (int i = 0; i < kFieldsCount; ++i) + fields_[i] = 0; } -inline uint32_t* Environment::ArrayBufferAllocatorInfo::fields() { - return fields_; +inline uint32_t* Environment::ArrayBufferAllocatorInfo::fields() +{ + return fields_; } -inline int Environment::ArrayBufferAllocatorInfo::fields_count() const { - return kFieldsCount; +inline int Environment::ArrayBufferAllocatorInfo::fields_count() const +{ + return kFieldsCount; } -inline bool Environment::ArrayBufferAllocatorInfo::no_zero_fill() const { - return fields_[kNoZeroFill] != 0; +inline bool Environment::ArrayBufferAllocatorInfo::no_zero_fill() const +{ + return fields_[kNoZeroFill] != 0; } -inline void Environment::ArrayBufferAllocatorInfo::reset_fill_flag() { - fields_[kNoZeroFill] = 0; +inline void Environment::ArrayBufferAllocatorInfo::reset_fill_flag() +{ + fields_[kNoZeroFill] = 0; } inline Environment* Environment::New(v8::Local context, - uv_loop_t* loop) { - Environment* env = new Environment(context, loop); - env->AssignToContext(context); - return env; + uv_loop_t* loop) +{ + Environment* env = new Environment(context, loop); + env->AssignToContext(context); + return env; } -inline void Environment::AssignToContext(v8::Local context) { - context->SetAlignedPointerInEmbedderData(kContextEmbedderDataIndex, this); +inline void Environment::AssignToContext(v8::Local context) +{ + context->SetAlignedPointerInEmbedderData(kContextEmbedderDataIndex, this); } -inline Environment* Environment::GetCurrent(v8::Isolate* isolate) { - return GetCurrent(isolate->GetCurrentContext()); +inline Environment* Environment::GetCurrent(v8::Isolate* isolate) +{ + return GetCurrent(isolate->GetCurrentContext()); } -inline Environment* Environment::GetCurrent(v8::Local context) { - Environment* env = static_cast(context->GetAlignedPointerFromEmbedderData(kContextEmbedderDataIndex)); - if (!IsLiveObj((intptr_t)env)) - return nullptr; - return env; +inline Environment* Environment::GetCurrent(v8::Local context) +{ + Environment* env = static_cast(context->GetAlignedPointerFromEmbedderData(kContextEmbedderDataIndex)); + if (!IsLiveObj((intptr_t)env)) + return nullptr; + return env; } inline Environment* Environment::GetCurrent( - const v8::FunctionCallbackInfo& info) { - ASSERT(info.Data()->IsExternal()); - Environment* env = static_cast(info.Data().As()->Value()); - if (!IsLiveObj((intptr_t)env)) - return nullptr; - return env; + const v8::FunctionCallbackInfo& info) +{ + NODE_ASSERT(info.Data()->IsExternal()); + Environment* env = static_cast(info.Data().As()->Value()); + if (!IsLiveObj((intptr_t)env)) + return nullptr; + return env; } template inline Environment* Environment::GetCurrent( - const v8::PropertyCallbackInfo& info) { - ASSERT(info.Data()->IsExternal()); - // XXX(bnoordhuis) Work around a g++ 4.9.2 template type inferrer bug - // when the expression is written as info.Data().As(). - v8::Local data = info.Data(); - Environment* env = static_cast(data.As()->Value()); - if (!IsLiveObj((intptr_t)env)) - return nullptr; - return env; + const v8::PropertyCallbackInfo& info) +{ + NODE_ASSERT(info.Data()->IsExternal()); + // XXX(bnoordhuis) Work around a g++ 4.9.2 template type inferrer bug + // when the expression is written as info.Data().As(). + v8::Local data = info.Data(); + Environment* env = static_cast(data.As()->Value()); + if (!IsLiveObj((intptr_t)env)) + return nullptr; + return env; } inline Environment::Environment(v8::Local context, - uv_loop_t* loop) - : isolate_(context->GetIsolate()), - isolate_data_(IsolateData::GetOrCreate(context->GetIsolate(), loop)), - timer_base_(uv_now(loop)), - using_domains_(false), - printed_error_(false), - trace_sync_io_(false), - makecallback_cntr_(0), - async_wrap_uid_(0), - debugger_agent_(nullptr), + uv_loop_t* loop) + : isolate_(context->GetIsolate()) + , isolate_data_(IsolateData::GetOrCreate(context->GetIsolate(), loop)) + , timer_base_(uv_now(loop)) + , using_domains_(false) + , printed_error_(false) + , trace_sync_io_(false) + , makecallback_cntr_(0) + , async_wrap_uid_(0) + , debugger_agent_(nullptr) + , #if HAVE_INSPECTOR - inspector_agent_(nullptr), + inspector_agent_(nullptr) + , #endif - http_parser_buffer_(nullptr), + http_parser_buffer_(nullptr) + , #ifndef MINIBLINK_NOT_IMPLEMENTED - file_system_hooks_(nullptr), - is_blink_core_(false), - blink_microtask_suppression_handle_(nullptr), + file_system_hooks_(nullptr) + , is_blink_core_(false) + , blink_microtask_suppression_handle_(nullptr) + , #endif - context_(context->GetIsolate(), context), - cleanup_hooks_(nullptr), - cleanup_hook_counter_(0) { - // We'll be creating new objects so make sure we've entered the context. - v8::HandleScope handle_scope(isolate()); - v8::Context::Scope context_scope(context); - set_as_external(v8::External::New(isolate(), this)); - set_binding_cache_object(v8::Object::New(isolate())); - set_module_load_list_array(v8::Array::New(isolate())); - - v8::Local fn = v8::FunctionTemplate::New(isolate()); - fn->SetClassName(FIXED_ONE_BYTE_STRING(isolate(), "InternalFieldObject")); - v8::Local obj = fn->InstanceTemplate(); - obj->SetInternalFieldCount(1); - set_generic_internal_field_template(obj); - - RB_INIT(&cares_task_list_); - handle_cleanup_waiting_ = 0; - - InitEnv(); - AddLiveSet((intptr_t)this); -} - -inline Environment::~Environment() { - v8::HandleScope handle_scope(isolate()); - CleanEnv(); - RemoveLiveSet((intptr_t)this); - - context()->SetAlignedPointerInEmbedderData(kContextEmbedderDataIndex, - nullptr); -#define V(PropertyName, TypeName) PropertyName ## _.Reset(); - ENVIRONMENT_STRONG_PERSISTENT_PROPERTIES(V) + context_(context->GetIsolate(), context) + , cleanup_hooks_(nullptr) + , cleanup_hook_counter_(0) +{ + // We'll be creating new objects so make sure we've entered the context. + v8::HandleScope handle_scope(isolate()); + v8::Context::Scope context_scope(context); + set_as_external(v8::External::New(isolate(), this)); + set_binding_cache_object(v8::Object::New(isolate())); + set_module_load_list_array(v8::Array::New(isolate())); + + v8::Local fn = v8::FunctionTemplate::New(isolate()); + fn->SetClassName(FIXED_ONE_BYTE_STRING(isolate(), "InternalFieldObject")); + v8::Local obj = fn->InstanceTemplate(); + obj->SetInternalFieldCount(1); + set_generic_internal_field_template(obj); + + RB_INIT(&cares_task_list_); + handle_cleanup_waiting_ = 0; + + InitEnv(); + AddLiveSet((intptr_t)this); +} + +inline Environment::~Environment() +{ + v8::HandleScope handle_scope(isolate()); + //CleanEnv(); + CleanNodeEnv(this); + RemoveLiveSet((intptr_t)this); + + context()->SetAlignedPointerInEmbedderData(kContextEmbedderDataIndex, + nullptr); +#define V(PropertyName, TypeName) PropertyName##_.Reset(); + ENVIRONMENT_STRONG_PERSISTENT_PROPERTIES(V) #undef V - isolate_data()->Put(); + isolate_data()->Put(); - delete[] heap_statistics_buffer_; - delete[] heap_space_statistics_buffer_; - delete[] http_parser_buffer_; + delete[] heap_statistics_buffer_; + delete[] heap_space_statistics_buffer_; + delete[] http_parser_buffer_; } -inline void Environment::CleanupHandles() { - while (HandleCleanup* hc = handle_cleanup_queue_.PopFront()) { - handle_cleanup_waiting_++; - hc->cb_(this, hc->handle_, hc->arg_); - delete hc; - } +inline void Environment::CleanupHandles() +{ + while (HandleCleanup* hc = handle_cleanup_queue_.PopFront()) { + handle_cleanup_waiting_++; + hc->cb_(this, hc->handle_, hc->arg_); + delete hc; + } - while (handle_cleanup_waiting_ != 0) - uv_run(event_loop(), UV_RUN_ONCE); + while (handle_cleanup_waiting_ != 0) + uv_run(event_loop(), UV_RUN_ONCE); } -inline void Environment::Dispose() { - delete this; +inline void Environment::Dispose() +{ + delete this; } -inline v8::Isolate* Environment::isolate() const { - return isolate_; +inline v8::Isolate* Environment::isolate() const +{ + return isolate_; } -inline bool Environment::async_wrap_callbacks_enabled() const { - // The const_cast is okay, it doesn't violate conceptual const-ness. - return const_cast(this)->async_hooks()->callbacks_enabled(); +inline bool Environment::async_wrap_callbacks_enabled() const +{ + // The const_cast is okay, it doesn't violate conceptual const-ness. + return const_cast(this)->async_hooks()->callbacks_enabled(); } -inline bool Environment::in_domain() const { - // The const_cast is okay, it doesn't violate conceptual const-ness. - return using_domains() && - const_cast(this)->domain_flag()->count() > 0; +inline bool Environment::in_domain() const +{ + // The const_cast is okay, it doesn't violate conceptual const-ness. + return using_domains() && const_cast(this)->domain_flag()->count() > 0; } inline Environment* Environment::from_immediate_check_handle( - uv_check_t* handle) { - return ContainerOf(&Environment::immediate_check_handle_, handle); + uv_check_t* handle) +{ + return ContainerOf(&Environment::immediate_check_handle_, handle); } -inline uv_check_t* Environment::immediate_check_handle() { - return &immediate_check_handle_; +inline uv_check_t* Environment::immediate_check_handle() +{ + return &immediate_check_handle_; } -inline uv_idle_t* Environment::immediate_idle_handle() { - return &immediate_idle_handle_; +inline uv_idle_t* Environment::immediate_idle_handle() +{ + return &immediate_idle_handle_; } inline Environment* Environment::from_destroy_ids_idle_handle( - uv_idle_t* handle) { - return ContainerOf(&Environment::destroy_ids_idle_handle_, handle); + uv_idle_t* handle) +{ + return ContainerOf(&Environment::destroy_ids_idle_handle_, handle); } -inline uv_idle_t* Environment::destroy_ids_idle_handle() { - return &destroy_ids_idle_handle_; +inline uv_idle_t* Environment::destroy_ids_idle_handle() +{ + return &destroy_ids_idle_handle_; } inline Environment* Environment::from_idle_prepare_handle( - uv_prepare_t* handle) { - return ContainerOf(&Environment::idle_prepare_handle_, handle); + uv_prepare_t* handle) +{ + return ContainerOf(&Environment::idle_prepare_handle_, handle); } -inline uv_prepare_t* Environment::idle_prepare_handle() { - return &idle_prepare_handle_; +inline uv_prepare_t* Environment::idle_prepare_handle() +{ + return &idle_prepare_handle_; } -inline Environment* Environment::from_idle_check_handle(uv_check_t* handle) { - return ContainerOf(&Environment::idle_check_handle_, handle); +inline Environment* Environment::from_idle_check_handle(uv_check_t* handle) +{ + return ContainerOf(&Environment::idle_check_handle_, handle); } -inline uv_check_t* Environment::idle_check_handle() { - return &idle_check_handle_; +inline uv_check_t* Environment::idle_check_handle() +{ + return &idle_check_handle_; } inline void Environment::RegisterHandleCleanup(uv_handle_t* handle, - HandleCleanupCb cb, - void *arg) { - handle_cleanup_queue_.PushBack(new HandleCleanup(handle, cb, arg)); + HandleCleanupCb cb, + void* arg) +{ + handle_cleanup_queue_.PushBack(new HandleCleanup(handle, cb, arg)); } -inline void Environment::FinishHandleCleanup(uv_handle_t* handle) { - handle_cleanup_waiting_--; +inline void Environment::FinishHandleCleanup(uv_handle_t* handle) +{ + handle_cleanup_waiting_--; } -inline uv_loop_t* Environment::event_loop() const { - return isolate_data()->event_loop(); +inline uv_loop_t* Environment::event_loop() const +{ + return isolate_data()->event_loop(); } -inline Environment::AsyncHooks* Environment::async_hooks() { - return &async_hooks_; +inline Environment::AsyncHooks* Environment::async_hooks() +{ + return &async_hooks_; } -inline Environment::FileSystemHooks* Environment::file_system_hooks() { - return file_system_hooks_; +inline Environment::FileSystemHooks* Environment::file_system_hooks() +{ + return file_system_hooks_; } -inline void Environment::file_system_hooks(Environment::FileSystemHooks* h) { - file_system_hooks_ = h; +inline void Environment::file_system_hooks(Environment::FileSystemHooks* h) +{ + file_system_hooks_ = h; } -inline void Environment::set_is_blink_core() { - is_blink_core_ = true; +inline void Environment::set_is_blink_core() +{ + is_blink_core_ = true; } -inline bool Environment::is_blink_core() const { +inline bool Environment::is_blink_core() const +{ return is_blink_core_; } -inline Environment::DomainFlag* Environment::domain_flag() { - return &domain_flag_; +inline Environment::DomainFlag* Environment::domain_flag() +{ + return &domain_flag_; } -inline Environment::TickInfo* Environment::tick_info() { - return &tick_info_; +inline Environment::TickInfo* Environment::tick_info() +{ + return &tick_info_; } inline Environment::ArrayBufferAllocatorInfo* - Environment::array_buffer_allocator_info() { - return &array_buffer_allocator_info_; +Environment::array_buffer_allocator_info() +{ + return &array_buffer_allocator_info_; } -inline uint64_t Environment::timer_base() const { - return timer_base_; +inline uint64_t Environment::timer_base() const +{ + return timer_base_; } -inline bool Environment::using_domains() const { - return using_domains_; +inline bool Environment::using_domains() const +{ + return using_domains_; } -inline void Environment::set_using_domains(bool value) { - using_domains_ = value; +inline void Environment::set_using_domains(bool value) +{ + using_domains_ = value; } -inline bool Environment::printed_error() const { - return printed_error_; +inline bool Environment::printed_error() const +{ + return printed_error_; } -inline void Environment::set_printed_error(bool value) { - printed_error_ = value; +inline void Environment::set_printed_error(bool value) +{ + printed_error_ = value; } -inline void Environment::set_trace_sync_io(bool value) { - trace_sync_io_ = value; +inline void Environment::set_trace_sync_io(bool value) +{ + trace_sync_io_ = value; } -inline int64_t Environment::get_async_wrap_uid() { - return ++async_wrap_uid_; +inline int64_t Environment::get_async_wrap_uid() +{ + return ++async_wrap_uid_; } -inline std::vector* Environment::destroy_ids_list() { - return destroy_ids_list_; +inline std::vector* Environment::destroy_ids_list() +{ + return destroy_ids_list_; } -inline uint32_t* Environment::heap_statistics_buffer() const { - CHECK_NE(heap_statistics_buffer_, nullptr); - return heap_statistics_buffer_; +inline uint32_t* Environment::heap_statistics_buffer() const +{ + NODE_CHECK_NE(heap_statistics_buffer_, nullptr); + return heap_statistics_buffer_; } -inline void Environment::set_heap_statistics_buffer(uint32_t* pointer) { - CHECK_EQ(heap_statistics_buffer_, nullptr); // Should be set only once. - heap_statistics_buffer_ = pointer; +inline void Environment::set_heap_statistics_buffer(uint32_t* pointer) +{ + NODE_CHECK_EQ(heap_statistics_buffer_, nullptr); // Should be set only once. + heap_statistics_buffer_ = pointer; } -inline uint32_t* Environment::heap_space_statistics_buffer() const { - CHECK_NE(heap_space_statistics_buffer_, nullptr); - return heap_space_statistics_buffer_; +inline uint32_t* Environment::heap_space_statistics_buffer() const +{ + NODE_CHECK_NE(heap_space_statistics_buffer_, nullptr); + return heap_space_statistics_buffer_; } -inline void Environment::set_heap_space_statistics_buffer(uint32_t* pointer) { - CHECK_EQ(heap_space_statistics_buffer_, nullptr); // Should be set only once. - heap_space_statistics_buffer_ = pointer; +inline void Environment::set_heap_space_statistics_buffer(uint32_t* pointer) +{ + NODE_CHECK_EQ(heap_space_statistics_buffer_, nullptr); // Should be set only once. + heap_space_statistics_buffer_ = pointer; } - -inline char* Environment::http_parser_buffer() const { - return http_parser_buffer_; +inline char* Environment::http_parser_buffer() const +{ + return http_parser_buffer_; } -inline void Environment::set_http_parser_buffer(char* buffer) { - CHECK_EQ(http_parser_buffer_, nullptr); // Should be set only once. - http_parser_buffer_ = buffer; +inline void Environment::set_http_parser_buffer(char* buffer) +{ + NODE_CHECK_EQ(http_parser_buffer_, nullptr); // Should be set only once. + http_parser_buffer_ = buffer; } -inline Environment* Environment::from_cares_timer_handle(uv_timer_t* handle) { - return ContainerOf(&Environment::cares_timer_handle_, handle); +inline Environment* Environment::from_cares_timer_handle(uv_timer_t* handle) +{ + return ContainerOf(&Environment::cares_timer_handle_, handle); } -inline uv_timer_t* Environment::cares_timer_handle() { - return &cares_timer_handle_; +inline uv_timer_t* Environment::cares_timer_handle() +{ + return &cares_timer_handle_; } -inline ares_channel Environment::cares_channel() { - return cares_channel_; +inline ares_channel Environment::cares_channel() +{ + return cares_channel_; } // Only used in the call to ares_init_options(). -inline ares_channel* Environment::cares_channel_ptr() { - return &cares_channel_; +inline ares_channel* Environment::cares_channel_ptr() +{ + return &cares_channel_; } -inline node_ares_task_list* Environment::cares_task_list() { - return &cares_task_list_; +inline node_ares_task_list* Environment::cares_task_list() +{ + return &cares_task_list_; } -inline Environment::IsolateData* Environment::isolate_data() const { - return isolate_data_; +inline Environment::IsolateData* Environment::isolate_data() const +{ + return isolate_data_; } -inline void Environment::ThrowError(const char* errmsg) { - ThrowError(v8::Exception::Error, errmsg); +inline void Environment::ThrowError(const char* errmsg) +{ + ThrowError(v8::Exception::Error, errmsg); } -inline void Environment::ThrowTypeError(const char* errmsg) { - ThrowError(v8::Exception::TypeError, errmsg); +inline void Environment::ThrowTypeError(const char* errmsg) +{ + ThrowError(v8::Exception::TypeError, errmsg); } -inline void Environment::ThrowRangeError(const char* errmsg) { - ThrowError(v8::Exception::RangeError, errmsg); +inline void Environment::ThrowRangeError(const char* errmsg) +{ + ThrowError(v8::Exception::RangeError, errmsg); } inline void Environment::ThrowError( v8::Local (*fun)(v8::Local), - const char* errmsg) { - v8::HandleScope handle_scope(isolate()); - isolate()->ThrowException(fun(OneByteString(isolate(), errmsg))); + const char* errmsg) +{ + v8::HandleScope handle_scope(isolate()); + isolate()->ThrowException(fun(OneByteString(isolate(), errmsg))); } inline void Environment::ThrowErrnoException(int errorno, - const char* syscall, - const char* message, - const char* path) { - isolate()->ThrowException( - ErrnoException(isolate(), errorno, syscall, message, path)); + const char* syscall, + const char* message, + const char* path) +{ + isolate()->ThrowException( + ErrnoException(isolate(), errorno, syscall, message, path)); } inline void Environment::ThrowUVException(int errorno, - const char* syscall, - const char* message, - const char* path, - const char* dest) { - isolate()->ThrowException( - UVException(isolate(), errorno, syscall, message, path, dest)); + const char* syscall, + const char* message, + const char* path, + const char* dest) +{ + isolate()->ThrowException( + UVException(isolate(), errorno, syscall, message, path, dest)); } inline v8::Local - Environment::NewFunctionTemplate(v8::FunctionCallback callback, - v8::Local signature) { - v8::Local external = as_external(); - return v8::FunctionTemplate::New(isolate(), callback, external, signature); +Environment::NewFunctionTemplate(v8::FunctionCallback callback, + v8::Local signature) +{ + v8::Local external = as_external(); + return v8::FunctionTemplate::New(isolate(), callback, external, signature); } inline void Environment::SetMethod(v8::Local that, - const char* name, - v8::FunctionCallback callback) { - v8::Local function = - NewFunctionTemplate(callback)->GetFunction(); - // kInternalized strings are created in the old space. - const v8::NewStringType type = v8::NewStringType::kInternalized; - v8::Local name_string = - v8::String::NewFromUtf8(isolate(), name, type).ToLocalChecked(); - that->Set(name_string, function); - function->SetName(name_string); // NODE_SET_METHOD() compatibility. + const char* name, + v8::FunctionCallback callback) +{ + v8::Local function = NewFunctionTemplate(callback)->GetFunction(); + // kInternalized strings are created in the old space. + const v8::NewStringType type = v8::NewStringType::kInternalized; + v8::Local name_string = v8::String::NewFromUtf8(isolate(), name, type).ToLocalChecked(); + that->Set(name_string, function); + function->SetName(name_string); // NODE_SET_METHOD() compatibility. } inline void Environment::SetProtoMethod(v8::Local that, - const char* name, - v8::FunctionCallback callback) { - v8::Local signature = v8::Signature::New(isolate(), that); - v8::Local t = NewFunctionTemplate(callback, signature); - // kInternalized strings are created in the old space. - const v8::NewStringType type = v8::NewStringType::kInternalized; - v8::Local name_string = - v8::String::NewFromUtf8(isolate(), name, type).ToLocalChecked(); - that->PrototypeTemplate()->Set(name_string, t); - t->SetClassName(name_string); // NODE_SET_PROTOTYPE_METHOD() compatibility. + const char* name, + v8::FunctionCallback callback) +{ + v8::Local signature = v8::Signature::New(isolate(), that); + v8::Local t = NewFunctionTemplate(callback, signature); + // kInternalized strings are created in the old space. + const v8::NewStringType type = v8::NewStringType::kInternalized; + v8::Local name_string = v8::String::NewFromUtf8(isolate(), name, type).ToLocalChecked(); + that->PrototypeTemplate()->Set(name_string, t); + t->SetClassName(name_string); // NODE_SET_PROTOTYPE_METHOD() compatibility. } inline void Environment::SetTemplateMethod(v8::Local that, - const char* name, - v8::FunctionCallback callback) { - v8::Local t = NewFunctionTemplate(callback); - // kInternalized strings are created in the old space. - const v8::NewStringType type = v8::NewStringType::kInternalized; - v8::Local name_string = - v8::String::NewFromUtf8(isolate(), name, type).ToLocalChecked(); - that->Set(name_string, t); - t->SetClassName(name_string); // NODE_SET_METHOD() compatibility. + const char* name, + v8::FunctionCallback callback) +{ + v8::Local t = NewFunctionTemplate(callback); + // kInternalized strings are created in the old space. + const v8::NewStringType type = v8::NewStringType::kInternalized; + v8::Local name_string = v8::String::NewFromUtf8(isolate(), name, type).ToLocalChecked(); + that->Set(name_string, t); + t->SetClassName(name_string); // NODE_SET_METHOD() compatibility. } -inline v8::Local Environment::NewInternalFieldObject() { - v8::MaybeLocal m_obj = - generic_internal_field_template()->NewInstance(context()); - return m_obj.ToLocalChecked(); +inline v8::Local Environment::NewInternalFieldObject() +{ + v8::MaybeLocal m_obj = generic_internal_field_template()->NewInstance(context()); + return m_obj.ToLocalChecked(); } template -inline void Environment::CloseHandle(T* handle, OnCloseCallback callback) { - handle_cleanup_waiting_++; - static_assert(sizeof(T) >= sizeof(uv_handle_t), "T is a libuv handle"); - static_assert(offsetof(T, data) == offsetof(uv_handle_t, data), "T is a libuv handle"); - static_assert(offsetof(T, close_cb) == offsetof(uv_handle_t, close_cb), "T is a libuv handle"); - struct CloseData { - Environment* env; - OnCloseCallback callback; - void* original_data; - }; - handle->data = new CloseData{ this, callback, handle->data }; - uv_close(reinterpret_cast(handle), [](uv_handle_t* handle) { - std::unique_ptr data{ static_cast(handle->data) }; - data->env->handle_cleanup_waiting_--; - handle->data = data->original_data; - data->callback(reinterpret_cast(handle)); - }); +inline void Environment::CloseHandle(T* handle, OnCloseCallback callback) +{ + handle_cleanup_waiting_++; + static_assert(sizeof(T) >= sizeof(uv_handle_t), "T is a libuv handle"); + static_assert(offsetof(T, data) == offsetof(uv_handle_t, data), "T is a libuv handle"); + static_assert(offsetof(T, close_cb) == offsetof(uv_handle_t, close_cb), "T is a libuv handle"); + struct CloseData { + Environment* env; + OnCloseCallback callback; + void* original_data; + }; + handle->data = new CloseData { this, callback, handle->data }; + uv_close(reinterpret_cast(handle), [](uv_handle_t* handle) { + std::unique_ptr data { static_cast(handle->data) }; + data->env->handle_cleanup_waiting_--; + handle->data = data->original_data; + data->callback(reinterpret_cast(handle)); + }); } #define VP(PropertyName, StringValue) V(v8::Private, PropertyName, StringValue) #define VS(PropertyName, StringValue) V(v8::String, PropertyName, StringValue) -#define V(TypeName, PropertyName, StringValue) \ - inline \ - v8::Local Environment::IsolateData::PropertyName() const { \ - /* Strings are immutable so casting away const-ness here is okay. */ \ - return const_cast(this)->PropertyName ## _.Get(isolate()); \ - } - PER_ISOLATE_PRIVATE_SYMBOL_PROPERTIES(VP) - PER_ISOLATE_STRING_PROPERTIES(VS) +#define V(TypeName, PropertyName, StringValue) \ + inline v8::Local Environment::IsolateData::PropertyName() const \ + { \ + /* Strings are immutable so casting away const-ness here is okay. */ \ + return const_cast(this)->PropertyName##_.Get(isolate()); \ + } +PER_ISOLATE_PRIVATE_SYMBOL_PROPERTIES(VP) +PER_ISOLATE_STRING_PROPERTIES(VS) #undef V #undef VS #undef VP #define VP(PropertyName, StringValue) V(v8::Private, PropertyName, StringValue) #define VS(PropertyName, StringValue) V(v8::String, PropertyName, StringValue) -#define V(TypeName, PropertyName, StringValue) \ - inline v8::Local Environment::PropertyName() const { \ - return isolate_data()->PropertyName(); \ - } - PER_ISOLATE_PRIVATE_SYMBOL_PROPERTIES(VP) - PER_ISOLATE_STRING_PROPERTIES(VS) +#define V(TypeName, PropertyName, StringValue) \ + inline v8::Local Environment::PropertyName() const \ + { \ + return isolate_data()->PropertyName(); \ + } +PER_ISOLATE_PRIVATE_SYMBOL_PROPERTIES(VP) +PER_ISOLATE_STRING_PROPERTIES(VS) #undef V #undef VS #undef VP -#define V(PropertyName, TypeName) \ - inline v8::Local Environment::PropertyName() const { \ - return StrongPersistentToLocal(PropertyName ## _); \ - } \ - inline void Environment::set_ ## PropertyName(v8::Local value) { \ - PropertyName ## _.Reset(isolate(), value); \ - } - ENVIRONMENT_STRONG_PERSISTENT_PROPERTIES(V) +#define V(PropertyName, TypeName) \ + inline v8::Local Environment::PropertyName() const \ + { \ + return StrongPersistentToLocal(PropertyName##_); \ + } \ + inline void Environment::set_##PropertyName(v8::Local value) \ + { \ + PropertyName##_.Reset(isolate(), value); \ + } +ENVIRONMENT_STRONG_PERSISTENT_PROPERTIES(V) #undef V #undef ENVIRONMENT_STRONG_PERSISTENT_PROPERTIES #undef PER_ISOLATE_PRIVATE_SYMBOL_PROPERTIES #undef PER_ISOLATE_STRING_PROPERTIES -} // namespace node +} // namespace node -#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS +#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS -#endif // SRC_ENV_INL_H_ +#endif // SRC_ENV_INL_H_ diff --git a/node/src/env.cc b/node/src/env.cc index df599dac11..f04a46fd2c 100644 --- a/node/src/env.cc +++ b/node/src/env.cc @@ -25,58 +25,60 @@ using v8::Message; using v8::StackFrame; using v8::StackTrace; -void Environment::PrintSyncTrace() const { - if (!trace_sync_io_) - return; - - HandleScope handle_scope(isolate()); - Local stack = - StackTrace::CurrentStackTrace(isolate(), 10, StackTrace::kDetailed); - - fprintf(stderr, "(node:%d) WARNING: Detected use of sync API\n", getpid()); - - for (int i = 0; i < stack->GetFrameCount() - 1; i++) { - Local stack_frame = stack->GetFrame(i); - node::Utf8Value fn_name_s(isolate(), stack_frame->GetFunctionName()); - node::Utf8Value script_name(isolate(), stack_frame->GetScriptName()); - const int line_number = stack_frame->GetLineNumber(); - const int column = stack_frame->GetColumn(); - - if (stack_frame->IsEval()) { - if (stack_frame->GetScriptId() == Message::kNoScriptIdInfo) { - fprintf(stderr, " at [eval]:%i:%i\n", line_number, column); - } else { - fprintf(stderr, - " at [eval] (%s:%i:%i)\n", +void Environment::PrintSyncTrace() const +{ + if (!trace_sync_io_) + return; + + HandleScope handle_scope(isolate()); + Local stack = StackTrace::CurrentStackTrace(isolate(), 10, StackTrace::kDetailed); + + fprintf(stderr, "(node:%d) WARNING: Detected use of sync API\n", getpid()); + + for (int i = 0; i < stack->GetFrameCount() - 1; i++) { + Local stack_frame = stack->GetFrame(isolate(), i); + node::Utf8Value fn_name_s(isolate(), stack_frame->GetFunctionName()); + node::Utf8Value script_name(isolate(), stack_frame->GetScriptName()); + const int line_number = stack_frame->GetLineNumber(); + const int column = stack_frame->GetColumn(); + + if (stack_frame->IsEval()) { + if (stack_frame->GetScriptId() == Message::kNoScriptIdInfo) { + fprintf(stderr, " at [eval]:%i:%i\n", line_number, column); + } else { + fprintf(stderr, + " at [eval] (%s:%i:%i)\n", + *script_name, + line_number, + column); + } + break; + } + + if (fn_name_s.length() == 0) { + fprintf(stderr, " at %s:%i:%i\n", *script_name, line_number, column); + } else { + fprintf(stderr, + " at %s (%s:%i:%i)\n", + *fn_name_s, *script_name, line_number, column); - } - break; - } - - if (fn_name_s.length() == 0) { - fprintf(stderr, " at %s:%i:%i\n", *script_name, line_number, column); - } else { - fprintf(stderr, - " at %s (%s:%i:%i)\n", - *fn_name_s, - *script_name, - line_number, - column); + } } - } - fflush(stderr); + fflush(stderr); } -void Environment::AddCleanupHook(void(*fn)(void*), void* arg) { +void Environment::AddCleanupHook(void (*fn)(void*), void* arg) +{ CleanupHookCallback* cb = new CleanupHookCallback(fn, arg, cleanup_hook_counter_++); if (!cleanup_hooks_) cleanup_hooks_ = new std::vector(); cleanup_hooks_->push_back(cb); } -void Environment::RemoveCleanupHook(void(*fn)(void*), void* arg) { +void Environment::RemoveCleanupHook(void (*fn)(void*), void* arg) +{ for (size_t i = 0; i < cleanup_hooks_->size(); ++i) { CleanupHookCallback* hook = cleanup_hooks_->at(i); if (hook->fn_ == fn && hook->arg_ == arg) { @@ -87,7 +89,8 @@ void Environment::RemoveCleanupHook(void(*fn)(void*), void* arg) { } } -void Environment::InitEnv() { +void Environment::InitEnv() +{ debugger_agent_ = new debugger::Agent(this); #if HAVE_INSPECTOR inspector_agent_ = new debugger::Agent(this); @@ -97,7 +100,14 @@ void Environment::InitEnv() { destroy_ids_list_->reserve(512); } -void Environment::CleanEnv() { +void CleanNodeEnv(void* env) +{ + Environment* envPtr = (Environment*)env; + envPtr->CleanEnv(); +} + +void Environment::CleanEnv() +{ CleanupHandles(); delete debugger_agent_; @@ -138,32 +148,37 @@ void Environment::CleanEnv() { } } -void AddLiveSet(intptr_t obj) { - net::ActivatingObjCheck::inst()->add((intptr_t)obj); -} - -void RemoveLiveSet(intptr_t obj) { +void AddLiveSet(intptr_t obj) +{ + net::ActivatingObjCheck::inst()->add((intptr_t)obj); +} + +void RemoveLiveSet(intptr_t obj) +{ net::ActivatingObjCheck::inst()->remove((intptr_t)obj); } -bool IsLiveObj(intptr_t obj) { +bool IsLiveObj(intptr_t obj) +{ return net::ActivatingObjCheck::inst()->isActivating((intptr_t)obj); } #ifndef MINIBLINK_NOT_IMPLEMENTED -void BlinkMicrotaskSuppressionEnterFunc(Environment* self) { +void BlinkMicrotaskSuppressionEnterFunc(Environment* self) +{ if (!self->blink_microtask_suppression_handle_) self->blink_microtask_suppression_handle_ = new std::list(); std::list* handleList = (std::list*)self->blink_microtask_suppression_handle_; handleList->push_back(nodeBlinkMicrotaskSuppressionEnter(self->isolate())); } -void BlinkMicrotaskSuppressionLeaveFunc(Environment* self) { +void BlinkMicrotaskSuppressionLeaveFunc(Environment* self) +{ std::list* handleList = (std::list*)self->blink_microtask_suppression_handle_; BlinkMicrotaskSuppressionHandle handle = (handleList->back()); handleList->pop_back(); - + if (0 == handleList->size()) { delete handleList; self->blink_microtask_suppression_handle_ = nullptr; @@ -172,10 +187,11 @@ void BlinkMicrotaskSuppressionLeaveFunc(Environment* self) { nodeBlinkMicrotaskSuppressionLeave(handle); } -void Environment::InitBlinkMicrotaskSuppression() { +void Environment::InitBlinkMicrotaskSuppression() +{ BlinkMicrotaskSuppressionEnter = BlinkMicrotaskSuppressionEnterFunc; BlinkMicrotaskSuppressionLeave = BlinkMicrotaskSuppressionLeaveFunc; } #endif -} // namespace node +} // namespace node diff --git a/node/src/env.h b/node/src/env.h index 97f04d5ddf..0db0164855 100644 --- a/node/src/env.h +++ b/node/src/env.h @@ -54,682 +54,690 @@ namespace node { // Private symbols are per-isolate primitives but Environment proxies them // for the sake of convenience. Strings should be ASCII-only and have a // "node:" prefix to avoid name clashes with third-party code. -#define PER_ISOLATE_PRIVATE_SYMBOL_PROPERTIES(V) \ - V(alpn_buffer_private_symbol, "node:alpnBuffer") \ - V(arrow_message_private_symbol, "node:arrowMessage") \ - V(contextify_context_private_symbol, "node:contextify:context") \ - V(contextify_global_private_symbol, "node:contextify:global") \ - V(decorated_private_symbol, "node:decorated") \ - V(npn_buffer_private_symbol, "node:npnBuffer") \ - V(processed_private_symbol, "node:processed") \ - V(napi_env, "node:napi:env") \ - V(napi_wrapper, "node:napi:wrapper") \ - V(selected_npn_buffer_private_symbol, "node:selectedNpnBuffer") \ +#define PER_ISOLATE_PRIVATE_SYMBOL_PROPERTIES(V) \ + V(alpn_buffer_private_symbol, "node:alpnBuffer") \ + V(arrow_message_private_symbol, "node:arrowMessage") \ + V(contextify_context_private_symbol, "node:contextify:context") \ + V(contextify_global_private_symbol, "node:contextify:global") \ + V(decorated_private_symbol, "node:decorated") \ + V(npn_buffer_private_symbol, "node:npnBuffer") \ + V(processed_private_symbol, "node:processed") \ + V(napi_env, "node:napi:env") \ + V(napi_wrapper, "node:napi:wrapper") \ + V(selected_npn_buffer_private_symbol, "node:selectedNpnBuffer") // Strings are per-isolate primitives but Environment proxies them // for the sake of convenience. Strings should be ASCII-only. #define PER_ISOLATE_STRING_PROPERTIES(V) \ - V(address_string, "address") \ - V(args_string, "args") \ - V(async, "async") \ - V(async_queue_string, "_asyncQueue") \ - V(buffer_string, "buffer") \ - V(bytes_string, "bytes") \ - V(bytes_parsed_string, "bytesParsed") \ - V(bytes_read_string, "bytesRead") \ - V(cached_data_string, "cachedData") \ - V(cached_data_produced_string, "cachedDataProduced") \ - V(cached_data_rejected_string, "cachedDataRejected") \ - V(callback_string, "callback") \ - V(change_string, "change") \ - V(oncertcb_string, "oncertcb") \ - V(onclose_string, "_onclose") \ - V(code_string, "code") \ - V(cwd_string, "cwd") \ - V(dest_string, "dest") \ - V(detached_string, "detached") \ - V(disposed_string, "_disposed") \ - V(domain_string, "domain") \ - V(emitting_top_level_domain_error_string, "_emittingTopLevelDomainError") \ - V(exchange_string, "exchange") \ - V(idle_string, "idle") \ - V(irq_string, "irq") \ - V(encoding_string, "encoding") \ - V(enter_string, "enter") \ - V(env_pairs_string, "envPairs") \ - V(env_string, "env") \ - V(errno_string, "errno") \ - V(error_string, "error") \ - V(events_string, "_events") \ - V(exiting_string, "_exiting") \ - V(exit_code_string, "exitCode") \ - V(exit_string, "exit") \ - V(expire_string, "expire") \ - V(exponent_string, "exponent") \ - V(exports_string, "exports") \ - V(ext_key_usage_string, "ext_key_usage") \ - V(external_stream_string, "_externalStream") \ - V(family_string, "family") \ - V(fatal_exception_string, "_fatalException") \ - V(fd_string, "fd") \ - V(file_string, "file") \ - V(fingerprint_string, "fingerprint") \ - V(flags_string, "flags") \ - V(gid_string, "gid") \ - V(handle_string, "handle") \ - V(heap_total_string, "heapTotal") \ - V(heap_used_string, "heapUsed") \ - V(homedir_string, "homedir") \ - V(hostmaster_string, "hostmaster") \ - V(ignore_string, "ignore") \ - V(immediate_callback_string, "_immediateCallback") \ - V(infoaccess_string, "infoAccess") \ - V(inherit_string, "inherit") \ - V(input_string, "input") \ - V(internal_string, "internal") \ - V(ipv4_string, "IPv4") \ - V(ipv6_string, "IPv6") \ - V(isalive_string, "isAlive") \ - V(isclosing_string, "isClosing") \ - V(issuer_string, "issuer") \ - V(issuercert_string, "issuerCertificate") \ - V(kill_signal_string, "killSignal") \ - V(mac_string, "mac") \ - V(max_buffer_string, "maxBuffer") \ - V(message_string, "message") \ - V(minttl_string, "minttl") \ - V(model_string, "model") \ - V(modulus_string, "modulus") \ - V(name_string, "name") \ - V(netmask_string, "netmask") \ - V(nice_string, "nice") \ - V(nsname_string, "nsname") \ - V(ocsp_request_string, "OCSPRequest") \ - V(onchange_string, "onchange") \ - V(onclienthello_string, "onclienthello") \ - V(oncomplete_string, "oncomplete") \ - V(onconnection_string, "onconnection") \ - V(ondone_string, "ondone") \ - V(onerror_string, "onerror") \ - V(onexit_string, "onexit") \ - V(onhandshakedone_string, "onhandshakedone") \ - V(onhandshakestart_string, "onhandshakestart") \ - V(onmessage_string, "onmessage") \ - V(onnewsession_string, "onnewsession") \ - V(onnewsessiondone_string, "onnewsessiondone") \ - V(onocspresponse_string, "onocspresponse") \ - V(onread_string, "onread") \ - V(onreadstart_string, "onreadstart") \ - V(onreadstop_string, "onreadstop") \ - V(onselect_string, "onselect") \ - V(onshutdown_string, "onshutdown") \ - V(onsignal_string, "onsignal") \ - V(onstop_string, "onstop") \ - V(onwrite_string, "onwrite") \ - V(output_string, "output") \ - V(order_string, "order") \ - V(owner_string, "owner") \ - V(parse_error_string, "Parse Error") \ - V(path_string, "path") \ - V(pbkdf2_error_string, "PBKDF2 Error") \ - V(pid_string, "pid") \ - V(pipe_string, "pipe") \ - V(port_string, "port") \ - V(preference_string, "preference") \ - V(priority_string, "priority") \ - V(produce_cached_data_string, "produceCachedData") \ - V(raw_string, "raw") \ - V(readable_string, "readable") \ - V(received_shutdown_string, "receivedShutdown") \ - V(refresh_string, "refresh") \ - V(regexp_string, "regexp") \ - V(rename_string, "rename") \ - V(replacement_string, "replacement") \ - V(retry_string, "retry") \ - V(rss_string, "rss") \ - V(serial_string, "serial") \ - V(scopeid_string, "scopeid") \ - V(sent_shutdown_string, "sentShutdown") \ - V(serial_number_string, "serialNumber") \ - V(service_string, "service") \ - V(servername_string, "servername") \ - V(session_id_string, "sessionId") \ - V(shell_string, "shell") \ - V(signal_string, "signal") \ - V(size_string, "size") \ - V(sni_context_err_string, "Invalid SNI context") \ - V(sni_context_string, "sni_context") \ - V(speed_string, "speed") \ - V(stack_string, "stack") \ - V(status_string, "status") \ - V(stdio_string, "stdio") \ - V(subject_string, "subject") \ - V(subjectaltname_string, "subjectaltname") \ - V(sys_string, "sys") \ - V(syscall_string, "syscall") \ - V(tick_callback_string, "_tickCallback") \ - V(tick_domain_cb_string, "_tickDomainCallback") \ - V(ticketkeycallback_string, "onticketkeycallback") \ - V(timeout_string, "timeout") \ - V(times_string, "times") \ - V(tls_ticket_string, "tlsTicket") \ - V(type_string, "type") \ - V(uid_string, "uid") \ - V(unknown_string, "") \ - V(user_string, "user") \ - V(username_string, "username") \ - V(valid_from_string, "valid_from") \ - V(valid_to_string, "valid_to") \ - V(verify_error_string, "verifyError") \ - V(version_string, "version") \ - V(weight_string, "weight") \ - V(windows_verbatim_arguments_string, "windowsVerbatimArguments") \ - V(wrap_string, "wrap") \ - V(writable_string, "writable") \ - V(write_queue_size_string, "writeQueueSize") \ - V(x_forwarded_string, "x-forwarded-for") \ - V(zero_return_string, "ZERO_RETURN") \ - -#define ENVIRONMENT_STRONG_PERSISTENT_PROPERTIES(V) \ - V(as_external, v8::External) \ - V(async_hooks_destroy_function, v8::Function) \ - V(async_hooks_init_function, v8::Function) \ - V(async_hooks_post_function, v8::Function) \ - V(async_hooks_pre_function, v8::Function) \ - V(binding_cache_object, v8::Object) \ - V(buffer_constructor_function, v8::Function) \ - V(buffer_prototype_object, v8::Object) \ - V(context, v8::Context) \ - V(domain_array, v8::Array) \ - V(domains_stack_array, v8::Array) \ - V(fs_stats_constructor_function, v8::Function) \ - V(generic_internal_field_template, v8::ObjectTemplate) \ - V(jsstream_constructor_template, v8::FunctionTemplate) \ - V(module_load_list_array, v8::Array) \ - V(pipe_constructor_template, v8::FunctionTemplate) \ - V(process_object, v8::Object) \ - V(promise_reject_function, v8::Function) \ - V(push_values_to_array_function, v8::Function) \ - V(script_context_constructor_template, v8::FunctionTemplate) \ - V(script_data_constructor_function, v8::Function) \ - V(secure_context_constructor_template, v8::FunctionTemplate) \ - V(tcp_constructor_template, v8::FunctionTemplate) \ - V(tick_callback_function, v8::Function) \ - V(tls_wrap_constructor_function, v8::Function) \ - V(tls_wrap_constructor_template, v8::FunctionTemplate) \ - V(tty_constructor_template, v8::FunctionTemplate) \ - V(udp_constructor_function, v8::Function) \ - V(write_wrap_constructor_function, v8::Function) \ - V(url_constructor_function, v8::Function) \ + V(address_string, "address") \ + V(args_string, "args") \ + V(async, "async") \ + V(async_queue_string, "_asyncQueue") \ + V(buffer_string, "buffer") \ + V(bytes_string, "bytes") \ + V(bytes_parsed_string, "bytesParsed") \ + V(bytes_read_string, "bytesRead") \ + V(cached_data_string, "cachedData") \ + V(cached_data_produced_string, "cachedDataProduced") \ + V(cached_data_rejected_string, "cachedDataRejected") \ + V(callback_string, "callback") \ + V(change_string, "change") \ + V(oncertcb_string, "oncertcb") \ + V(onclose_string, "_onclose") \ + V(code_string, "code") \ + V(cwd_string, "cwd") \ + V(dest_string, "dest") \ + V(detached_string, "detached") \ + V(disposed_string, "_disposed") \ + V(domain_string, "domain") \ + V(emitting_top_level_domain_error_string, "_emittingTopLevelDomainError") \ + V(exchange_string, "exchange") \ + V(idle_string, "idle") \ + V(irq_string, "irq") \ + V(encoding_string, "encoding") \ + V(enter_string, "enter") \ + V(env_pairs_string, "envPairs") \ + V(env_string, "env") \ + V(errno_string, "errno") \ + V(error_string, "error") \ + V(events_string, "_events") \ + V(exiting_string, "_exiting") \ + V(exit_code_string, "exitCode") \ + V(exit_string, "exit") \ + V(expire_string, "expire") \ + V(exponent_string, "exponent") \ + V(exports_string, "exports") \ + V(ext_key_usage_string, "ext_key_usage") \ + V(external_stream_string, "_externalStream") \ + V(family_string, "family") \ + V(fatal_exception_string, "_fatalException") \ + V(fd_string, "fd") \ + V(file_string, "file") \ + V(fingerprint_string, "fingerprint") \ + V(flags_string, "flags") \ + V(gid_string, "gid") \ + V(handle_string, "handle") \ + V(heap_total_string, "heapTotal") \ + V(heap_used_string, "heapUsed") \ + V(homedir_string, "homedir") \ + V(hostmaster_string, "hostmaster") \ + V(ignore_string, "ignore") \ + V(immediate_callback_string, "_immediateCallback") \ + V(infoaccess_string, "infoAccess") \ + V(inherit_string, "inherit") \ + V(input_string, "input") \ + V(internal_string, "internal") \ + V(ipv4_string, "IPv4") \ + V(ipv6_string, "IPv6") \ + V(isalive_string, "isAlive") \ + V(isclosing_string, "isClosing") \ + V(issuer_string, "issuer") \ + V(issuercert_string, "issuerCertificate") \ + V(kill_signal_string, "killSignal") \ + V(mac_string, "mac") \ + V(max_buffer_string, "maxBuffer") \ + V(message_string, "message") \ + V(minttl_string, "minttl") \ + V(model_string, "model") \ + V(modulus_string, "modulus") \ + V(name_string, "name") \ + V(netmask_string, "netmask") \ + V(nice_string, "nice") \ + V(nsname_string, "nsname") \ + V(ocsp_request_string, "OCSPRequest") \ + V(onchange_string, "onchange") \ + V(onclienthello_string, "onclienthello") \ + V(oncomplete_string, "oncomplete") \ + V(onconnection_string, "onconnection") \ + V(ondone_string, "ondone") \ + V(onerror_string, "onerror") \ + V(onexit_string, "onexit") \ + V(onhandshakedone_string, "onhandshakedone") \ + V(onhandshakestart_string, "onhandshakestart") \ + V(onmessage_string, "onmessage") \ + V(onnewsession_string, "onnewsession") \ + V(onnewsessiondone_string, "onnewsessiondone") \ + V(onocspresponse_string, "onocspresponse") \ + V(onread_string, "onread") \ + V(onreadstart_string, "onreadstart") \ + V(onreadstop_string, "onreadstop") \ + V(onselect_string, "onselect") \ + V(onshutdown_string, "onshutdown") \ + V(onsignal_string, "onsignal") \ + V(onstop_string, "onstop") \ + V(onwrite_string, "onwrite") \ + V(output_string, "output") \ + V(order_string, "order") \ + V(owner_string, "owner") \ + V(parse_error_string, "Parse Error") \ + V(path_string, "path") \ + V(pbkdf2_error_string, "PBKDF2 Error") \ + V(pid_string, "pid") \ + V(pipe_string, "pipe") \ + V(port_string, "port") \ + V(preference_string, "preference") \ + V(priority_string, "priority") \ + V(produce_cached_data_string, "produceCachedData") \ + V(raw_string, "raw") \ + V(readable_string, "readable") \ + V(received_shutdown_string, "receivedShutdown") \ + V(refresh_string, "refresh") \ + V(regexp_string, "regexp") \ + V(rename_string, "rename") \ + V(replacement_string, "replacement") \ + V(retry_string, "retry") \ + V(rss_string, "rss") \ + V(serial_string, "serial") \ + V(scopeid_string, "scopeid") \ + V(sent_shutdown_string, "sentShutdown") \ + V(serial_number_string, "serialNumber") \ + V(service_string, "service") \ + V(servername_string, "servername") \ + V(session_id_string, "sessionId") \ + V(shell_string, "shell") \ + V(signal_string, "signal") \ + V(size_string, "size") \ + V(sni_context_err_string, "Invalid SNI context") \ + V(sni_context_string, "sni_context") \ + V(speed_string, "speed") \ + V(stack_string, "stack") \ + V(status_string, "status") \ + V(stdio_string, "stdio") \ + V(subject_string, "subject") \ + V(subjectaltname_string, "subjectaltname") \ + V(sys_string, "sys") \ + V(syscall_string, "syscall") \ + V(tick_callback_string, "_tickCallback") \ + V(tick_domain_cb_string, "_tickDomainCallback") \ + V(ticketkeycallback_string, "onticketkeycallback") \ + V(timeout_string, "timeout") \ + V(times_string, "times") \ + V(tls_ticket_string, "tlsTicket") \ + V(type_string, "type") \ + V(uid_string, "uid") \ + V(unknown_string, "") \ + V(user_string, "user") \ + V(username_string, "username") \ + V(valid_from_string, "valid_from") \ + V(valid_to_string, "valid_to") \ + V(verify_error_string, "verifyError") \ + V(version_string, "version") \ + V(weight_string, "weight") \ + V(windows_verbatim_arguments_string, "windowsVerbatimArguments") \ + V(wrap_string, "wrap") \ + V(writable_string, "writable") \ + V(write_queue_size_string, "writeQueueSize") \ + V(x_forwarded_string, "x-forwarded-for") \ + V(zero_return_string, "ZERO_RETURN") + +#define ENVIRONMENT_STRONG_PERSISTENT_PROPERTIES(V) \ + V(as_external, v8::External) \ + V(async_hooks_destroy_function, v8::Function) \ + V(async_hooks_init_function, v8::Function) \ + V(async_hooks_post_function, v8::Function) \ + V(async_hooks_pre_function, v8::Function) \ + V(binding_cache_object, v8::Object) \ + V(buffer_constructor_function, v8::Function) \ + V(buffer_prototype_object, v8::Object) \ + V(context, v8::Context) \ + V(domain_array, v8::Array) \ + V(domains_stack_array, v8::Array) \ + V(fs_stats_constructor_function, v8::Function) \ + V(generic_internal_field_template, v8::ObjectTemplate) \ + V(jsstream_constructor_template, v8::FunctionTemplate) \ + V(module_load_list_array, v8::Array) \ + V(pipe_constructor_template, v8::FunctionTemplate) \ + V(process_object, v8::Object) \ + V(promise_reject_function, v8::Function) \ + V(push_values_to_array_function, v8::Function) \ + V(script_context_constructor_template, v8::FunctionTemplate) \ + V(script_data_constructor_function, v8::Function) \ + V(secure_context_constructor_template, v8::FunctionTemplate) \ + V(tcp_constructor_template, v8::FunctionTemplate) \ + V(tick_callback_function, v8::Function) \ + V(tls_wrap_constructor_function, v8::Function) \ + V(tls_wrap_constructor_template, v8::FunctionTemplate) \ + V(tty_constructor_template, v8::FunctionTemplate) \ + V(udp_constructor_function, v8::Function) \ + V(write_wrap_constructor_function, v8::Function) \ + V(url_constructor_function, v8::Function) class Environment; struct node_ares_task { - Environment* env; - ares_socket_t sock; - uv_poll_t poll_watcher; - RB_ENTRY(node_ares_task) node; + Environment* env; + ares_socket_t sock; + uv_poll_t poll_watcher; + RB_ENTRY(node_ares_task) + node; }; RB_HEAD(node_ares_task_list, node_ares_task); class Environment { - public: - class AsyncHooks { - public: - inline uint32_t* fields(); - inline int fields_count() const; - inline bool callbacks_enabled(); - inline void set_enable_callbacks(uint32_t flag); - - private: - friend class Environment; // So we can call the constructor. - inline AsyncHooks(); - - enum Fields { - // Set this to not zero if the init hook should be called. - kEnableCallbacks, - kFieldsCount +public: + class AsyncHooks { + public: + inline uint32_t* fields(); + inline int fields_count() const; + inline bool callbacks_enabled(); + inline void set_enable_callbacks(uint32_t flag); + + private: + friend class Environment; // So we can call the constructor. + inline AsyncHooks(); + + enum Fields { + // Set this to not zero if the init hook should be called. + kEnableCallbacks, + kFieldsCount + }; + + uint32_t fields_[kFieldsCount]; + + DISALLOW_COPY_AND_ASSIGN(AsyncHooks); }; + class FileSystemHooks { + public: + void access(); + void close(); + virtual void open() = 0; + void read(); + void fdatasync(); + void fsync(); + void rename(); + void ftruncate(); + void rmdir(); + void mkdir(); + void readdir(); + void internalModuleReadFile(); + virtual bool internalModuleStat(const char* path, int* rc) = 0; + void stat(); + void lstat(); + void fstat(); + void link(); + void symlink(); + void readlink(); + void unlink(); + void writeBuffer(); + void writeBuffers(); + void writeString(); + void realpath(); + + void chmod(); + void fchmod(); + + void chown(); + void fchow(); + + void utimes(); + void futimes(); + + private: + friend class Environment; // So we can call the constructor. + }; + class AsyncCallbackScope { + public: + explicit AsyncCallbackScope(Environment* env); + ~AsyncCallbackScope(); + + inline bool in_makecallback(); + + private: + Environment* env_; - uint32_t fields_[kFieldsCount]; - - DISALLOW_COPY_AND_ASSIGN(AsyncHooks); - }; - class FileSystemHooks { - public: - void access(); - void close(); - virtual void open() = 0; - void read(); - void fdatasync(); - void fsync(); - void rename(); - void ftruncate(); - void rmdir(); - void mkdir(); - void readdir(); - void internalModuleReadFile(); - virtual bool internalModuleStat(const char* path, int *rc) = 0; - void stat(); - void lstat(); - void fstat(); - void link(); - void symlink(); - void readlink(); - void unlink(); - void writeBuffer(); - void writeBuffers(); - void writeString(); - void realpath(); - - void chmod(); - void fchmod(); - - void chown(); - void fchow(); - - void utimes(); - void futimes(); - - private: - friend class Environment; // So we can call the constructor. - }; - class AsyncCallbackScope { - public: - explicit AsyncCallbackScope(Environment* env); - ~AsyncCallbackScope(); - - inline bool in_makecallback(); - - private: - Environment* env_; - - DISALLOW_COPY_AND_ASSIGN(AsyncCallbackScope); - }; - - class DomainFlag { - public: - inline uint32_t* fields(); - inline int fields_count() const; - inline uint32_t count() const; - - private: - friend class Environment; // So we can call the constructor. - inline DomainFlag(); - - enum Fields { - kCount, - kFieldsCount + DISALLOW_COPY_AND_ASSIGN(AsyncCallbackScope); }; - uint32_t fields_[kFieldsCount]; + class DomainFlag { + public: + inline uint32_t* fields(); + inline int fields_count() const; + inline uint32_t count() const; - DISALLOW_COPY_AND_ASSIGN(DomainFlag); - }; + private: + friend class Environment; // So we can call the constructor. + inline DomainFlag(); - class TickInfo { - public: - inline uint32_t* fields(); - inline int fields_count() const; - inline uint32_t index() const; - inline uint32_t length() const; - inline void set_index(uint32_t value); + enum Fields { + kCount, + kFieldsCount + }; - private: - friend class Environment; // So we can call the constructor. - inline TickInfo(); + uint32_t fields_[kFieldsCount]; - enum Fields { - kIndex, - kLength, - kFieldsCount + DISALLOW_COPY_AND_ASSIGN(DomainFlag); }; - uint32_t fields_[kFieldsCount]; + class TickInfo { + public: + inline uint32_t* fields(); + inline int fields_count() const; + inline uint32_t index() const; + inline uint32_t length() const; + inline void set_index(uint32_t value); - DISALLOW_COPY_AND_ASSIGN(TickInfo); - }; + private: + friend class Environment; // So we can call the constructor. + inline TickInfo(); - class ArrayBufferAllocatorInfo { - public: - inline uint32_t* fields(); - inline int fields_count() const; - inline bool no_zero_fill() const; - inline void reset_fill_flag(); + enum Fields { + kIndex, + kLength, + kFieldsCount + }; - private: - friend class Environment; // So we can call the constructor. - inline ArrayBufferAllocatorInfo(); + uint32_t fields_[kFieldsCount]; - enum Fields { - kNoZeroFill, - kFieldsCount + DISALLOW_COPY_AND_ASSIGN(TickInfo); }; - uint32_t fields_[kFieldsCount]; + class ArrayBufferAllocatorInfo { + public: + inline uint32_t* fields(); + inline int fields_count() const; + inline bool no_zero_fill() const; + inline void reset_fill_flag(); - DISALLOW_COPY_AND_ASSIGN(ArrayBufferAllocatorInfo); - }; + private: + friend class Environment; // So we can call the constructor. + inline ArrayBufferAllocatorInfo(); - typedef void (*HandleCleanupCb)(Environment* env, - uv_handle_t* handle, - void* arg); + enum Fields { + kNoZeroFill, + kFieldsCount + }; - class HandleCleanup { - private: - friend class Environment; + uint32_t fields_[kFieldsCount]; - HandleCleanup(uv_handle_t* handle, HandleCleanupCb cb, void* arg) - : handle_(handle), - cb_(cb), - arg_(arg) { - } + DISALLOW_COPY_AND_ASSIGN(ArrayBufferAllocatorInfo); + }; - uv_handle_t* handle_; - HandleCleanupCb cb_; - void* arg_; - ListNode handle_cleanup_queue_; - }; + typedef void (*HandleCleanupCb)(Environment* env, + uv_handle_t* handle, + void* arg); + + class HandleCleanup { + private: + friend class Environment; + + HandleCleanup(uv_handle_t* handle, HandleCleanupCb cb, void* arg) + : handle_(handle) + , cb_(cb) + , arg_(arg) + { + } + + uv_handle_t* handle_; + HandleCleanupCb cb_; + void* arg_; + ListNode handle_cleanup_queue_; + }; #ifndef MINIBLINK_NOT_IMPLEMENTED - typedef void* MicrotaskSuppressionHandle; - typedef void (* FN_BlinkMicrotaskSuppressionEnter)(Environment* self); - FN_BlinkMicrotaskSuppressionEnter BlinkMicrotaskSuppressionEnter; - typedef void (* FN_BlinkMicrotaskSuppressionLeave)(Environment* self); - FN_BlinkMicrotaskSuppressionLeave BlinkMicrotaskSuppressionLeave; - void InitBlinkMicrotaskSuppression(); + typedef void* MicrotaskSuppressionHandle; + typedef void (*FN_BlinkMicrotaskSuppressionEnter)(Environment* self); + FN_BlinkMicrotaskSuppressionEnter BlinkMicrotaskSuppressionEnter; + typedef void (*FN_BlinkMicrotaskSuppressionLeave)(Environment* self); + FN_BlinkMicrotaskSuppressionLeave BlinkMicrotaskSuppressionLeave; + void InitBlinkMicrotaskSuppression(); #endif - void AddCleanupHook(void(*fn)(void*), void* arg); - void RemoveCleanupHook(void(*fn)(void*), void* arg); - struct CleanupHookCallback; - std::vector* get_cleanup_hooks() { return cleanup_hooks_; } - - void InitEnv(); - void CleanEnv(); - - static inline Environment* GetCurrent(v8::Isolate* isolate); - static inline Environment* GetCurrent(v8::Local context); - static inline Environment* GetCurrent( - const v8::FunctionCallbackInfo& info); - - template - static inline Environment* GetCurrent( - const v8::PropertyCallbackInfo& info); - - // See CreateEnvironment() in src/node.cc. - static inline Environment* New(v8::Local context, - uv_loop_t* loop); - inline void CleanupHandles(); - inline void Dispose(); - - template - inline void CloseHandle(T* handle, OnCloseCallback callback); - - void AssignToContext(v8::Local context); - - inline v8::Isolate* isolate() const; - inline uv_loop_t* event_loop() const; - inline bool async_wrap_callbacks_enabled() const; - inline bool in_domain() const; - inline uint32_t watched_providers() const; - - static inline Environment* from_immediate_check_handle(uv_check_t* handle); - static inline Environment* from_destroy_ids_idle_handle(uv_idle_t* handle); - inline uv_check_t* immediate_check_handle(); - inline uv_idle_t* immediate_idle_handle(); - inline uv_idle_t* destroy_ids_idle_handle(); - - static inline Environment* from_idle_prepare_handle(uv_prepare_t* handle); - inline uv_prepare_t* idle_prepare_handle(); - - static inline Environment* from_idle_check_handle(uv_check_t* handle); - inline uv_check_t* idle_check_handle(); - - // Register clean-up cb to be called on env->Dispose() - inline void RegisterHandleCleanup(uv_handle_t* handle, - HandleCleanupCb cb, - void *arg); - inline void FinishHandleCleanup(uv_handle_t* handle); - - inline AsyncHooks* async_hooks(); - inline FileSystemHooks* file_system_hooks(); - inline void set_is_blink_core(); - inline bool is_blink_core() const; - inline void file_system_hooks(FileSystemHooks*); - inline DomainFlag* domain_flag(); - inline TickInfo* tick_info(); - inline ArrayBufferAllocatorInfo* array_buffer_allocator_info(); - inline uint64_t timer_base() const; - - static inline Environment* from_cares_timer_handle(uv_timer_t* handle); - inline uv_timer_t* cares_timer_handle(); - inline ares_channel cares_channel(); - inline ares_channel* cares_channel_ptr(); - inline node_ares_task_list* cares_task_list(); - - inline bool using_domains() const; - inline void set_using_domains(bool value); - - inline bool printed_error() const; - inline void set_printed_error(bool value); - - void PrintSyncTrace() const; - inline void set_trace_sync_io(bool value); - - inline int64_t get_async_wrap_uid(); - - // List of id's that have been destroyed and need the destroy() cb called. - inline std::vector* destroy_ids_list(); - - inline uint32_t* heap_statistics_buffer() const; - inline void set_heap_statistics_buffer(uint32_t* pointer); - - inline uint32_t* heap_space_statistics_buffer() const; - inline void set_heap_space_statistics_buffer(uint32_t* pointer); - - inline char* http_parser_buffer() const; - inline void set_http_parser_buffer(char* buffer); - - inline void ThrowError(const char* errmsg); - inline void ThrowTypeError(const char* errmsg); - inline void ThrowRangeError(const char* errmsg); - inline void ThrowErrnoException(int errorno, - const char* syscall = nullptr, - const char* message = nullptr, - const char* path = nullptr); - inline void ThrowUVException(int errorno, - const char* syscall = nullptr, - const char* message = nullptr, - const char* path = nullptr, - const char* dest = nullptr); - - inline v8::Local - NewFunctionTemplate(v8::FunctionCallback callback, - v8::Local signature = - v8::Local()); - - // Convenience methods for NewFunctionTemplate(). - inline void SetMethod(v8::Local that, - const char* name, - v8::FunctionCallback callback); - inline void SetProtoMethod(v8::Local that, - const char* name, - v8::FunctionCallback callback); - inline void SetTemplateMethod(v8::Local that, - const char* name, - v8::FunctionCallback callback); - - inline v8::Local NewInternalFieldObject(); - - // Strings and private symbols are shared across shared contexts - // The getters simply proxy to the per-isolate primitive. + void AddCleanupHook(void (*fn)(void*), void* arg); + void RemoveCleanupHook(void (*fn)(void*), void* arg); + struct CleanupHookCallback; + std::vector* get_cleanup_hooks() { return cleanup_hooks_; } + + void InitEnv(); + void CleanEnv(); + + static inline Environment* GetCurrent(v8::Isolate* isolate); + static inline Environment* GetCurrent(v8::Local context); + static inline Environment* GetCurrent( + const v8::FunctionCallbackInfo& info); + + template + static inline Environment* GetCurrent( + const v8::PropertyCallbackInfo& info); + + // See CreateEnvironment() in src/node.cc. + static inline Environment* New(v8::Local context, + uv_loop_t* loop); + inline void CleanupHandles(); + inline void Dispose(); + + template + inline void CloseHandle(T* handle, OnCloseCallback callback); + + void AssignToContext(v8::Local context); + + inline v8::Isolate* isolate() const; + inline uv_loop_t* event_loop() const; + inline bool async_wrap_callbacks_enabled() const; + inline bool in_domain() const; + inline uint32_t watched_providers() const; + + static inline Environment* from_immediate_check_handle(uv_check_t* handle); + static inline Environment* from_destroy_ids_idle_handle(uv_idle_t* handle); + inline uv_check_t* immediate_check_handle(); + inline uv_idle_t* immediate_idle_handle(); + inline uv_idle_t* destroy_ids_idle_handle(); + + static inline Environment* from_idle_prepare_handle(uv_prepare_t* handle); + inline uv_prepare_t* idle_prepare_handle(); + + static inline Environment* from_idle_check_handle(uv_check_t* handle); + inline uv_check_t* idle_check_handle(); + + // Register clean-up cb to be called on env->Dispose() + inline void RegisterHandleCleanup(uv_handle_t* handle, + HandleCleanupCb cb, + void* arg); + inline void FinishHandleCleanup(uv_handle_t* handle); + + inline AsyncHooks* async_hooks(); + inline FileSystemHooks* file_system_hooks(); + inline void set_is_blink_core(); + inline bool is_blink_core() const; + inline void file_system_hooks(FileSystemHooks*); + inline DomainFlag* domain_flag(); + inline TickInfo* tick_info(); + inline ArrayBufferAllocatorInfo* array_buffer_allocator_info(); + inline uint64_t timer_base() const; + + static inline Environment* from_cares_timer_handle(uv_timer_t* handle); + inline uv_timer_t* cares_timer_handle(); + inline ares_channel cares_channel(); + inline ares_channel* cares_channel_ptr(); + inline node_ares_task_list* cares_task_list(); + + inline bool using_domains() const; + inline void set_using_domains(bool value); + + inline bool printed_error() const; + inline void set_printed_error(bool value); + + void PrintSyncTrace() const; + inline void set_trace_sync_io(bool value); + + inline int64_t get_async_wrap_uid(); + + // List of id's that have been destroyed and need the destroy() cb called. + inline std::vector* destroy_ids_list(); + + inline uint32_t* heap_statistics_buffer() const; + inline void set_heap_statistics_buffer(uint32_t* pointer); + + inline uint32_t* heap_space_statistics_buffer() const; + inline void set_heap_space_statistics_buffer(uint32_t* pointer); + + inline char* http_parser_buffer() const; + inline void set_http_parser_buffer(char* buffer); + + inline void ThrowError(const char* errmsg); + inline void ThrowTypeError(const char* errmsg); + inline void ThrowRangeError(const char* errmsg); + inline void ThrowErrnoException(int errorno, + const char* syscall = nullptr, + const char* message = nullptr, + const char* path = nullptr); + inline void ThrowUVException(int errorno, + const char* syscall = nullptr, + const char* message = nullptr, + const char* path = nullptr, + const char* dest = nullptr); + + inline v8::Local + NewFunctionTemplate(v8::FunctionCallback callback, + v8::Local signature = v8::Local()); + + // Convenience methods for NewFunctionTemplate(). + inline void SetMethod(v8::Local that, + const char* name, + v8::FunctionCallback callback); + inline void SetProtoMethod(v8::Local that, + const char* name, + v8::FunctionCallback callback); + inline void SetTemplateMethod(v8::Local that, + const char* name, + v8::FunctionCallback callback); + + inline v8::Local NewInternalFieldObject(); + + // Strings and private symbols are shared across shared contexts + // The getters simply proxy to the per-isolate primitive. #define VP(PropertyName, StringValue) V(v8::Private, PropertyName, StringValue) #define VS(PropertyName, StringValue) V(v8::String, PropertyName, StringValue) -#define V(TypeName, PropertyName, StringValue) \ - inline v8::Local PropertyName() const; - PER_ISOLATE_PRIVATE_SYMBOL_PROPERTIES(VP) - PER_ISOLATE_STRING_PROPERTIES(VS) +#define V(TypeName, PropertyName, StringValue) \ + inline v8::Local PropertyName() const; + PER_ISOLATE_PRIVATE_SYMBOL_PROPERTIES(VP) + PER_ISOLATE_STRING_PROPERTIES(VS) #undef V #undef VS #undef VP -#define V(PropertyName, TypeName) \ - inline v8::Local PropertyName() const; \ - inline void set_ ## PropertyName(v8::Local value); - ENVIRONMENT_STRONG_PERSISTENT_PROPERTIES(V) +#define V(PropertyName, TypeName) \ + inline v8::Local PropertyName() const; \ + inline void set_##PropertyName(v8::Local value); + ENVIRONMENT_STRONG_PERSISTENT_PROPERTIES(V) #undef V - inline debugger::Agent* debugger_agent() { - return debugger_agent_; - } + inline debugger::Agent* debugger_agent() + { + return debugger_agent_; + } #if HAVE_INSPECTOR - inline inspector::Agent* inspector_agent() { - return inspector_agent_; - } + inline inspector::Agent* inspector_agent() + { + return inspector_agent_; + } #endif - typedef ListHead HandleWrapQueue; - typedef ListHead, &ReqWrap::req_wrap_queue_> - ReqWrapQueue; + typedef ListHead HandleWrapQueue; + typedef ListHead, &ReqWrap::req_wrap_queue_> + ReqWrapQueue; - inline HandleWrapQueue* handle_wrap_queue() { return &handle_wrap_queue_; } - inline ReqWrapQueue* req_wrap_queue() { return &req_wrap_queue_; } + inline HandleWrapQueue* handle_wrap_queue() { return &handle_wrap_queue_; } + inline ReqWrapQueue* req_wrap_queue() { return &req_wrap_queue_; } - static const int kContextEmbedderDataIndex = NODE_CONTEXT_EMBEDDER_DATA_INDEX; + static const int kContextEmbedderDataIndex = NODE_CONTEXT_EMBEDDER_DATA_INDEX; - private: - inline void ThrowError(v8::Local (*fun)(v8::Local), - const char* errmsg); +private: + inline void ThrowError(v8::Local (*fun)(v8::Local), + const char* errmsg); - static const int kIsolateSlot = NODE_ISOLATE_SLOT; + static const int kIsolateSlot = NODE_ISOLATE_SLOT; - class IsolateData; - inline Environment(v8::Local context, uv_loop_t* loop); - inline ~Environment(); - inline IsolateData* isolate_data() const; + class IsolateData; + inline Environment(v8::Local context, uv_loop_t* loop); + inline ~Environment(); + inline IsolateData* isolate_data() const; - v8::Isolate* const isolate_; - IsolateData* const isolate_data_; - uv_check_t immediate_check_handle_; - uv_idle_t immediate_idle_handle_; - uv_idle_t destroy_ids_idle_handle_; - uv_prepare_t idle_prepare_handle_; - uv_check_t idle_check_handle_; - AsyncHooks async_hooks_; + v8::Isolate* const isolate_; + IsolateData* const isolate_data_; + uv_check_t immediate_check_handle_; + uv_idle_t immediate_idle_handle_; + uv_idle_t destroy_ids_idle_handle_; + uv_prepare_t idle_prepare_handle_; + uv_check_t idle_check_handle_; + AsyncHooks async_hooks_; #ifndef MINIBLINK_NOT_IMPLEMENTED - FileSystemHooks *file_system_hooks_; - bool is_blink_core_; - public: - MicrotaskSuppressionHandle blink_microtask_suppression_handle_; - private: + FileSystemHooks* file_system_hooks_; + bool is_blink_core_; + +public: + MicrotaskSuppressionHandle blink_microtask_suppression_handle_; + +private: #endif - DomainFlag domain_flag_; - TickInfo tick_info_; - ArrayBufferAllocatorInfo array_buffer_allocator_info_; - const uint64_t timer_base_; - uv_timer_t cares_timer_handle_; - ares_channel cares_channel_; - node_ares_task_list cares_task_list_; - bool using_domains_; - bool printed_error_; - bool trace_sync_io_; - size_t makecallback_cntr_; - int64_t async_wrap_uid_; - std::vector* destroy_ids_list_; - debugger::Agent* debugger_agent_; + DomainFlag domain_flag_; + TickInfo tick_info_; + ArrayBufferAllocatorInfo array_buffer_allocator_info_; + const uint64_t timer_base_; + uv_timer_t cares_timer_handle_; + ares_channel cares_channel_; + node_ares_task_list cares_task_list_; + bool using_domains_; + bool printed_error_; + bool trace_sync_io_; + size_t makecallback_cntr_; + int64_t async_wrap_uid_; + std::vector* destroy_ids_list_; + debugger::Agent* debugger_agent_; #if HAVE_INSPECTOR - inspector::Agent* inspector_agent_; + inspector::Agent* inspector_agent_; #endif - HandleWrapQueue handle_wrap_queue_; - ReqWrapQueue req_wrap_queue_; - ListHead handle_cleanup_queue_; - int handle_cleanup_waiting_; + HandleWrapQueue handle_wrap_queue_; + ReqWrapQueue req_wrap_queue_; + ListHead + handle_cleanup_queue_; + int handle_cleanup_waiting_; - uint32_t* heap_statistics_buffer_ = nullptr; - uint32_t* heap_space_statistics_buffer_ = nullptr; + uint32_t* heap_statistics_buffer_ = nullptr; + uint32_t* heap_space_statistics_buffer_ = nullptr; - char* http_parser_buffer_; + char* http_parser_buffer_; -#define V(PropertyName, TypeName) \ - v8::Persistent PropertyName ## _; - ENVIRONMENT_STRONG_PERSISTENT_PROPERTIES(V) +#define V(PropertyName, TypeName) \ + v8::Persistent PropertyName##_; + ENVIRONMENT_STRONG_PERSISTENT_PROPERTIES(V) #undef V - // Per-thread, reference-counted singleton. - class IsolateData { - public: - static inline IsolateData* GetOrCreate(v8::Isolate* isolate, - uv_loop_t* loop); - inline void Put(); - inline uv_loop_t* event_loop() const; + // Per-thread, reference-counted singleton. + class IsolateData { + public: + static inline IsolateData* GetOrCreate(v8::Isolate* isolate, + uv_loop_t* loop); + inline void Put(); + inline uv_loop_t* event_loop() const; #define VP(PropertyName, StringValue) V(v8::Private, PropertyName, StringValue) #define VS(PropertyName, StringValue) V(v8::String, PropertyName, StringValue) -#define V(TypeName, PropertyName, StringValue) \ +#define V(TypeName, PropertyName, StringValue) \ inline v8::Local PropertyName() const; - PER_ISOLATE_PRIVATE_SYMBOL_PROPERTIES(VP) - PER_ISOLATE_STRING_PROPERTIES(VS) + PER_ISOLATE_PRIVATE_SYMBOL_PROPERTIES(VP) + PER_ISOLATE_STRING_PROPERTIES(VS) #undef V #undef VS #undef VP - private: - inline static IsolateData* Get(v8::Isolate* isolate); - inline explicit IsolateData(v8::Isolate* isolate, uv_loop_t* loop); - inline v8::Isolate* isolate() const; + private: + inline static IsolateData* Get(v8::Isolate* isolate); + inline explicit IsolateData(v8::Isolate* isolate, uv_loop_t* loop); + inline v8::Isolate* isolate() const; - uv_loop_t* const event_loop_; - v8::Isolate* const isolate_; + uv_loop_t* const event_loop_; + v8::Isolate* const isolate_; #define VP(PropertyName, StringValue) V(v8::Private, PropertyName, StringValue) #define VS(PropertyName, StringValue) V(v8::String, PropertyName, StringValue) -#define V(TypeName, PropertyName, StringValue) \ - v8::Eternal PropertyName ## _; - PER_ISOLATE_PRIVATE_SYMBOL_PROPERTIES(VP) - PER_ISOLATE_STRING_PROPERTIES(VS) +#define V(TypeName, PropertyName, StringValue) \ + v8::Eternal PropertyName##_; + PER_ISOLATE_PRIVATE_SYMBOL_PROPERTIES(VP) + PER_ISOLATE_STRING_PROPERTIES(VS) #undef V #undef VS #undef VP - unsigned int ref_count_; + unsigned int ref_count_; - DISALLOW_COPY_AND_ASSIGN(IsolateData); - }; - - public: - struct CleanupHookCallback { - CleanupHookCallback(void(*fn)(void*), void* arg, uint64_t insertion_order_counter) - { - fn_ = fn; - arg_ = arg; - insertion_order_counter_ = insertion_order_counter; - } - void(*fn_)(void*); - void* arg_; + DISALLOW_COPY_AND_ASSIGN(IsolateData); + }; - // We keep track of the insertion order for these objects, so that we can - // call the callbacks in reverse order when we are cleaning up. - uint64_t insertion_order_counter_; +public: + struct CleanupHookCallback { + CleanupHookCallback(void (*fn)(void*), void* arg, uint64_t insertion_order_counter) + { + fn_ = fn; + arg_ = arg; + insertion_order_counter_ = insertion_order_counter; + } + void (*fn_)(void*); + void* arg_; + + // We keep track of the insertion order for these objects, so that we can + // call the callbacks in reverse order when we are cleaning up. + uint64_t insertion_order_counter_; + + static bool CompareGT(const CleanupHookCallback* a, const CleanupHookCallback* b) + { + // Sort in descending order so that the most recently inserted callbacks are run first. + return a->insertion_order_counter_ > b->insertion_order_counter_; + } + }; - static bool CompareGT(const CleanupHookCallback* a, const CleanupHookCallback* b) { - // Sort in descending order so that the most recently inserted callbacks are run first. - return a->insertion_order_counter_ > b->insertion_order_counter_; - } - }; - private: - // Use an unordered_set, so that we have efficient insertion and removal. - std::vector* cleanup_hooks_; - uint64_t cleanup_hook_counter_ ; +private: + // Use an unordered_set, so that we have efficient insertion and removal. + std::vector* cleanup_hooks_; + uint64_t cleanup_hook_counter_; - DISALLOW_COPY_AND_ASSIGN(Environment); + DISALLOW_COPY_AND_ASSIGN(Environment); }; -} // namespace node +} // namespace node -#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS +#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS -#endif // SRC_ENV_H_ +#endif // SRC_ENV_H_ diff --git a/node/src/fs_event_wrap.cc b/node/src/fs_event_wrap.cc index 025f511d93..a91e6f1fbb 100644 --- a/node/src/fs_event_wrap.cc +++ b/node/src/fs_event_wrap.cc @@ -1,3 +1,6 @@ +//Copyright Joyent, Inc. and other Node contributors. +//The MIT License (MIT) + #include "async-wrap.h" #include "async-wrap-inl.h" #include "env.h" @@ -22,177 +25,178 @@ using v8::Object; using v8::String; using v8::Value; -class FSEventWrap: public HandleWrap { - public: - static void Initialize(Local target, - Local unused, - Local context); - static void New(const FunctionCallbackInfo& args); - static void Start(const FunctionCallbackInfo& args); - static void Close(const FunctionCallbackInfo& args); +class FSEventWrap : public HandleWrap { +public: + static void Initialize(Local target, + Local unused, + Local context); + static void New(const FunctionCallbackInfo& args); + static void Start(const FunctionCallbackInfo& args); + static void Close(const FunctionCallbackInfo& args); - size_t self_size() const override { return sizeof(*this); } + size_t self_size() const override { return sizeof(*this); } - private: - static const encoding kDefaultEncoding = UTF8; +private: + static const encoding kDefaultEncoding = UTF8; - FSEventWrap(Environment* env, Local object); - ~FSEventWrap() override; + FSEventWrap(Environment* env, Local object); + ~FSEventWrap() override; - static void OnEvent(uv_fs_event_t* handle, const char* filename, int events, - int status); + static void OnEvent(uv_fs_event_t* handle, const char* filename, int events, + int status); - uv_fs_event_t handle_; - bool initialized_ = false; - enum encoding encoding_ = kDefaultEncoding; + uv_fs_event_t handle_; + bool initialized_ = false; + enum encoding encoding_ = kDefaultEncoding; }; - FSEventWrap::FSEventWrap(Environment* env, Local object) : HandleWrap(env, - object, - reinterpret_cast(&handle_), - AsyncWrap::PROVIDER_FSEVENTWRAP) {} - - -FSEventWrap::~FSEventWrap() { - CHECK_EQ(initialized_, false); + object, + reinterpret_cast(&handle_), + AsyncWrap::PROVIDER_FSEVENTWRAP) +{ } +FSEventWrap::~FSEventWrap() +{ + NODE_CHECK_EQ(initialized_, false); +} void FSEventWrap::Initialize(Local target, - Local unused, - Local context) { - Environment* env = Environment::GetCurrent(context); + Local unused, + Local context) +{ + Environment* env = Environment::GetCurrent(context); - auto fsevent_string = FIXED_ONE_BYTE_STRING(env->isolate(), "FSEvent"); - Local t = env->NewFunctionTemplate(New); - t->InstanceTemplate()->SetInternalFieldCount(1); - t->SetClassName(fsevent_string); + auto fsevent_string = FIXED_ONE_BYTE_STRING(env->isolate(), "FSEvent"); + Local t = env->NewFunctionTemplate(New); + t->InstanceTemplate()->SetInternalFieldCount(1); + t->SetClassName(fsevent_string); - env->SetProtoMethod(t, "start", Start); - env->SetProtoMethod(t, "close", Close); + env->SetProtoMethod(t, "start", Start); + env->SetProtoMethod(t, "close", Close); - target->Set(fsevent_string, t->GetFunction()); + target->Set(fsevent_string, t->GetFunction()); } - -void FSEventWrap::New(const FunctionCallbackInfo& args) { - CHECK(args.IsConstructCall()); - Environment* env = Environment::GetCurrent(args); - new FSEventWrap(env, args.This()); +void FSEventWrap::New(const FunctionCallbackInfo& args) +{ + NODE_CHECK(args.IsConstructCall()); + Environment* env = Environment::GetCurrent(args); + new FSEventWrap(env, args.This()); } +void FSEventWrap::Start(const FunctionCallbackInfo& args) +{ + Environment* env = Environment::GetCurrent(args); -void FSEventWrap::Start(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - - FSEventWrap* wrap; - ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); - CHECK_EQ(wrap->initialized_, false); + FSEventWrap* wrap; + ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); + NODE_CHECK_EQ(wrap->initialized_, false); - static const char kErrMsg[] = "filename must be a string or Buffer"; - if (args.Length() < 1) - return env->ThrowTypeError(kErrMsg); + static const char kErrMsg[] = "filename must be a string or Buffer"; + if (args.Length() < 1) + return env->ThrowTypeError(kErrMsg); - BufferValue path(env->isolate(), args[0]); - if (*path == nullptr) - return env->ThrowTypeError(kErrMsg); + BufferValue path(env->isolate(), args[0]); + if (*path == nullptr) + return env->ThrowTypeError(kErrMsg); - unsigned int flags = 0; - if (args[2]->IsTrue()) - flags |= UV_FS_EVENT_RECURSIVE; + unsigned int flags = 0; + if (args[2]->IsTrue()) + flags |= UV_FS_EVENT_RECURSIVE; - wrap->encoding_ = ParseEncoding(env->isolate(), args[3], kDefaultEncoding); - - int err = uv_fs_event_init(wrap->env()->event_loop(), &wrap->handle_); - if (err == 0) { - wrap->initialized_ = true; - - err = uv_fs_event_start(&wrap->handle_, OnEvent, *path, flags); + wrap->encoding_ = ParseEncoding(env->isolate(), args[3], kDefaultEncoding); + int err = uv_fs_event_init(wrap->env()->event_loop(), &wrap->handle_); if (err == 0) { - // Check for persistent argument - if (!args[1]->IsTrue()) { - uv_unref(reinterpret_cast(&wrap->handle_)); - } - } else { - FSEventWrap::Close(args); + wrap->initialized_ = true; + + err = uv_fs_event_start(&wrap->handle_, OnEvent, *path, flags); + + if (err == 0) { + // Check for persistent argument + if (!args[1]->IsTrue()) { + uv_unref(reinterpret_cast(&wrap->handle_)); + } + } else { + FSEventWrap::Close(args); + } } - } - args.GetReturnValue().Set(err); + args.GetReturnValue().Set(err); } - void FSEventWrap::OnEvent(uv_fs_event_t* handle, const char* filename, - int events, int status) { - FSEventWrap* wrap = static_cast(handle->data); - Environment* env = wrap->env(); - - HandleScope handle_scope(env->isolate()); - Context::Scope context_scope(env->context()); - - CHECK_EQ(wrap->persistent().IsEmpty(), false); - - // We're in a bind here. libuv can set both UV_RENAME and UV_CHANGE but - // the Node API only lets us pass a single event to JS land. - // - // The obvious solution is to run the callback twice, once for each event. - // However, since the second event is not allowed to fire if the handle is - // closed after the first event, and since there is no good way to detect - // closed handles, that option is out. - // - // For now, ignore the UV_CHANGE event if UV_RENAME is also set. Make the - // assumption that a rename implicitly means an attribute change. Not too - // unreasonable, right? Still, we should revisit this before v1.0. - Local event_string; - if (status) { - event_string = String::Empty(env->isolate()); - } else if (events & UV_RENAME) { - event_string = env->rename_string(); - } else if (events & UV_CHANGE) { - event_string = env->change_string(); - } else { - CHECK(0 && "bad fs events flag"); - } - - Local argv[] = { - Integer::New(env->isolate(), status), - event_string, - Null(env->isolate()) - }; - - if (filename != nullptr) { - Local fn = StringBytes::Encode(env->isolate(), - filename, - wrap->encoding_); - if (fn.IsEmpty()) { - argv[0] = Integer::New(env->isolate(), UV_EINVAL); - argv[2] = StringBytes::Encode(env->isolate(), - filename, - strlen(filename), - BUFFER); + int events, int status) +{ + FSEventWrap* wrap = static_cast(handle->data); + Environment* env = wrap->env(); + + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); + + NODE_CHECK_EQ(wrap->persistent().IsEmpty(), false); + + // We're in a bind here. libuv can set both UV_RENAME and UV_CHANGE but + // the Node API only lets us pass a single event to JS land. + // + // The obvious solution is to run the callback twice, once for each event. + // However, since the second event is not allowed to fire if the handle is + // closed after the first event, and since there is no good way to detect + // closed handles, that option is out. + // + // For now, ignore the UV_CHANGE event if UV_RENAME is also set. Make the + // assumption that a rename implicitly means an attribute change. Not too + // unreasonable, right? Still, we should revisit this before v1.0. + Local event_string; + if (status) { + event_string = String::Empty(env->isolate()); + } else if (events & UV_RENAME) { + event_string = env->rename_string(); + } else if (events & UV_CHANGE) { + event_string = env->change_string(); } else { - argv[2] = fn; + NODE_CHECK(0 && "bad fs events flag"); } - } - wrap->MakeCallback(env->onchange_string(), arraysize(argv), argv); -} + Local argv[] = { + Integer::New(env->isolate(), status), + event_string, + Null(env->isolate()) + }; + + if (filename != nullptr) { + Local fn = StringBytes::Encode(env->isolate(), + filename, + wrap->encoding_); + if (fn.IsEmpty()) { + argv[0] = Integer::New(env->isolate(), UV_EINVAL); + argv[2] = StringBytes::Encode(env->isolate(), + filename, + strlen(filename), + BUFFER); + } else { + argv[2] = fn; + } + } + wrap->MakeCallback(env->onchange_string(), arraysize(argv), argv); +} -void FSEventWrap::Close(const FunctionCallbackInfo& args) { - FSEventWrap* wrap; - ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); +void FSEventWrap::Close(const FunctionCallbackInfo& args) +{ + FSEventWrap* wrap; + ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); - if (wrap == nullptr || wrap->initialized_ == false) - return; - wrap->initialized_ = false; + if (wrap == nullptr || wrap->initialized_ == false) + return; + wrap->initialized_ = false; - HandleWrap::Close(args); + HandleWrap::Close(args); } -} // namespace node +} // namespace node NODE_MODULE_CONTEXT_AWARE_BUILTIN(fs_event_wrap, node::FSEventWrap::Initialize) diff --git a/node/src/handle_wrap.cc b/node/src/handle_wrap.cc index 9b26b5f829..02ea92ef12 100644 --- a/node/src/handle_wrap.cc +++ b/node/src/handle_wrap.cc @@ -16,109 +16,108 @@ using v8::Local; using v8::Object; using v8::Value; +void HandleWrap::Ref(const FunctionCallbackInfo& args) +{ + HandleWrap* wrap; + ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); -void HandleWrap::Ref(const FunctionCallbackInfo& args) { - HandleWrap* wrap; - ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); - - if (IsAlive(wrap)) - uv_ref(wrap->GetHandle()); + if (IsAlive(wrap)) + uv_ref(wrap->GetHandle()); } +void HandleWrap::Unref(const FunctionCallbackInfo& args) +{ + HandleWrap* wrap; + ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); -void HandleWrap::Unref(const FunctionCallbackInfo& args) { - HandleWrap* wrap; - ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); - - if (IsAlive(wrap)) - uv_unref(wrap->GetHandle()); + if (IsAlive(wrap)) + uv_unref(wrap->GetHandle()); } - -void HandleWrap::HasRef(const FunctionCallbackInfo& args) { - HandleWrap* wrap; - ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); - args.GetReturnValue().Set(HasRef(wrap)); +void HandleWrap::HasRef(const FunctionCallbackInfo& args) +{ + HandleWrap* wrap; + ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); + args.GetReturnValue().Set(HasRef(wrap)); } +void HandleWrap::Close(const FunctionCallbackInfo& args) +{ + Environment* env = Environment::GetCurrent(args); -void HandleWrap::Close(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - - HandleWrap* wrap; - ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); + HandleWrap* wrap; + ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); - // Guard against uninitialized handle or double close. - if (!IsAlive(wrap)) - return; + // Guard against uninitialized handle or double close. + if (!IsAlive(wrap)) + return; - if (wrap->state_ != kInitialized) - return; + if (wrap->state_ != kInitialized) + return; - CHECK_EQ(false, wrap->persistent().IsEmpty()); - uv_close(wrap->handle_, OnClose); - wrap->state_ = kClosing; + NODE_CHECK_EQ(false, wrap->persistent().IsEmpty()); + uv_close(wrap->handle_, OnClose); + wrap->state_ = kClosing; - if (args[0]->IsFunction()) { - wrap->object()->Set(env->onclose_string(), args[0]); - wrap->state_ = kClosingWithCallback; - } + if (args[0]->IsFunction()) { + wrap->object()->Set(env->onclose_string(), args[0]); + wrap->state_ = kClosingWithCallback; + } } - HandleWrap::HandleWrap(Environment* env, - Local object, - uv_handle_t* handle, - AsyncWrap::ProviderType provider, - AsyncWrap* parent) - : AsyncWrap(env, object, provider, parent), - state_(kInitialized), - handle_(handle) { - handle_->data = this; - HandleScope scope(env->isolate()); - Wrap(object, this); - env->handle_wrap_queue()->PushBack(this); + Local object, + uv_handle_t* handle, + AsyncWrap::ProviderType provider, + AsyncWrap* parent) + : AsyncWrap(env, object, provider, parent) + , state_(kInitialized) + , handle_(handle) +{ + handle_->data = this; + HandleScope scope(env->isolate()); + Wrap(object, this); + env->handle_wrap_queue()->PushBack(this); } - -HandleWrap::~HandleWrap() { - CHECK(persistent().IsEmpty()); - - // weolar: delete myself from env->handle_wrap_queue() - Environment::HandleWrapQueue handle_wrap_queue; - while (!env()->handle_wrap_queue()->IsEmpty()) { - HandleWrap* it = env()->handle_wrap_queue()->PopFront(); - if (it == this) - continue; - handle_wrap_queue.PushBack(it); - } - while (!handle_wrap_queue.IsEmpty()) { - HandleWrap* it = handle_wrap_queue.PopFront(); - env()->handle_wrap_queue()->PushBack(it); - } +HandleWrap::~HandleWrap() +{ + NODE_CHECK(persistent().IsEmpty()); + + // weolar: delete myself from env->handle_wrap_queue() + Environment::HandleWrapQueue handle_wrap_queue; + while (!env()->handle_wrap_queue()->IsEmpty()) { + HandleWrap* it = env()->handle_wrap_queue()->PopFront(); + if (it == this) + continue; + handle_wrap_queue.PushBack(it); + } + while (!handle_wrap_queue.IsEmpty()) { + HandleWrap* it = handle_wrap_queue.PopFront(); + env()->handle_wrap_queue()->PushBack(it); + } } +void HandleWrap::OnClose(uv_handle_t* handle) +{ + HandleWrap* wrap = static_cast(handle->data); + Environment* env = wrap->env(); + HandleScope scope(env->isolate()); + Context::Scope context_scope(env->context()); -void HandleWrap::OnClose(uv_handle_t* handle) { - HandleWrap* wrap = static_cast(handle->data); - Environment* env = wrap->env(); - HandleScope scope(env->isolate()); - Context::Scope context_scope(env->context()); + // The wrap object should still be there. + NODE_CHECK_EQ(wrap->persistent().IsEmpty(), false); + NODE_CHECK(wrap->state_ >= kClosing && wrap->state_ <= kClosingWithCallback); - // The wrap object should still be there. - CHECK_EQ(wrap->persistent().IsEmpty(), false); - CHECK(wrap->state_ >= kClosing && wrap->state_ <= kClosingWithCallback); + const bool have_close_callback = (wrap->state_ == kClosingWithCallback); + wrap->state_ = kClosed; - const bool have_close_callback = (wrap->state_ == kClosingWithCallback); - wrap->state_ = kClosed; + if (have_close_callback) + wrap->MakeCallback(env->onclose_string(), 0, nullptr); - if (have_close_callback) - wrap->MakeCallback(env->onclose_string(), 0, nullptr); - - ClearWrap(wrap->object()); - wrap->persistent().Reset(); - delete wrap; + ClearWrap(wrap->object()); + wrap->persistent().Reset(); + delete wrap; } - -} // namespace node +} // namespace node diff --git a/node/src/handle_wrap.h b/node/src/handle_wrap.h index 2a128dd8b1..d406be2a5f 100644 --- a/node/src/handle_wrap.h +++ b/node/src/handle_wrap.h @@ -1,3 +1,6 @@ +//Copyright Joyent, Inc. and other Node contributors. +//The MIT License (MIT) + #ifndef SRC_HANDLE_WRAP_H_ #define SRC_HANDLE_WRAP_H_ @@ -18,7 +21,7 @@ class Environment; // // - MakeCallback may only be made directly off the event loop. // That is there can be no JavaScript stack frames underneath it. -// (Is there any way to assert that?) +// (Is there any way to NODE_ASSERT that?) // // - No use of v8::WeakReferenceCallback. The close callback signifies that // we're done with a handle - external resources can be freed. @@ -33,42 +36,46 @@ class Environment; // taken care of. class HandleWrap : public AsyncWrap { - public: - static void Close(const v8::FunctionCallbackInfo& args); - static void Ref(const v8::FunctionCallbackInfo& args); - static void Unref(const v8::FunctionCallbackInfo& args); - static void HasRef(const v8::FunctionCallbackInfo& args); +public: + static void Close(const v8::FunctionCallbackInfo& args); + static void Ref(const v8::FunctionCallbackInfo& args); + static void Unref(const v8::FunctionCallbackInfo& args); + static void HasRef(const v8::FunctionCallbackInfo& args); - static inline bool IsAlive(const HandleWrap* wrap) { - return wrap != nullptr && wrap->state_ != kClosed; - } + static inline bool IsAlive(const HandleWrap* wrap) + { + return wrap != nullptr && wrap->state_ != kClosed; + } - static inline bool HasRef(const HandleWrap* wrap) { - return IsAlive(wrap) && uv_has_ref(wrap->GetHandle()); - } + static inline bool HasRef(const HandleWrap* wrap) + { + return IsAlive(wrap) && uv_has_ref(wrap->GetHandle()); + } - inline uv_handle_t* GetHandle() const { return handle_; } + inline uv_handle_t* GetHandle() const { return handle_; } - protected: - HandleWrap(Environment* env, - v8::Local object, - uv_handle_t* handle, - AsyncWrap::ProviderType provider, - AsyncWrap* parent = nullptr); - ~HandleWrap() override; +protected: + HandleWrap(Environment* env, + v8::Local object, + uv_handle_t* handle, + AsyncWrap::ProviderType provider, + AsyncWrap* parent = nullptr); + ~HandleWrap() override; - private: - friend class Environment; - friend void GetActiveHandles(const v8::FunctionCallbackInfo&); - static void OnClose(uv_handle_t* handle); - ListNode handle_wrap_queue_; - enum { kInitialized, kClosing, kClosingWithCallback, kClosed } state_; - uv_handle_t* const handle_; +private: + friend class Environment; + friend void GetActiveHandles(const v8::FunctionCallbackInfo&); + static void OnClose(uv_handle_t* handle); + ListNode handle_wrap_queue_; + enum { kInitialized, + kClosing, + kClosingWithCallback, + kClosed } state_; + uv_handle_t* const handle_; }; +} // namespace node -} // namespace node - -#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS +#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS -#endif // SRC_HANDLE_WRAP_H_ +#endif // SRC_HANDLE_WRAP_H_ diff --git a/node/src/http_parser.c b/node/src/http_parser.c index 719617549d..9eefde71ba 100644 --- a/node/src/http_parser.c +++ b/node/src/http_parser.c @@ -30,115 +30,111 @@ #include #ifndef ULLONG_MAX -# define ULLONG_MAX ((uint64_t) -1) /* 2^64-1 */ +#define ULLONG_MAX ((uint64_t)-1) /* 2^64-1 */ #endif #ifndef MIN -# define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MIN(a, b) ((a) < (b) ? (a) : (b)) #endif #ifndef ARRAY_SIZE -# define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) #endif #ifndef BIT_AT -# define BIT_AT(a, i) \ - (!!((unsigned int) (a)[(unsigned int) (i) >> 3] & \ - (1 << ((unsigned int) (i) & 7)))) +#define BIT_AT(a, i) \ + (!!((unsigned int)(a)[(unsigned int)(i) >> 3] & (1 << ((unsigned int)(i)&7)))) #endif #ifndef ELEM_AT -# define ELEM_AT(a, i, v) ((unsigned int) (i) < ARRAY_SIZE(a) ? (a)[(i)] : (v)) +#define ELEM_AT(a, i, v) ((unsigned int)(i) < ARRAY_SIZE(a) ? (a)[(i)] : (v)) #endif -#define SET_ERRNO(e) \ -do { \ - parser->http_errno = (e); \ -} while(0) +#define SET_ERRNO(e) \ + do { \ + parser->http_errno = (e); \ + } while (0) #define CURRENT_STATE() p_state -#define UPDATE_STATE(V) p_state = (enum state) (V); -#define RETURN(V) \ -do { \ - parser->state = CURRENT_STATE(); \ - return (V); \ -} while (0); -#define REEXECUTE() \ - goto reexecute; \ - +#define UPDATE_STATE(V) p_state = (enum state)(V); +#define RETURN(V) \ + do { \ + parser->state = CURRENT_STATE(); \ + return (V); \ + } while (0); +#define REEXECUTE() \ + goto reexecute; #ifdef __GNUC__ -# define LIKELY(X) __builtin_expect(!!(X), 1) -# define UNLIKELY(X) __builtin_expect(!!(X), 0) +#define LIKELY(X) __builtin_expect(!!(X), 1) +#define UNLIKELY(X) __builtin_expect(!!(X), 0) #else -# define LIKELY(X) (X) -# define UNLIKELY(X) (X) +#define LIKELY(X) (X) +#define UNLIKELY(X) (X) #endif - /* Run the notify callback FOR, returning ER if it fails */ -#define CALLBACK_NOTIFY_(FOR, ER) \ -do { \ - assert(HTTP_PARSER_ERRNO(parser) == HPE_OK); \ - \ - if (LIKELY(settings->on_##FOR)) { \ - parser->state = CURRENT_STATE(); \ - if (UNLIKELY(0 != settings->on_##FOR(parser))) { \ - SET_ERRNO(HPE_CB_##FOR); \ - } \ - UPDATE_STATE(parser->state); \ - \ - /* We either errored above or got paused; get out */ \ - if (UNLIKELY(HTTP_PARSER_ERRNO(parser) != HPE_OK)) { \ - return (ER); \ - } \ - } \ -} while (0) +#define CALLBACK_NOTIFY_(FOR, ER) \ + do { \ + assert(HTTP_PARSER_ERRNO(parser) == HPE_OK); \ + \ + if (LIKELY(settings->on_##FOR)) { \ + parser->state = CURRENT_STATE(); \ + if (UNLIKELY(0 != settings->on_##FOR(parser))) { \ + SET_ERRNO(HPE_CB_##FOR); \ + } \ + UPDATE_STATE(parser->state); \ + \ + /* We either errored above or got paused; get out */ \ + if (UNLIKELY(HTTP_PARSER_ERRNO(parser) != HPE_OK)) { \ + return (ER); \ + } \ + } \ + } while (0) /* Run the notify callback FOR and consume the current byte */ -#define CALLBACK_NOTIFY(FOR) CALLBACK_NOTIFY_(FOR, p - data + 1) +#define CALLBACK_NOTIFY(FOR) CALLBACK_NOTIFY_(FOR, p - data + 1) /* Run the notify callback FOR and don't consume the current byte */ -#define CALLBACK_NOTIFY_NOADVANCE(FOR) CALLBACK_NOTIFY_(FOR, p - data) +#define CALLBACK_NOTIFY_NOADVANCE(FOR) CALLBACK_NOTIFY_(FOR, p - data) /* Run data callback FOR with LEN bytes, returning ER if it fails */ -#define CALLBACK_DATA_(FOR, LEN, ER) \ -do { \ - assert(HTTP_PARSER_ERRNO(parser) == HPE_OK); \ - \ - if (FOR##_mark) { \ - if (LIKELY(settings->on_##FOR)) { \ - parser->state = CURRENT_STATE(); \ - if (UNLIKELY(0 != \ - settings->on_##FOR(parser, FOR##_mark, (LEN)))) { \ - SET_ERRNO(HPE_CB_##FOR); \ - } \ - UPDATE_STATE(parser->state); \ - \ - /* We either errored above or got paused; get out */ \ - if (UNLIKELY(HTTP_PARSER_ERRNO(parser) != HPE_OK)) { \ - return (ER); \ - } \ - } \ - FOR##_mark = NULL; \ - } \ -} while (0) +#define CALLBACK_DATA_(FOR, LEN, ER) \ + do { \ + assert(HTTP_PARSER_ERRNO(parser) == HPE_OK); \ + \ + if (FOR##_mark) { \ + if (LIKELY(settings->on_##FOR)) { \ + parser->state = CURRENT_STATE(); \ + if (UNLIKELY(0 != settings->on_##FOR(parser, FOR##_mark, (LEN)))) { \ + SET_ERRNO(HPE_CB_##FOR); \ + } \ + UPDATE_STATE(parser->state); \ + \ + /* We either errored above or got paused; get out */ \ + if (UNLIKELY(HTTP_PARSER_ERRNO(parser) != HPE_OK)) { \ + return (ER); \ + } \ + } \ + FOR##_mark = NULL; \ + } \ + } while (0) /* Run the data callback FOR and consume the current byte */ -#define CALLBACK_DATA(FOR) \ +#define CALLBACK_DATA(FOR) \ CALLBACK_DATA_(FOR, p - FOR##_mark, p - data + 1) /* Run the data callback FOR and don't consume the current byte */ -#define CALLBACK_DATA_NOADVANCE(FOR) \ +#define CALLBACK_DATA_NOADVANCE(FOR) \ CALLBACK_DATA_(FOR, p - FOR##_mark, p - data) /* Set the mark FOR; non-destructive if mark is already set */ -#define MARK(FOR) \ -do { \ - if (!FOR##_mark) { \ - FOR##_mark = p; \ - } \ -} while (0) +#define MARK(FOR) \ + do { \ + if (!FOR##_mark) { \ + FOR##_mark = p; \ + } \ + } while (0) /* Don't allow the total size of the HTTP headers (including the status * line) to exceed HTTP_MAX_HEADER_SIZE. This check is here to protect @@ -151,15 +147,14 @@ do { \ * than any reasonable request or response so this should never affect * day-to-day operation. */ -#define COUNT_HEADER_SIZE(V) \ -do { \ - parser->nread += (V); \ - if (UNLIKELY(parser->nread > (HTTP_MAX_HEADER_SIZE))) { \ - SET_ERRNO(HPE_HEADER_OVERFLOW); \ - goto error; \ - } \ -} while (0) - +#define COUNT_HEADER_SIZE(V) \ + do { \ + parser->nread += (V); \ + if (UNLIKELY(parser->nread > (HTTP_MAX_HEADER_SIZE))) { \ + SET_ERRNO(HPE_HEADER_OVERFLOW); \ + goto error; \ + } \ + } while (0) #define PROXY_CONNECTION "proxy-connection" #define CONNECTION "connection" @@ -170,14 +165,11 @@ do { \ #define KEEP_ALIVE "keep-alive" #define CLOSE "close" - -static const char *method_strings[] = - { +static const char* method_strings[] = { #define XX(num, name, string) #string, - HTTP_METHOD_MAP(XX) + HTTP_METHOD_MAP(XX) #undef XX - }; - +}; /* Tokens as defined by rfc 2616. Also lowercases them. * token = 1* @@ -187,290 +179,283 @@ static const char *method_strings[] = * | "{" | "}" | SP | HT */ static const char tokens[256] = { -/* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */ - 0, 0, 0, 0, 0, 0, 0, 0, -/* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */ - 0, 0, 0, 0, 0, 0, 0, 0, -/* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */ - 0, 0, 0, 0, 0, 0, 0, 0, -/* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */ - 0, 0, 0, 0, 0, 0, 0, 0, -/* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */ - 0, '!', 0, '#', '$', '%', '&', '\'', -/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */ - 0, 0, '*', '+', 0, '-', '.', 0, -/* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */ - '0', '1', '2', '3', '4', '5', '6', '7', -/* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */ - '8', '9', 0, 0, 0, 0, 0, 0, -/* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */ - 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', -/* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */ - 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', -/* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */ - 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', -/* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */ - 'x', 'y', 'z', 0, 0, 0, '^', '_', -/* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */ - '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', -/* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */ - 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', -/* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */ - 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', -/* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */ - 'x', 'y', 'z', 0, '|', 0, '~', 0 }; - - -static const int8_t unhex[256] = - {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 - ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 - ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 - , 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1 - ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1 - ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 - ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1 - ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 - }; + /* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */ + 0, 0, 0, 0, 0, 0, 0, 0, + /* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */ + 0, 0, 0, 0, 0, 0, 0, 0, + /* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */ + 0, 0, 0, 0, 0, 0, 0, 0, + /* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */ + 0, 0, 0, 0, 0, 0, 0, 0, + /* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */ + 0, '!', 0, '#', '$', '%', '&', '\'', + /* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */ + 0, 0, '*', '+', 0, '-', '.', 0, + /* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */ + '0', '1', '2', '3', '4', '5', '6', '7', + /* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */ + '8', '9', 0, 0, 0, 0, 0, 0, + /* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */ + 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', + /* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */ + 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', + /* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */ + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', + /* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */ + 'x', 'y', 'z', 0, 0, 0, '^', '_', + /* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */ + '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', + /* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */ + 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', + /* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */ + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', + /* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */ + 'x', 'y', 'z', 0, '|', 0, '~', 0 +}; +static const int8_t unhex[256] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; #if HTTP_PARSER_STRICT -# define T(v) 0 +#define T(v) 0 #else -# define T(v) v +#define T(v) v #endif - static const uint8_t normal_url_char[32] = { -/* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */ - 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, -/* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */ - 0 | T(2) | 0 | 0 | T(16) | 0 | 0 | 0, -/* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */ - 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, -/* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */ - 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, -/* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */ - 0 | 2 | 4 | 0 | 16 | 32 | 64 | 128, -/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */ - 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, -/* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */ - 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, -/* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */ - 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0, -/* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */ - 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, -/* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */ - 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, -/* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */ - 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, -/* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */ - 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, -/* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */ - 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, -/* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */ - 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, -/* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */ - 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, -/* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */ - 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0, }; + /* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */ + 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, + /* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */ + 0 | T(2) | 0 | 0 | T(16) | 0 | 0 | 0, + /* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */ + 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, + /* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */ + 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, + /* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */ + 0 | 2 | 4 | 0 | 16 | 32 | 64 | 128, + /* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, + /* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, + /* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0, + /* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, + /* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, + /* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, + /* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, + /* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, + /* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, + /* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, + /* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0, +}; #undef T -enum state - { s_dead = 1 /* important that this is > 0 */ - - , s_start_req_or_res - , s_res_or_resp_H - , s_start_res - , s_res_H - , s_res_HT - , s_res_HTT - , s_res_HTTP - , s_res_first_http_major - , s_res_http_major - , s_res_first_http_minor - , s_res_http_minor - , s_res_first_status_code - , s_res_status_code - , s_res_status_start - , s_res_status - , s_res_line_almost_done - - , s_start_req - - , s_req_method - , s_req_spaces_before_url - , s_req_schema - , s_req_schema_slash - , s_req_schema_slash_slash - , s_req_server_start - , s_req_server - , s_req_server_with_at - , s_req_path - , s_req_query_string_start - , s_req_query_string - , s_req_fragment_start - , s_req_fragment - , s_req_http_start - , s_req_http_H - , s_req_http_HT - , s_req_http_HTT - , s_req_http_HTTP - , s_req_first_http_major - , s_req_http_major - , s_req_first_http_minor - , s_req_http_minor - , s_req_line_almost_done - - , s_header_field_start - , s_header_field - , s_header_value_discard_ws - , s_header_value_discard_ws_almost_done - , s_header_value_discard_lws - , s_header_value_start - , s_header_value - , s_header_value_lws - - , s_header_almost_done - - , s_chunk_size_start - , s_chunk_size - , s_chunk_parameters - , s_chunk_size_almost_done - - , s_headers_almost_done - , s_headers_done - - /* Important: 's_headers_done' must be the last 'header' state. All +enum state { s_dead = 1 /* important that this is > 0 */ + + , + s_start_req_or_res, + s_res_or_resp_H, + s_start_res, + s_res_H, + s_res_HT, + s_res_HTT, + s_res_HTTP, + s_res_first_http_major, + s_res_http_major, + s_res_first_http_minor, + s_res_http_minor, + s_res_first_status_code, + s_res_status_code, + s_res_status_start, + s_res_status, + s_res_line_almost_done + + , + s_start_req + + , + s_req_method, + s_req_spaces_before_url, + s_req_schema, + s_req_schema_slash, + s_req_schema_slash_slash, + s_req_server_start, + s_req_server, + s_req_server_with_at, + s_req_path, + s_req_query_string_start, + s_req_query_string, + s_req_fragment_start, + s_req_fragment, + s_req_http_start, + s_req_http_H, + s_req_http_HT, + s_req_http_HTT, + s_req_http_HTTP, + s_req_first_http_major, + s_req_http_major, + s_req_first_http_minor, + s_req_http_minor, + s_req_line_almost_done + + , + s_header_field_start, + s_header_field, + s_header_value_discard_ws, + s_header_value_discard_ws_almost_done, + s_header_value_discard_lws, + s_header_value_start, + s_header_value, + s_header_value_lws + + , + s_header_almost_done + + , + s_chunk_size_start, + s_chunk_size, + s_chunk_parameters, + s_chunk_size_almost_done + + , + s_headers_almost_done, + s_headers_done + + /* Important: 's_headers_done' must be the last 'header' state. All * states beyond this must be 'body' states. It is used for overflow * checking. See the PARSING_HEADER() macro. */ - , s_chunk_data - , s_chunk_data_almost_done - , s_chunk_data_done - - , s_body_identity - , s_body_identity_eof + , + s_chunk_data, + s_chunk_data_almost_done, + s_chunk_data_done - , s_message_done - }; + , + s_body_identity, + s_body_identity_eof + , + s_message_done +}; #define PARSING_HEADER(state) (state <= s_headers_done) +enum header_states { h_general = 0, + h_C, + h_CO, + h_CON + + , + h_matching_connection, + h_matching_proxy_connection, + h_matching_content_length, + h_matching_transfer_encoding, + h_matching_upgrade + + , + h_connection, + h_content_length, + h_transfer_encoding, + h_upgrade + + , + h_matching_transfer_encoding_chunked, + h_matching_connection_token_start, + h_matching_connection_keep_alive, + h_matching_connection_close, + h_matching_connection_upgrade, + h_matching_connection_token + + , + h_transfer_encoding_chunked, + h_connection_keep_alive, + h_connection_close, + h_connection_upgrade +}; -enum header_states - { h_general = 0 - , h_C - , h_CO - , h_CON - - , h_matching_connection - , h_matching_proxy_connection - , h_matching_content_length - , h_matching_transfer_encoding - , h_matching_upgrade - - , h_connection - , h_content_length - , h_transfer_encoding - , h_upgrade - - , h_matching_transfer_encoding_chunked - , h_matching_connection_token_start - , h_matching_connection_keep_alive - , h_matching_connection_close - , h_matching_connection_upgrade - , h_matching_connection_token - - , h_transfer_encoding_chunked - , h_connection_keep_alive - , h_connection_close - , h_connection_upgrade - }; - -enum http_host_state - { - s_http_host_dead = 1 - , s_http_userinfo_start - , s_http_userinfo - , s_http_host_start - , s_http_host_v6_start - , s_http_host - , s_http_host_v6 - , s_http_host_v6_end - , s_http_host_v6_zone_start - , s_http_host_v6_zone - , s_http_host_port_start - , s_http_host_port +enum http_host_state { + s_http_host_dead = 1, + s_http_userinfo_start, + s_http_userinfo, + s_http_host_start, + s_http_host_v6_start, + s_http_host, + s_http_host_v6, + s_http_host_v6_end, + s_http_host_v6_zone_start, + s_http_host_v6_zone, + s_http_host_port_start, + s_http_host_port }; /* Macros for character classes; depends on strict-mode */ -#define CR '\r' -#define LF '\n' -#define LOWER(c) (unsigned char)(c | 0x20) -#define IS_ALPHA(c) (LOWER(c) >= 'a' && LOWER(c) <= 'z') -#define IS_NUM(c) ((c) >= '0' && (c) <= '9') -#define IS_ALPHANUM(c) (IS_ALPHA(c) || IS_NUM(c)) -#define IS_HEX(c) (IS_NUM(c) || (LOWER(c) >= 'a' && LOWER(c) <= 'f')) -#define IS_MARK(c) ((c) == '-' || (c) == '_' || (c) == '.' || \ - (c) == '!' || (c) == '~' || (c) == '*' || (c) == '\'' || (c) == '(' || \ - (c) == ')') -#define IS_USERINFO_CHAR(c) (IS_ALPHANUM(c) || IS_MARK(c) || (c) == '%' || \ - (c) == ';' || (c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' || \ - (c) == '$' || (c) == ',') - -#define STRICT_TOKEN(c) (tokens[(unsigned char)c]) +#define CR '\r' +#define LF '\n' +#define LOWER(c) (unsigned char)(c | 0x20) +#define IS_ALPHA(c) (LOWER(c) >= 'a' && LOWER(c) <= 'z') +#define IS_NUM(c) ((c) >= '0' && (c) <= '9') +#define IS_ALPHANUM(c) (IS_ALPHA(c) || IS_NUM(c)) +#define IS_HEX(c) (IS_NUM(c) || (LOWER(c) >= 'a' && LOWER(c) <= 'f')) +#define IS_MARK(c) ((c) == '-' || (c) == '_' || (c) == '.' || (c) == '!' || (c) == '~' || (c) == '*' || (c) == '\'' || (c) == '(' || (c) == ')') +#define IS_USERINFO_CHAR(c) (IS_ALPHANUM(c) || IS_MARK(c) || (c) == '%' || (c) == ';' || (c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' || (c) == '$' || (c) == ',') + +#define STRICT_TOKEN(c) (tokens[(unsigned char)c]) #if HTTP_PARSER_STRICT -#define TOKEN(c) (tokens[(unsigned char)c]) -#define IS_URL_CHAR(c) (BIT_AT(normal_url_char, (unsigned char)c)) -#define IS_HOST_CHAR(c) (IS_ALPHANUM(c) || (c) == '.' || (c) == '-') +#define TOKEN(c) (tokens[(unsigned char)c]) +#define IS_URL_CHAR(c) (BIT_AT(normal_url_char, (unsigned char)c)) +#define IS_HOST_CHAR(c) (IS_ALPHANUM(c) || (c) == '.' || (c) == '-') #else -#define TOKEN(c) ((c == ' ') ? ' ' : tokens[(unsigned char)c]) -#define IS_URL_CHAR(c) \ - (BIT_AT(normal_url_char, (unsigned char)c) || ((c) & 0x80)) -#define IS_HOST_CHAR(c) \ - (IS_ALPHANUM(c) || (c) == '.' || (c) == '-' || (c) == '_') +#define TOKEN(c) ((c == ' ') ? ' ' : tokens[(unsigned char)c]) +#define IS_URL_CHAR(c) \ + (BIT_AT(normal_url_char, (unsigned char)c) || ((c)&0x80)) +#define IS_HOST_CHAR(c) \ + (IS_ALPHANUM(c) || (c) == '.' || (c) == '-' || (c) == '_') #endif /** * Verify that a char is a valid visible (printable) US-ASCII * character or %x80-FF **/ -#define IS_HEADER_CHAR(ch) \ - (ch == CR || ch == LF || ch == 9 || ((unsigned char)ch > 31 && ch != 127)) +#define IS_HEADER_CHAR(ch) \ + (ch == CR || ch == LF || ch == 9 || ((unsigned char)ch > 31 && ch != 127)) #define start_state (parser->type == HTTP_REQUEST ? s_start_req : s_start_res) - #if HTTP_PARSER_STRICT -# define STRICT_CHECK(cond) \ -do { \ - if (cond) { \ - SET_ERRNO(HPE_STRICT); \ - goto error; \ - } \ -} while (0) -# define NEW_MESSAGE() (http_should_keep_alive(parser) ? start_state : s_dead) +#define STRICT_CHECK(cond) \ + do { \ + if (cond) { \ + SET_ERRNO(HPE_STRICT); \ + goto error; \ + } \ + } while (0) +#define NEW_MESSAGE() (http_should_keep_alive(parser) ? start_state : s_dead) #else -# define STRICT_CHECK(cond) -# define NEW_MESSAGE() start_state +#define STRICT_CHECK(cond) +#define NEW_MESSAGE() start_state #endif - /* Map errno values to strings for human-readable output */ #define HTTP_STRERROR_GEN(n, s) { "HPE_" #n, s }, static struct { - const char *name; - const char *description; + const char* name; + const char* description; } http_strerror_tab[] = { - HTTP_ERRNO_MAP(HTTP_STRERROR_GEN) + HTTP_ERRNO_MAP(HTTP_STRERROR_GEN) }; #undef HTTP_STRERROR_GEN -int http_message_needs_eof(const http_parser *parser); +int http_message_needs_eof(const http_parser* parser); /* Our URL parser. * @@ -486,1319 +471,1318 @@ int http_message_needs_eof(const http_parser *parser); static enum state parse_url_char(enum state s, const char ch) { - if (ch == ' ' || ch == '\r' || ch == '\n') { - return s_dead; - } + if (ch == ' ' || ch == '\r' || ch == '\n') { + return s_dead; + } #if HTTP_PARSER_STRICT - if (ch == '\t' || ch == '\f') { - return s_dead; - } + if (ch == '\t' || ch == '\f') { + return s_dead; + } #endif - switch (s) { + switch (s) { case s_req_spaces_before_url: - /* Proxied requests are followed by scheme of an absolute URI (alpha). + /* Proxied requests are followed by scheme of an absolute URI (alpha). * All methods except CONNECT are followed by '/' or '*'. */ - if (ch == '/' || ch == '*') { - return s_req_path; - } + if (ch == '/' || ch == '*') { + return s_req_path; + } - if (IS_ALPHA(ch)) { - return s_req_schema; - } + if (IS_ALPHA(ch)) { + return s_req_schema; + } - break; + break; case s_req_schema: - if (IS_ALPHA(ch)) { - return s; - } + if (IS_ALPHA(ch)) { + return s; + } - if (ch == ':') { - return s_req_schema_slash; - } + if (ch == ':') { + return s_req_schema_slash; + } - break; + break; case s_req_schema_slash: - if (ch == '/') { - return s_req_schema_slash_slash; - } + if (ch == '/') { + return s_req_schema_slash_slash; + } - break; + break; case s_req_schema_slash_slash: - if (ch == '/') { - return s_req_server_start; - } + if (ch == '/') { + return s_req_server_start; + } - break; + break; case s_req_server_with_at: - if (ch == '@') { - return s_dead; - } + if (ch == '@') { + return s_dead; + } /* FALLTHROUGH */ case s_req_server_start: case s_req_server: - if (ch == '/') { - return s_req_path; - } + if (ch == '/') { + return s_req_path; + } - if (ch == '?') { - return s_req_query_string_start; - } + if (ch == '?') { + return s_req_query_string_start; + } - if (ch == '@') { - return s_req_server_with_at; - } + if (ch == '@') { + return s_req_server_with_at; + } - if (IS_USERINFO_CHAR(ch) || ch == '[' || ch == ']') { - return s_req_server; - } + if (IS_USERINFO_CHAR(ch) || ch == '[' || ch == ']') { + return s_req_server; + } - break; + break; case s_req_path: - if (IS_URL_CHAR(ch)) { - return s; - } + if (IS_URL_CHAR(ch)) { + return s; + } - switch (ch) { + switch (ch) { case '?': - return s_req_query_string_start; + return s_req_query_string_start; case '#': - return s_req_fragment_start; - } + return s_req_fragment_start; + } - break; + break; case s_req_query_string_start: case s_req_query_string: - if (IS_URL_CHAR(ch)) { - return s_req_query_string; - } + if (IS_URL_CHAR(ch)) { + return s_req_query_string; + } - switch (ch) { + switch (ch) { case '?': - /* allow extra '?' in query string */ - return s_req_query_string; + /* allow extra '?' in query string */ + return s_req_query_string; case '#': - return s_req_fragment_start; - } + return s_req_fragment_start; + } - break; + break; case s_req_fragment_start: - if (IS_URL_CHAR(ch)) { - return s_req_fragment; - } + if (IS_URL_CHAR(ch)) { + return s_req_fragment; + } - switch (ch) { + switch (ch) { case '?': - return s_req_fragment; + return s_req_fragment; case '#': - return s; - } + return s; + } - break; + break; case s_req_fragment: - if (IS_URL_CHAR(ch)) { - return s; - } + if (IS_URL_CHAR(ch)) { + return s; + } - switch (ch) { + switch (ch) { case '?': case '#': - return s; - } + return s; + } - break; + break; default: - break; - } + break; + } - /* We should never fall out of the switch above unless there's an error */ - return s_dead; + /* We should never fall out of the switch above unless there's an error */ + return s_dead; } -size_t http_parser_execute (http_parser *parser, - const http_parser_settings *settings, - const char *data, - size_t len) +size_t http_parser_execute(http_parser* parser, + const http_parser_settings* settings, + const char* data, + size_t len) { - char c, ch; - int8_t unhex_val; - const char *p = data; - const char *header_field_mark = 0; - const char *header_value_mark = 0; - const char *url_mark = 0; - const char *body_mark = 0; - const char *status_mark = 0; - enum state p_state = (enum state) parser->state; - const unsigned int lenient = parser->lenient_http_headers; - - /* We're in an error state. Don't bother doing anything. */ - if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { - return 0; - } + char c, ch; + int8_t unhex_val; + const char* p = data; + const char* header_field_mark = 0; + const char* header_value_mark = 0; + const char* url_mark = 0; + const char* body_mark = 0; + const char* status_mark = 0; + enum state p_state = (enum state)parser->state; + const unsigned int lenient = parser->lenient_http_headers; + + /* We're in an error state. Don't bother doing anything. */ + if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { + return 0; + } - if (len == 0) { - switch (CURRENT_STATE()) { - case s_body_identity_eof: - /* Use of CALLBACK_NOTIFY() here would erroneously return 1 byte read if + if (len == 0) { + switch (CURRENT_STATE()) { + case s_body_identity_eof: + /* Use of CALLBACK_NOTIFY() here would erroneously return 1 byte read if * we got paused. */ - CALLBACK_NOTIFY_NOADVANCE(message_complete); - return 0; + CALLBACK_NOTIFY_NOADVANCE(message_complete); + return 0; - case s_dead: - case s_start_req_or_res: - case s_start_res: - case s_start_req: - return 0; + case s_dead: + case s_start_req_or_res: + case s_start_res: + case s_start_req: + return 0; - default: - SET_ERRNO(HPE_INVALID_EOF_STATE); - return 1; + default: + SET_ERRNO(HPE_INVALID_EOF_STATE); + return 1; + } } - } - - - if (CURRENT_STATE() == s_header_field) - header_field_mark = data; - if (CURRENT_STATE() == s_header_value) - header_value_mark = data; - switch (CURRENT_STATE()) { - case s_req_path: - case s_req_schema: - case s_req_schema_slash: - case s_req_schema_slash_slash: - case s_req_server_start: - case s_req_server: - case s_req_server_with_at: - case s_req_query_string_start: - case s_req_query_string: - case s_req_fragment_start: - case s_req_fragment: - url_mark = data; - break; - case s_res_status: - status_mark = data; - break; - default: - break; - } - - for (p=data; p != data + len; p++) { - ch = *p; - - if (PARSING_HEADER(CURRENT_STATE())) - COUNT_HEADER_SIZE(1); - -reexecute: + + if (CURRENT_STATE() == s_header_field) + header_field_mark = data; + if (CURRENT_STATE() == s_header_value) + header_value_mark = data; switch (CURRENT_STATE()) { + case s_req_path: + case s_req_schema: + case s_req_schema_slash: + case s_req_schema_slash_slash: + case s_req_server_start: + case s_req_server: + case s_req_server_with_at: + case s_req_query_string_start: + case s_req_query_string: + case s_req_fragment_start: + case s_req_fragment: + url_mark = data; + break; + case s_res_status: + status_mark = data; + break; + default: + break; + } + + for (p = data; p != data + len; p++) { + ch = *p; - case s_dead: - /* this state is used after a 'Connection: close' message + if (PARSING_HEADER(CURRENT_STATE())) + COUNT_HEADER_SIZE(1); + + reexecute: + switch (CURRENT_STATE()) { + + case s_dead: + /* this state is used after a 'Connection: close' message * the parser will error out if it reads another message */ - if (LIKELY(ch == CR || ch == LF)) - break; + if (LIKELY(ch == CR || ch == LF)) + break; - SET_ERRNO(HPE_CLOSED_CONNECTION); - goto error; - - case s_start_req_or_res: - { - if (ch == CR || ch == LF) - break; - parser->flags = 0; - parser->content_length = ULLONG_MAX; + SET_ERRNO(HPE_CLOSED_CONNECTION); + goto error; - if (ch == 'H') { - UPDATE_STATE(s_res_or_resp_H); + case s_start_req_or_res: { + if (ch == CR || ch == LF) + break; + parser->flags = 0; + parser->content_length = ULLONG_MAX; - CALLBACK_NOTIFY(message_begin); - } else { - parser->type = HTTP_REQUEST; - UPDATE_STATE(s_start_req); - REEXECUTE(); - } + if (ch == 'H') { + UPDATE_STATE(s_res_or_resp_H); - break; - } - - case s_res_or_resp_H: - if (ch == 'T') { - parser->type = HTTP_RESPONSE; - UPDATE_STATE(s_res_HT); - } else { - if (UNLIKELY(ch != 'E')) { - SET_ERRNO(HPE_INVALID_CONSTANT); - goto error; - } + CALLBACK_NOTIFY(message_begin); + } else { + parser->type = HTTP_REQUEST; + UPDATE_STATE(s_start_req); + REEXECUTE(); + } - parser->type = HTTP_REQUEST; - parser->method = HTTP_HEAD; - parser->index = 2; - UPDATE_STATE(s_req_method); + break; } - break; - case s_start_res: - { - parser->flags = 0; - parser->content_length = ULLONG_MAX; + case s_res_or_resp_H: + if (ch == 'T') { + parser->type = HTTP_RESPONSE; + UPDATE_STATE(s_res_HT); + } else { + if (UNLIKELY(ch != 'E')) { + SET_ERRNO(HPE_INVALID_CONSTANT); + goto error; + } - switch (ch) { - case 'H': - UPDATE_STATE(s_res_H); + parser->type = HTTP_REQUEST; + parser->method = HTTP_HEAD; + parser->index = 2; + UPDATE_STATE(s_req_method); + } break; - case CR: - case LF: - break; + case s_start_res: { + parser->flags = 0; + parser->content_length = ULLONG_MAX; - default: - SET_ERRNO(HPE_INVALID_CONSTANT); - goto error; + switch (ch) { + case 'H': + UPDATE_STATE(s_res_H); + break; + + case CR: + case LF: + break; + + default: + SET_ERRNO(HPE_INVALID_CONSTANT); + goto error; + } + + CALLBACK_NOTIFY(message_begin); + break; } - CALLBACK_NOTIFY(message_begin); - break; - } + case s_res_H: + STRICT_CHECK(ch != 'T'); + UPDATE_STATE(s_res_HT); + break; - case s_res_H: - STRICT_CHECK(ch != 'T'); - UPDATE_STATE(s_res_HT); - break; + case s_res_HT: + STRICT_CHECK(ch != 'T'); + UPDATE_STATE(s_res_HTT); + break; - case s_res_HT: - STRICT_CHECK(ch != 'T'); - UPDATE_STATE(s_res_HTT); - break; + case s_res_HTT: + STRICT_CHECK(ch != 'P'); + UPDATE_STATE(s_res_HTTP); + break; - case s_res_HTT: - STRICT_CHECK(ch != 'P'); - UPDATE_STATE(s_res_HTTP); - break; + case s_res_HTTP: + STRICT_CHECK(ch != '/'); + UPDATE_STATE(s_res_first_http_major); + break; - case s_res_HTTP: - STRICT_CHECK(ch != '/'); - UPDATE_STATE(s_res_first_http_major); - break; + case s_res_first_http_major: + if (UNLIKELY(ch < '0' || ch > '9')) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } - case s_res_first_http_major: - if (UNLIKELY(ch < '0' || ch > '9')) { - SET_ERRNO(HPE_INVALID_VERSION); - goto error; - } + parser->http_major = ch - '0'; + UPDATE_STATE(s_res_http_major); + break; - parser->http_major = ch - '0'; - UPDATE_STATE(s_res_http_major); - break; + /* major HTTP version or dot */ + case s_res_http_major: { + if (ch == '.') { + UPDATE_STATE(s_res_first_http_minor); + break; + } - /* major HTTP version or dot */ - case s_res_http_major: - { - if (ch == '.') { - UPDATE_STATE(s_res_first_http_minor); - break; - } + if (!IS_NUM(ch)) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } - if (!IS_NUM(ch)) { - SET_ERRNO(HPE_INVALID_VERSION); - goto error; - } + parser->http_major *= 10; + parser->http_major += ch - '0'; - parser->http_major *= 10; - parser->http_major += ch - '0'; + if (UNLIKELY(parser->http_major > 999)) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } - if (UNLIKELY(parser->http_major > 999)) { - SET_ERRNO(HPE_INVALID_VERSION); - goto error; + break; } - break; - } + /* first digit of minor HTTP version */ + case s_res_first_http_minor: + if (UNLIKELY(!IS_NUM(ch))) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } - /* first digit of minor HTTP version */ - case s_res_first_http_minor: - if (UNLIKELY(!IS_NUM(ch))) { - SET_ERRNO(HPE_INVALID_VERSION); - goto error; - } + parser->http_minor = ch - '0'; + UPDATE_STATE(s_res_http_minor); + break; - parser->http_minor = ch - '0'; - UPDATE_STATE(s_res_http_minor); - break; + /* minor HTTP version or end of request line */ + case s_res_http_minor: { + if (ch == ' ') { + UPDATE_STATE(s_res_first_status_code); + break; + } - /* minor HTTP version or end of request line */ - case s_res_http_minor: - { - if (ch == ' ') { - UPDATE_STATE(s_res_first_status_code); - break; - } + if (UNLIKELY(!IS_NUM(ch))) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } - if (UNLIKELY(!IS_NUM(ch))) { - SET_ERRNO(HPE_INVALID_VERSION); - goto error; - } + parser->http_minor *= 10; + parser->http_minor += ch - '0'; - parser->http_minor *= 10; - parser->http_minor += ch - '0'; + if (UNLIKELY(parser->http_minor > 999)) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } - if (UNLIKELY(parser->http_minor > 999)) { - SET_ERRNO(HPE_INVALID_VERSION); - goto error; + break; } - break; - } + case s_res_first_status_code: { + if (!IS_NUM(ch)) { + if (ch == ' ') { + break; + } - case s_res_first_status_code: - { - if (!IS_NUM(ch)) { - if (ch == ' ') { + SET_ERRNO(HPE_INVALID_STATUS); + goto error; + } + parser->status_code = ch - '0'; + UPDATE_STATE(s_res_status_code); break; - } - - SET_ERRNO(HPE_INVALID_STATUS); - goto error; } - parser->status_code = ch - '0'; - UPDATE_STATE(s_res_status_code); - break; - } - case s_res_status_code: - { - if (!IS_NUM(ch)) { - switch (ch) { - case ' ': - UPDATE_STATE(s_res_status_start); - break; - case CR: - UPDATE_STATE(s_res_line_almost_done); - break; - case LF: - UPDATE_STATE(s_header_field_start); - break; - default: - SET_ERRNO(HPE_INVALID_STATUS); - goto error; - } - break; - } + case s_res_status_code: { + if (!IS_NUM(ch)) { + switch (ch) { + case ' ': + UPDATE_STATE(s_res_status_start); + break; + case CR: + UPDATE_STATE(s_res_line_almost_done); + break; + case LF: + UPDATE_STATE(s_header_field_start); + break; + default: + SET_ERRNO(HPE_INVALID_STATUS); + goto error; + } + break; + } - parser->status_code *= 10; - parser->status_code += ch - '0'; + parser->status_code *= 10; + parser->status_code += ch - '0'; + + if (UNLIKELY(parser->status_code > 999)) { + SET_ERRNO(HPE_INVALID_STATUS); + goto error; + } - if (UNLIKELY(parser->status_code > 999)) { - SET_ERRNO(HPE_INVALID_STATUS); - goto error; + break; } - break; - } + case s_res_status_start: { + if (ch == CR) { + UPDATE_STATE(s_res_line_almost_done); + break; + } - case s_res_status_start: - { - if (ch == CR) { - UPDATE_STATE(s_res_line_almost_done); - break; - } + if (ch == LF) { + UPDATE_STATE(s_header_field_start); + break; + } - if (ch == LF) { - UPDATE_STATE(s_header_field_start); - break; + MARK(status); + UPDATE_STATE(s_res_status); + parser->index = 0; + break; } - MARK(status); - UPDATE_STATE(s_res_status); - parser->index = 0; - break; - } + case s_res_status: + if (ch == CR) { + UPDATE_STATE(s_res_line_almost_done); + CALLBACK_DATA(status); + break; + } - case s_res_status: - if (ch == CR) { - UPDATE_STATE(s_res_line_almost_done); - CALLBACK_DATA(status); - break; - } + if (ch == LF) { + UPDATE_STATE(s_header_field_start); + CALLBACK_DATA(status); + break; + } - if (ch == LF) { - UPDATE_STATE(s_header_field_start); - CALLBACK_DATA(status); - break; - } + break; - break; + case s_res_line_almost_done: + STRICT_CHECK(ch != LF); + UPDATE_STATE(s_header_field_start); + break; - case s_res_line_almost_done: - STRICT_CHECK(ch != LF); - UPDATE_STATE(s_header_field_start); - break; + case s_start_req: { + if (ch == CR || ch == LF) + break; + parser->flags = 0; + parser->content_length = ULLONG_MAX; - case s_start_req: - { - if (ch == CR || ch == LF) - break; - parser->flags = 0; - parser->content_length = ULLONG_MAX; + if (UNLIKELY(!IS_ALPHA(ch))) { + SET_ERRNO(HPE_INVALID_METHOD); + goto error; + } - if (UNLIKELY(!IS_ALPHA(ch))) { - SET_ERRNO(HPE_INVALID_METHOD); - goto error; - } + parser->method = (enum http_method)0; + parser->index = 1; + switch (ch) { + case 'A': + parser->method = HTTP_ACL; + break; + case 'B': + parser->method = HTTP_BIND; + break; + case 'C': + parser->method = HTTP_CONNECT; /* or COPY, CHECKOUT */ + break; + case 'D': + parser->method = HTTP_DELETE; + break; + case 'G': + parser->method = HTTP_GET; + break; + case 'H': + parser->method = HTTP_HEAD; + break; + case 'L': + parser->method = HTTP_LOCK; /* or LINK */ + break; + case 'M': + parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH, MKCALENDAR */ + break; + case 'N': + parser->method = HTTP_NOTIFY; + break; + case 'O': + parser->method = HTTP_OPTIONS; + break; + case 'P': + parser->method = HTTP_POST; + /* or PROPFIND|PROPPATCH|PUT|PATCH|PURGE */ + break; + case 'R': + parser->method = HTTP_REPORT; /* or REBIND */ + break; + case 'S': + parser->method = HTTP_SUBSCRIBE; /* or SEARCH */ + break; + case 'T': + parser->method = HTTP_TRACE; + break; + case 'U': + parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE, UNBIND, UNLINK */ + break; + default: + SET_ERRNO(HPE_INVALID_METHOD); + goto error; + } + UPDATE_STATE(s_req_method); + + CALLBACK_NOTIFY(message_begin); - parser->method = (enum http_method) 0; - parser->index = 1; - switch (ch) { - case 'A': parser->method = HTTP_ACL; break; - case 'B': parser->method = HTTP_BIND; break; - case 'C': parser->method = HTTP_CONNECT; /* or COPY, CHECKOUT */ break; - case 'D': parser->method = HTTP_DELETE; break; - case 'G': parser->method = HTTP_GET; break; - case 'H': parser->method = HTTP_HEAD; break; - case 'L': parser->method = HTTP_LOCK; /* or LINK */ break; - case 'M': parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH, MKCALENDAR */ break; - case 'N': parser->method = HTTP_NOTIFY; break; - case 'O': parser->method = HTTP_OPTIONS; break; - case 'P': parser->method = HTTP_POST; - /* or PROPFIND|PROPPATCH|PUT|PATCH|PURGE */ break; - case 'R': parser->method = HTTP_REPORT; /* or REBIND */ break; - case 'S': parser->method = HTTP_SUBSCRIBE; /* or SEARCH */ break; - case 'T': parser->method = HTTP_TRACE; break; - case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE, UNBIND, UNLINK */ break; - default: - SET_ERRNO(HPE_INVALID_METHOD); - goto error; } - UPDATE_STATE(s_req_method); - CALLBACK_NOTIFY(message_begin); + case s_req_method: { + const char* matcher; + if (UNLIKELY(ch == '\0')) { + SET_ERRNO(HPE_INVALID_METHOD); + goto error; + } + matcher = method_strings[parser->method]; + if (ch == ' ' && matcher[parser->index] == '\0') { + UPDATE_STATE(s_req_spaces_before_url); + } else if (ch == matcher[parser->index]) { + ; /* nada */ + } else if (IS_ALPHA(ch)) { + + switch (parser->method << 16 | parser->index << 8 | ch) { +#define XX(meth, pos, ch, new_meth) \ + case (HTTP_##meth << 16 | pos << 8 | ch): \ + parser->method = HTTP_##new_meth; \ break; - } - - case s_req_method: - { - const char *matcher; - if (UNLIKELY(ch == '\0')) { - SET_ERRNO(HPE_INVALID_METHOD); - goto error; - } - - matcher = method_strings[parser->method]; - if (ch == ' ' && matcher[parser->index] == '\0') { - UPDATE_STATE(s_req_spaces_before_url); - } else if (ch == matcher[parser->index]) { - ; /* nada */ - } else if (IS_ALPHA(ch)) { - - switch (parser->method << 16 | parser->index << 8 | ch) { -#define XX(meth, pos, ch, new_meth) \ - case (HTTP_##meth << 16 | pos << 8 | ch): \ - parser->method = HTTP_##new_meth; break; - - XX(POST, 1, 'U', PUT) - XX(POST, 1, 'A', PATCH) - XX(CONNECT, 1, 'H', CHECKOUT) - XX(CONNECT, 2, 'P', COPY) - XX(MKCOL, 1, 'O', MOVE) - XX(MKCOL, 1, 'E', MERGE) - XX(MKCOL, 2, 'A', MKACTIVITY) - XX(MKCOL, 3, 'A', MKCALENDAR) - XX(SUBSCRIBE, 1, 'E', SEARCH) - XX(REPORT, 2, 'B', REBIND) - XX(POST, 1, 'R', PROPFIND) - XX(PROPFIND, 4, 'P', PROPPATCH) - XX(PUT, 2, 'R', PURGE) - XX(LOCK, 1, 'I', LINK) - XX(UNLOCK, 2, 'S', UNSUBSCRIBE) - XX(UNLOCK, 2, 'B', UNBIND) - XX(UNLOCK, 3, 'I', UNLINK) -#undef XX - default: - SET_ERRNO(HPE_INVALID_METHOD); - goto error; - } - } else if (ch == '-' && - parser->index == 1 && - parser->method == HTTP_MKCOL) { - parser->method = HTTP_MSEARCH; - } else { - SET_ERRNO(HPE_INVALID_METHOD); - goto error; - } - - ++parser->index; - break; - } + XX(POST, 1, 'U', PUT) + XX(POST, 1, 'A', PATCH) + XX(CONNECT, 1, 'H', CHECKOUT) + XX(CONNECT, 2, 'P', COPY) + XX(MKCOL, 1, 'O', MOVE) + XX(MKCOL, 1, 'E', MERGE) + XX(MKCOL, 2, 'A', MKACTIVITY) + XX(MKCOL, 3, 'A', MKCALENDAR) + XX(SUBSCRIBE, 1, 'E', SEARCH) + XX(REPORT, 2, 'B', REBIND) + XX(POST, 1, 'R', PROPFIND) + XX(PROPFIND, 4, 'P', PROPPATCH) + XX(PUT, 2, 'R', PURGE) + XX(LOCK, 1, 'I', LINK) + XX(UNLOCK, 2, 'S', UNSUBSCRIBE) + XX(UNLOCK, 2, 'B', UNBIND) + XX(UNLOCK, 3, 'I', UNLINK) +#undef XX - case s_req_spaces_before_url: - { - if (ch == ' ') break; + default: + SET_ERRNO(HPE_INVALID_METHOD); + goto error; + } + } else if (ch == '-' && parser->index == 1 && parser->method == HTTP_MKCOL) { + parser->method = HTTP_MSEARCH; + } else { + SET_ERRNO(HPE_INVALID_METHOD); + goto error; + } - MARK(url); - if (parser->method == HTTP_CONNECT) { - UPDATE_STATE(s_req_server_start); + ++parser->index; + break; } - UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch)); - if (UNLIKELY(CURRENT_STATE() == s_dead)) { - SET_ERRNO(HPE_INVALID_URL); - goto error; - } + case s_req_spaces_before_url: { + if (ch == ' ') + break; - break; - } + MARK(url); + if (parser->method == HTTP_CONNECT) { + UPDATE_STATE(s_req_server_start); + } - case s_req_schema: - case s_req_schema_slash: - case s_req_schema_slash_slash: - case s_req_server_start: - { - switch (ch) { - /* No whitespace allowed here */ - case ' ': - case CR: - case LF: - SET_ERRNO(HPE_INVALID_URL); - goto error; - default: UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch)); if (UNLIKELY(CURRENT_STATE() == s_dead)) { - SET_ERRNO(HPE_INVALID_URL); - goto error; + SET_ERRNO(HPE_INVALID_URL); + goto error; } - } - break; - } - - case s_req_server: - case s_req_server_with_at: - case s_req_path: - case s_req_query_string_start: - case s_req_query_string: - case s_req_fragment_start: - case s_req_fragment: - { - switch (ch) { - case ' ': - UPDATE_STATE(s_req_http_start); - CALLBACK_DATA(url); break; - case CR: - case LF: - parser->http_major = 0; - parser->http_minor = 9; - UPDATE_STATE((ch == CR) ? - s_req_line_almost_done : - s_header_field_start); - CALLBACK_DATA(url); - break; - default: - UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch)); - if (UNLIKELY(CURRENT_STATE() == s_dead)) { - SET_ERRNO(HPE_INVALID_URL); - goto error; - } } - break; - } - case s_req_http_start: - switch (ch) { - case 'H': - UPDATE_STATE(s_req_http_H); - break; - case ' ': + case s_req_schema: + case s_req_schema_slash: + case s_req_schema_slash_slash: + case s_req_server_start: { + switch (ch) { + /* No whitespace allowed here */ + case ' ': + case CR: + case LF: + SET_ERRNO(HPE_INVALID_URL); + goto error; + default: + UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch)); + if (UNLIKELY(CURRENT_STATE() == s_dead)) { + SET_ERRNO(HPE_INVALID_URL); + goto error; + } + } + break; - default: - SET_ERRNO(HPE_INVALID_CONSTANT); - goto error; } - break; - case s_req_http_H: - STRICT_CHECK(ch != 'T'); - UPDATE_STATE(s_req_http_HT); - break; + case s_req_server: + case s_req_server_with_at: + case s_req_path: + case s_req_query_string_start: + case s_req_query_string: + case s_req_fragment_start: + case s_req_fragment: { + switch (ch) { + case ' ': + UPDATE_STATE(s_req_http_start); + CALLBACK_DATA(url); + break; + case CR: + case LF: + parser->http_major = 0; + parser->http_minor = 9; + UPDATE_STATE((ch == CR) ? s_req_line_almost_done : s_header_field_start); + CALLBACK_DATA(url); + break; + default: + UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch)); + if (UNLIKELY(CURRENT_STATE() == s_dead)) { + SET_ERRNO(HPE_INVALID_URL); + goto error; + } + } + break; + } - case s_req_http_HT: - STRICT_CHECK(ch != 'T'); - UPDATE_STATE(s_req_http_HTT); - break; + case s_req_http_start: + switch (ch) { + case 'H': + UPDATE_STATE(s_req_http_H); + break; + case ' ': + break; + default: + SET_ERRNO(HPE_INVALID_CONSTANT); + goto error; + } + break; - case s_req_http_HTT: - STRICT_CHECK(ch != 'P'); - UPDATE_STATE(s_req_http_HTTP); - break; + case s_req_http_H: + STRICT_CHECK(ch != 'T'); + UPDATE_STATE(s_req_http_HT); + break; - case s_req_http_HTTP: - STRICT_CHECK(ch != '/'); - UPDATE_STATE(s_req_first_http_major); - break; + case s_req_http_HT: + STRICT_CHECK(ch != 'T'); + UPDATE_STATE(s_req_http_HTT); + break; - /* first digit of major HTTP version */ - case s_req_first_http_major: - if (UNLIKELY(ch < '1' || ch > '9')) { - SET_ERRNO(HPE_INVALID_VERSION); - goto error; - } + case s_req_http_HTT: + STRICT_CHECK(ch != 'P'); + UPDATE_STATE(s_req_http_HTTP); + break; - parser->http_major = ch - '0'; - UPDATE_STATE(s_req_http_major); - break; + case s_req_http_HTTP: + STRICT_CHECK(ch != '/'); + UPDATE_STATE(s_req_first_http_major); + break; - /* major HTTP version or dot */ - case s_req_http_major: - { - if (ch == '.') { - UPDATE_STATE(s_req_first_http_minor); - break; - } + /* first digit of major HTTP version */ + case s_req_first_http_major: + if (UNLIKELY(ch < '1' || ch > '9')) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } - if (UNLIKELY(!IS_NUM(ch))) { - SET_ERRNO(HPE_INVALID_VERSION); - goto error; - } + parser->http_major = ch - '0'; + UPDATE_STATE(s_req_http_major); + break; - parser->http_major *= 10; - parser->http_major += ch - '0'; + /* major HTTP version or dot */ + case s_req_http_major: { + if (ch == '.') { + UPDATE_STATE(s_req_first_http_minor); + break; + } - if (UNLIKELY(parser->http_major > 999)) { - SET_ERRNO(HPE_INVALID_VERSION); - goto error; - } + if (UNLIKELY(!IS_NUM(ch))) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } - break; - } + parser->http_major *= 10; + parser->http_major += ch - '0'; - /* first digit of minor HTTP version */ - case s_req_first_http_minor: - if (UNLIKELY(!IS_NUM(ch))) { - SET_ERRNO(HPE_INVALID_VERSION); - goto error; + if (UNLIKELY(parser->http_major > 999)) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + break; } - parser->http_minor = ch - '0'; - UPDATE_STATE(s_req_http_minor); - break; + /* first digit of minor HTTP version */ + case s_req_first_http_minor: + if (UNLIKELY(!IS_NUM(ch))) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } - /* minor HTTP version or end of request line */ - case s_req_http_minor: - { - if (ch == CR) { - UPDATE_STATE(s_req_line_almost_done); - break; - } + parser->http_minor = ch - '0'; + UPDATE_STATE(s_req_http_minor); + break; - if (ch == LF) { - UPDATE_STATE(s_header_field_start); - break; - } + /* minor HTTP version or end of request line */ + case s_req_http_minor: { + if (ch == CR) { + UPDATE_STATE(s_req_line_almost_done); + break; + } - /* XXX allow spaces after digit? */ + if (ch == LF) { + UPDATE_STATE(s_header_field_start); + break; + } - if (UNLIKELY(!IS_NUM(ch))) { - SET_ERRNO(HPE_INVALID_VERSION); - goto error; - } + /* XXX allow spaces after digit? */ - parser->http_minor *= 10; - parser->http_minor += ch - '0'; + if (UNLIKELY(!IS_NUM(ch))) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } - if (UNLIKELY(parser->http_minor > 999)) { - SET_ERRNO(HPE_INVALID_VERSION); - goto error; - } + parser->http_minor *= 10; + parser->http_minor += ch - '0'; - break; - } + if (UNLIKELY(parser->http_minor > 999)) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } - /* end of request line */ - case s_req_line_almost_done: - { - if (UNLIKELY(ch != LF)) { - SET_ERRNO(HPE_LF_EXPECTED); - goto error; + break; } - UPDATE_STATE(s_header_field_start); - break; - } + /* end of request line */ + case s_req_line_almost_done: { + if (UNLIKELY(ch != LF)) { + SET_ERRNO(HPE_LF_EXPECTED); + goto error; + } - case s_header_field_start: - { - if (ch == CR) { - UPDATE_STATE(s_headers_almost_done); - break; + UPDATE_STATE(s_header_field_start); + break; } - if (ch == LF) { - /* they might be just sending \n instead of \r\n so this would be + case s_header_field_start: { + if (ch == CR) { + UPDATE_STATE(s_headers_almost_done); + break; + } + + if (ch == LF) { + /* they might be just sending \n instead of \r\n so this would be * the second \n to denote the end of headers*/ - UPDATE_STATE(s_headers_almost_done); - REEXECUTE(); - } + UPDATE_STATE(s_headers_almost_done); + REEXECUTE(); + } - c = TOKEN(ch); + c = TOKEN(ch); - if (UNLIKELY(!c)) { - SET_ERRNO(HPE_INVALID_HEADER_TOKEN); - goto error; - } + if (UNLIKELY(!c)) { + SET_ERRNO(HPE_INVALID_HEADER_TOKEN); + goto error; + } - MARK(header_field); + MARK(header_field); - parser->index = 0; - UPDATE_STATE(s_header_field); + parser->index = 0; + UPDATE_STATE(s_header_field); - switch (c) { - case 'c': - parser->header_state = h_C; - break; + switch (c) { + case 'c': + parser->header_state = h_C; + break; - case 'p': - parser->header_state = h_matching_proxy_connection; - break; + case 'p': + parser->header_state = h_matching_proxy_connection; + break; - case 't': - parser->header_state = h_matching_transfer_encoding; - break; + case 't': + parser->header_state = h_matching_transfer_encoding; + break; - case 'u': - parser->header_state = h_matching_upgrade; - break; + case 'u': + parser->header_state = h_matching_upgrade; + break; - default: - parser->header_state = h_general; + default: + parser->header_state = h_general; + break; + } break; } - break; - } - case s_header_field: - { - const char* start = p; - for (; p != data + len; p++) { - ch = *p; - c = TOKEN(ch); + case s_header_field: { + const char* start = p; + for (; p != data + len; p++) { + ch = *p; + c = TOKEN(ch); + + if (!c) + break; + + switch (parser->header_state) { + case h_general: + break; + + case h_C: + parser->index++; + parser->header_state = (c == 'o' ? h_CO : h_general); + break; + + case h_CO: + parser->index++; + parser->header_state = (c == 'n' ? h_CON : h_general); + break; + + case h_CON: + parser->index++; + switch (c) { + case 'n': + parser->header_state = h_matching_connection; + break; + case 't': + parser->header_state = h_matching_content_length; + break; + default: + parser->header_state = h_general; + break; + } + break; + + /* connection */ + + case h_matching_connection: + parser->index++; + if (parser->index > sizeof(CONNECTION) - 1 + || c != CONNECTION[parser->index]) { + parser->header_state = h_general; + } else if (parser->index == sizeof(CONNECTION) - 2) { + parser->header_state = h_connection; + } + break; + + /* proxy-connection */ + + case h_matching_proxy_connection: + parser->index++; + if (parser->index > sizeof(PROXY_CONNECTION) - 1 + || c != PROXY_CONNECTION[parser->index]) { + parser->header_state = h_general; + } else if (parser->index == sizeof(PROXY_CONNECTION) - 2) { + parser->header_state = h_connection; + } + break; + + /* content-length */ + + case h_matching_content_length: + parser->index++; + if (parser->index > sizeof(CONTENT_LENGTH) - 1 + || c != CONTENT_LENGTH[parser->index]) { + parser->header_state = h_general; + } else if (parser->index == sizeof(CONTENT_LENGTH) - 2) { + if (parser->flags & F_CONTENTLENGTH) { + SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH); + goto error; + } + parser->header_state = h_content_length; + parser->flags |= F_CONTENTLENGTH; + } + break; + + /* transfer-encoding */ + + case h_matching_transfer_encoding: + parser->index++; + if (parser->index > sizeof(TRANSFER_ENCODING) - 1 + || c != TRANSFER_ENCODING[parser->index]) { + parser->header_state = h_general; + } else if (parser->index == sizeof(TRANSFER_ENCODING) - 2) { + parser->header_state = h_transfer_encoding; + } + break; + + /* upgrade */ + + case h_matching_upgrade: + parser->index++; + if (parser->index > sizeof(UPGRADE) - 1 + || c != UPGRADE[parser->index]) { + parser->header_state = h_general; + } else if (parser->index == sizeof(UPGRADE) - 2) { + parser->header_state = h_upgrade; + } + break; + + case h_connection: + case h_content_length: + case h_transfer_encoding: + case h_upgrade: + if (ch != ' ') + parser->header_state = h_general; + break; - if (!c) - break; - - switch (parser->header_state) { - case h_general: - break; - - case h_C: - parser->index++; - parser->header_state = (c == 'o' ? h_CO : h_general); - break; - - case h_CO: - parser->index++; - parser->header_state = (c == 'n' ? h_CON : h_general); - break; - - case h_CON: - parser->index++; - switch (c) { - case 'n': - parser->header_state = h_matching_connection; - break; - case 't': - parser->header_state = h_matching_content_length; - break; default: - parser->header_state = h_general; - break; - } - break; - - /* connection */ - - case h_matching_connection: - parser->index++; - if (parser->index > sizeof(CONNECTION)-1 - || c != CONNECTION[parser->index]) { - parser->header_state = h_general; - } else if (parser->index == sizeof(CONNECTION)-2) { - parser->header_state = h_connection; - } - break; - - /* proxy-connection */ - - case h_matching_proxy_connection: - parser->index++; - if (parser->index > sizeof(PROXY_CONNECTION)-1 - || c != PROXY_CONNECTION[parser->index]) { - parser->header_state = h_general; - } else if (parser->index == sizeof(PROXY_CONNECTION)-2) { - parser->header_state = h_connection; - } - break; - - /* content-length */ - - case h_matching_content_length: - parser->index++; - if (parser->index > sizeof(CONTENT_LENGTH)-1 - || c != CONTENT_LENGTH[parser->index]) { - parser->header_state = h_general; - } else if (parser->index == sizeof(CONTENT_LENGTH)-2) { - if (parser->flags & F_CONTENTLENGTH) { - SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH); - goto error; + assert(0 && "Unknown header_state"); + break; } - parser->header_state = h_content_length; - parser->flags |= F_CONTENTLENGTH; - } - break; - - /* transfer-encoding */ - - case h_matching_transfer_encoding: - parser->index++; - if (parser->index > sizeof(TRANSFER_ENCODING)-1 - || c != TRANSFER_ENCODING[parser->index]) { - parser->header_state = h_general; - } else if (parser->index == sizeof(TRANSFER_ENCODING)-2) { - parser->header_state = h_transfer_encoding; - } - break; + } - /* upgrade */ + COUNT_HEADER_SIZE(p - start); - case h_matching_upgrade: - parser->index++; - if (parser->index > sizeof(UPGRADE)-1 - || c != UPGRADE[parser->index]) { - parser->header_state = h_general; - } else if (parser->index == sizeof(UPGRADE)-2) { - parser->header_state = h_upgrade; - } - break; + if (p == data + len) { + --p; + break; + } - case h_connection: - case h_content_length: - case h_transfer_encoding: - case h_upgrade: - if (ch != ' ') parser->header_state = h_general; - break; + if (ch == ':') { + UPDATE_STATE(s_header_value_discard_ws); + CALLBACK_DATA(header_field); + break; + } - default: - assert(0 && "Unknown header_state"); - break; - } + SET_ERRNO(HPE_INVALID_HEADER_TOKEN); + goto error; } - COUNT_HEADER_SIZE(p - start); + case s_header_value_discard_ws: + if (ch == ' ' || ch == '\t') + break; - if (p == data + len) { - --p; - break; - } + if (ch == CR) { + UPDATE_STATE(s_header_value_discard_ws_almost_done); + break; + } - if (ch == ':') { - UPDATE_STATE(s_header_value_discard_ws); - CALLBACK_DATA(header_field); - break; - } + if (ch == LF) { + UPDATE_STATE(s_header_value_discard_lws); + break; + } - SET_ERRNO(HPE_INVALID_HEADER_TOKEN); - goto error; - } + /* FALLTHROUGH */ - case s_header_value_discard_ws: - if (ch == ' ' || ch == '\t') break; + case s_header_value_start: { + MARK(header_value); - if (ch == CR) { - UPDATE_STATE(s_header_value_discard_ws_almost_done); - break; - } + UPDATE_STATE(s_header_value); + parser->index = 0; - if (ch == LF) { - UPDATE_STATE(s_header_value_discard_lws); - break; - } + c = LOWER(ch); - /* FALLTHROUGH */ + switch (parser->header_state) { + case h_upgrade: + parser->flags |= F_UPGRADE; + parser->header_state = h_general; + break; - case s_header_value_start: - { - MARK(header_value); + case h_transfer_encoding: + /* looking for 'Transfer-Encoding: chunked' */ + if ('c' == c) { + parser->header_state = h_matching_transfer_encoding_chunked; + } else { + parser->header_state = h_general; + } + break; - UPDATE_STATE(s_header_value); - parser->index = 0; + case h_content_length: + if (UNLIKELY(!IS_NUM(ch))) { + SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); + goto error; + } - c = LOWER(ch); + parser->content_length = ch - '0'; + break; - switch (parser->header_state) { - case h_upgrade: - parser->flags |= F_UPGRADE; - parser->header_state = h_general; - break; + case h_connection: + /* looking for 'Connection: keep-alive' */ + if (c == 'k') { + parser->header_state = h_matching_connection_keep_alive; + /* looking for 'Connection: close' */ + } else if (c == 'c') { + parser->header_state = h_matching_connection_close; + } else if (c == 'u') { + parser->header_state = h_matching_connection_upgrade; + } else { + parser->header_state = h_matching_connection_token; + } + break; - case h_transfer_encoding: - /* looking for 'Transfer-Encoding: chunked' */ - if ('c' == c) { - parser->header_state = h_matching_transfer_encoding_chunked; - } else { - parser->header_state = h_general; - } - break; + /* Multi-value `Connection` header */ + case h_matching_connection_token_start: + break; - case h_content_length: - if (UNLIKELY(!IS_NUM(ch))) { - SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); - goto error; + default: + parser->header_state = h_general; + break; } - - parser->content_length = ch - '0'; break; + } - case h_connection: - /* looking for 'Connection: keep-alive' */ - if (c == 'k') { - parser->header_state = h_matching_connection_keep_alive; - /* looking for 'Connection: close' */ - } else if (c == 'c') { - parser->header_state = h_matching_connection_close; - } else if (c == 'u') { - parser->header_state = h_matching_connection_upgrade; - } else { - parser->header_state = h_matching_connection_token; - } - break; + case s_header_value: { + const char* start = p; + enum header_states h_state = (enum header_states)parser->header_state; + for (; p != data + len; p++) { + ch = *p; + if (ch == CR) { + UPDATE_STATE(s_header_almost_done); + parser->header_state = h_state; + CALLBACK_DATA(header_value); + break; + } - /* Multi-value `Connection` header */ - case h_matching_connection_token_start: - break; + if (ch == LF) { + UPDATE_STATE(s_header_almost_done); + COUNT_HEADER_SIZE(p - start); + parser->header_state = h_state; + CALLBACK_DATA_NOADVANCE(header_value); + REEXECUTE(); + } - default: - parser->header_state = h_general; - break; - } - break; - } - - case s_header_value: - { - const char* start = p; - enum header_states h_state = (enum header_states) parser->header_state; - for (; p != data + len; p++) { - ch = *p; - if (ch == CR) { - UPDATE_STATE(s_header_almost_done); - parser->header_state = h_state; - CALLBACK_DATA(header_value); - break; - } + if (!lenient && !IS_HEADER_CHAR(ch)) { + SET_ERRNO(HPE_INVALID_HEADER_TOKEN); + goto error; + } - if (ch == LF) { - UPDATE_STATE(s_header_almost_done); - COUNT_HEADER_SIZE(p - start); - parser->header_state = h_state; - CALLBACK_DATA_NOADVANCE(header_value); - REEXECUTE(); - } + c = LOWER(ch); + + switch (h_state) { + case h_general: { + const char* p_cr; + const char* p_lf; + size_t limit = data + len - p; + + limit = MIN(limit, HTTP_MAX_HEADER_SIZE); + + p_cr = (const char*)memchr(p, CR, limit); + p_lf = (const char*)memchr(p, LF, limit); + if (p_cr != NULL) { + if (p_lf != NULL && p_cr >= p_lf) + p = p_lf; + else + p = p_cr; + } else if (UNLIKELY(p_lf != NULL)) { + p = p_lf; + } else { + p = data + len; + } + --p; + + break; + } - if (!lenient && !IS_HEADER_CHAR(ch)) { - SET_ERRNO(HPE_INVALID_HEADER_TOKEN); - goto error; - } + case h_connection: + case h_transfer_encoding: + assert(0 && "Shouldn't get here."); + break; - c = LOWER(ch); + case h_content_length: { + uint64_t t; - switch (h_state) { - case h_general: - { - const char* p_cr; - const char* p_lf; - size_t limit = data + len - p; + if (ch == ' ') + break; - limit = MIN(limit, HTTP_MAX_HEADER_SIZE); + if (UNLIKELY(!IS_NUM(ch))) { + SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); + parser->header_state = h_state; + goto error; + } - p_cr = (const char*) memchr(p, CR, limit); - p_lf = (const char*) memchr(p, LF, limit); - if (p_cr != NULL) { - if (p_lf != NULL && p_cr >= p_lf) - p = p_lf; - else - p = p_cr; - } else if (UNLIKELY(p_lf != NULL)) { - p = p_lf; - } else { - p = data + len; - } - --p; + t = parser->content_length; + t *= 10; + t += ch - '0'; - break; - } + /* Overflow? Test against a conservative limit for simplicity. */ + if (UNLIKELY((ULLONG_MAX - 10) / 10 < parser->content_length)) { + SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); + parser->header_state = h_state; + goto error; + } - case h_connection: - case h_transfer_encoding: - assert(0 && "Shouldn't get here."); - break; + parser->content_length = t; + break; + } - case h_content_length: - { - uint64_t t; + /* Transfer-Encoding: chunked */ + case h_matching_transfer_encoding_chunked: + parser->index++; + if (parser->index > sizeof(CHUNKED) - 1 + || c != CHUNKED[parser->index]) { + h_state = h_general; + } else if (parser->index == sizeof(CHUNKED) - 2) { + h_state = h_transfer_encoding_chunked; + } + break; + + case h_matching_connection_token_start: + /* looking for 'Connection: keep-alive' */ + if (c == 'k') { + h_state = h_matching_connection_keep_alive; + /* looking for 'Connection: close' */ + } else if (c == 'c') { + h_state = h_matching_connection_close; + } else if (c == 'u') { + h_state = h_matching_connection_upgrade; + } else if (STRICT_TOKEN(c)) { + h_state = h_matching_connection_token; + } else if (c == ' ' || c == '\t') { + /* Skip lws */ + } else { + h_state = h_general; + } + break; + + /* looking for 'Connection: keep-alive' */ + case h_matching_connection_keep_alive: + parser->index++; + if (parser->index > sizeof(KEEP_ALIVE) - 1 + || c != KEEP_ALIVE[parser->index]) { + h_state = h_matching_connection_token; + } else if (parser->index == sizeof(KEEP_ALIVE) - 2) { + h_state = h_connection_keep_alive; + } + break; + + /* looking for 'Connection: close' */ + case h_matching_connection_close: + parser->index++; + if (parser->index > sizeof(CLOSE) - 1 || c != CLOSE[parser->index]) { + h_state = h_matching_connection_token; + } else if (parser->index == sizeof(CLOSE) - 2) { + h_state = h_connection_close; + } + break; + + /* looking for 'Connection: upgrade' */ + case h_matching_connection_upgrade: + parser->index++; + if (parser->index > sizeof(UPGRADE) - 1 || c != UPGRADE[parser->index]) { + h_state = h_matching_connection_token; + } else if (parser->index == sizeof(UPGRADE) - 2) { + h_state = h_connection_upgrade; + } + break; + + case h_matching_connection_token: + if (ch == ',') { + h_state = h_matching_connection_token_start; + parser->index = 0; + } + break; + + case h_transfer_encoding_chunked: + if (ch != ' ') + h_state = h_general; + break; + + case h_connection_keep_alive: + case h_connection_close: + case h_connection_upgrade: + if (ch == ',') { + if (h_state == h_connection_keep_alive) { + parser->flags |= F_CONNECTION_KEEP_ALIVE; + } else if (h_state == h_connection_close) { + parser->flags |= F_CONNECTION_CLOSE; + } else if (h_state == h_connection_upgrade) { + parser->flags |= F_CONNECTION_UPGRADE; + } + h_state = h_matching_connection_token_start; + parser->index = 0; + } else if (ch != ' ') { + h_state = h_matching_connection_token; + } + break; - if (ch == ' ') break; + default: + UPDATE_STATE(s_header_value); + h_state = h_general; + break; + } + } + parser->header_state = h_state; - if (UNLIKELY(!IS_NUM(ch))) { - SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); - parser->header_state = h_state; - goto error; - } + COUNT_HEADER_SIZE(p - start); - t = parser->content_length; - t *= 10; - t += ch - '0'; + if (p == data + len) + --p; + break; + } - /* Overflow? Test against a conservative limit for simplicity. */ - if (UNLIKELY((ULLONG_MAX - 10) / 10 < parser->content_length)) { - SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); - parser->header_state = h_state; + case s_header_almost_done: { + if (UNLIKELY(ch != LF)) { + SET_ERRNO(HPE_LF_EXPECTED); goto error; - } - - parser->content_length = t; - break; } - /* Transfer-Encoding: chunked */ - case h_matching_transfer_encoding_chunked: - parser->index++; - if (parser->index > sizeof(CHUNKED)-1 - || c != CHUNKED[parser->index]) { - h_state = h_general; - } else if (parser->index == sizeof(CHUNKED)-2) { - h_state = h_transfer_encoding_chunked; - } - break; - - case h_matching_connection_token_start: - /* looking for 'Connection: keep-alive' */ - if (c == 'k') { - h_state = h_matching_connection_keep_alive; - /* looking for 'Connection: close' */ - } else if (c == 'c') { - h_state = h_matching_connection_close; - } else if (c == 'u') { - h_state = h_matching_connection_upgrade; - } else if (STRICT_TOKEN(c)) { - h_state = h_matching_connection_token; - } else if (c == ' ' || c == '\t') { - /* Skip lws */ - } else { - h_state = h_general; - } - break; - - /* looking for 'Connection: keep-alive' */ - case h_matching_connection_keep_alive: - parser->index++; - if (parser->index > sizeof(KEEP_ALIVE)-1 - || c != KEEP_ALIVE[parser->index]) { - h_state = h_matching_connection_token; - } else if (parser->index == sizeof(KEEP_ALIVE)-2) { - h_state = h_connection_keep_alive; - } - break; - - /* looking for 'Connection: close' */ - case h_matching_connection_close: - parser->index++; - if (parser->index > sizeof(CLOSE)-1 || c != CLOSE[parser->index]) { - h_state = h_matching_connection_token; - } else if (parser->index == sizeof(CLOSE)-2) { - h_state = h_connection_close; - } - break; - - /* looking for 'Connection: upgrade' */ - case h_matching_connection_upgrade: - parser->index++; - if (parser->index > sizeof(UPGRADE) - 1 || - c != UPGRADE[parser->index]) { - h_state = h_matching_connection_token; - } else if (parser->index == sizeof(UPGRADE)-2) { - h_state = h_connection_upgrade; - } - break; - - case h_matching_connection_token: - if (ch == ',') { - h_state = h_matching_connection_token_start; - parser->index = 0; - } - break; + UPDATE_STATE(s_header_value_lws); + break; + } - case h_transfer_encoding_chunked: - if (ch != ' ') h_state = h_general; - break; + case s_header_value_lws: { + if (ch == ' ' || ch == '\t') { + UPDATE_STATE(s_header_value_start); + REEXECUTE(); + } + /* finished the header */ + switch (parser->header_state) { case h_connection_keep_alive: + parser->flags |= F_CONNECTION_KEEP_ALIVE; + break; case h_connection_close: + parser->flags |= F_CONNECTION_CLOSE; + break; + case h_transfer_encoding_chunked: + parser->flags |= F_CHUNKED; + break; case h_connection_upgrade: - if (ch == ',') { - if (h_state == h_connection_keep_alive) { - parser->flags |= F_CONNECTION_KEEP_ALIVE; - } else if (h_state == h_connection_close) { - parser->flags |= F_CONNECTION_CLOSE; - } else if (h_state == h_connection_upgrade) { - parser->flags |= F_CONNECTION_UPGRADE; - } - h_state = h_matching_connection_token_start; - parser->index = 0; - } else if (ch != ' ') { - h_state = h_matching_connection_token; - } - break; - + parser->flags |= F_CONNECTION_UPGRADE; + break; default: - UPDATE_STATE(s_header_value); - h_state = h_general; - break; - } - } - parser->header_state = h_state; - - COUNT_HEADER_SIZE(p - start); - - if (p == data + len) - --p; - break; - } - - case s_header_almost_done: - { - if (UNLIKELY(ch != LF)) { - SET_ERRNO(HPE_LF_EXPECTED); - goto error; - } - - UPDATE_STATE(s_header_value_lws); - break; - } + break; + } - case s_header_value_lws: - { - if (ch == ' ' || ch == '\t') { - UPDATE_STATE(s_header_value_start); - REEXECUTE(); + UPDATE_STATE(s_header_field_start); + REEXECUTE(); } - /* finished the header */ - switch (parser->header_state) { - case h_connection_keep_alive: - parser->flags |= F_CONNECTION_KEEP_ALIVE; - break; - case h_connection_close: - parser->flags |= F_CONNECTION_CLOSE; - break; - case h_transfer_encoding_chunked: - parser->flags |= F_CHUNKED; - break; - case h_connection_upgrade: - parser->flags |= F_CONNECTION_UPGRADE; - break; - default: + case s_header_value_discard_ws_almost_done: { + STRICT_CHECK(ch != LF); + UPDATE_STATE(s_header_value_discard_lws); break; } - UPDATE_STATE(s_header_field_start); - REEXECUTE(); - } - - case s_header_value_discard_ws_almost_done: - { - STRICT_CHECK(ch != LF); - UPDATE_STATE(s_header_value_discard_lws); - break; - } - - case s_header_value_discard_lws: - { - if (ch == ' ' || ch == '\t') { - UPDATE_STATE(s_header_value_discard_ws); - break; - } else { - switch (parser->header_state) { - case h_connection_keep_alive: - parser->flags |= F_CONNECTION_KEEP_ALIVE; - break; - case h_connection_close: - parser->flags |= F_CONNECTION_CLOSE; - break; - case h_connection_upgrade: - parser->flags |= F_CONNECTION_UPGRADE; - break; - case h_transfer_encoding_chunked: - parser->flags |= F_CHUNKED; - break; - default: - break; - } + case s_header_value_discard_lws: { + if (ch == ' ' || ch == '\t') { + UPDATE_STATE(s_header_value_discard_ws); + break; + } else { + switch (parser->header_state) { + case h_connection_keep_alive: + parser->flags |= F_CONNECTION_KEEP_ALIVE; + break; + case h_connection_close: + parser->flags |= F_CONNECTION_CLOSE; + break; + case h_connection_upgrade: + parser->flags |= F_CONNECTION_UPGRADE; + break; + case h_transfer_encoding_chunked: + parser->flags |= F_CHUNKED; + break; + default: + break; + } - /* header value was empty */ - MARK(header_value); - UPDATE_STATE(s_header_field_start); - CALLBACK_DATA_NOADVANCE(header_value); - REEXECUTE(); + /* header value was empty */ + MARK(header_value); + UPDATE_STATE(s_header_field_start); + CALLBACK_DATA_NOADVANCE(header_value); + REEXECUTE(); + } } - } - case s_headers_almost_done: - { - STRICT_CHECK(ch != LF); + case s_headers_almost_done: { + STRICT_CHECK(ch != LF); - if (parser->flags & F_TRAILING) { - /* End of a chunked request */ - UPDATE_STATE(s_message_done); - CALLBACK_NOTIFY_NOADVANCE(chunk_complete); - REEXECUTE(); - } + if (parser->flags & F_TRAILING) { + /* End of a chunked request */ + UPDATE_STATE(s_message_done); + CALLBACK_NOTIFY_NOADVANCE(chunk_complete); + REEXECUTE(); + } - /* Cannot use chunked encoding and a content-length header together + /* Cannot use chunked encoding and a content-length header together per the HTTP specification. */ - if ((parser->flags & F_CHUNKED) && - (parser->flags & F_CONTENTLENGTH)) { - SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH); - goto error; - } + if ((parser->flags & F_CHUNKED) && (parser->flags & F_CONTENTLENGTH)) { + SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH); + goto error; + } - UPDATE_STATE(s_headers_done); + UPDATE_STATE(s_headers_done); - /* Set this here so that on_headers_complete() callbacks can see it */ - parser->upgrade = - ((parser->flags & (F_UPGRADE | F_CONNECTION_UPGRADE)) == - (F_UPGRADE | F_CONNECTION_UPGRADE) || - parser->method == HTTP_CONNECT); + /* Set this here so that on_headers_complete() callbacks can see it */ + parser->upgrade = ((parser->flags & (F_UPGRADE | F_CONNECTION_UPGRADE)) == (F_UPGRADE | F_CONNECTION_UPGRADE) || parser->method == HTTP_CONNECT); - /* Here we call the headers_complete callback. This is somewhat + /* Here we call the headers_complete callback. This is somewhat * different than other callbacks because if the user returns 1, we * will interpret that as saying that this message has no body. This * is needed for the annoying case of recieving a response to a HEAD @@ -1807,98 +1791,94 @@ size_t http_parser_execute (http_parser *parser, * We'd like to use CALLBACK_NOTIFY_NOADVANCE() here but we cannot, so * we have to simulate it by handling a change in errno below. */ - if (settings->on_headers_complete) { - switch (settings->on_headers_complete(parser)) { - case 0: - break; + if (settings->on_headers_complete) { + switch (settings->on_headers_complete(parser)) { + case 0: + break; - case 2: - parser->upgrade = 1; + case 2: + parser->upgrade = 1; - case 1: - parser->flags |= F_SKIPBODY; - break; + case 1: + parser->flags |= F_SKIPBODY; + break; - default: - SET_ERRNO(HPE_CB_headers_complete); - RETURN(p - data); /* Error */ - } - } + default: + SET_ERRNO(HPE_CB_headers_complete); + RETURN(p - data); /* Error */ + } + } - if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { - RETURN(p - data); - } + if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { + RETURN(p - data); + } - REEXECUTE(); - } + REEXECUTE(); + } - case s_headers_done: - { - int hasBody; - STRICT_CHECK(ch != LF); + case s_headers_done: { + int hasBody; + STRICT_CHECK(ch != LF); - parser->nread = 0; + parser->nread = 0; - hasBody = parser->flags & F_CHUNKED || - (parser->content_length > 0 && parser->content_length != ULLONG_MAX); - if (parser->upgrade && (parser->method == HTTP_CONNECT || - (parser->flags & F_SKIPBODY) || !hasBody)) { - /* Exit, the rest of the message is in a different protocol. */ - UPDATE_STATE(NEW_MESSAGE()); - CALLBACK_NOTIFY(message_complete); - RETURN((p - data) + 1); - } + hasBody = parser->flags & F_CHUNKED || (parser->content_length > 0 && parser->content_length != ULLONG_MAX); + if (parser->upgrade && (parser->method == HTTP_CONNECT || (parser->flags & F_SKIPBODY) || !hasBody)) { + /* Exit, the rest of the message is in a different protocol. */ + UPDATE_STATE(NEW_MESSAGE()); + CALLBACK_NOTIFY(message_complete); + RETURN((p - data) + 1); + } - if (parser->flags & F_SKIPBODY) { - UPDATE_STATE(NEW_MESSAGE()); - CALLBACK_NOTIFY(message_complete); - } else if (parser->flags & F_CHUNKED) { - /* chunked encoding - ignore Content-Length header */ - UPDATE_STATE(s_chunk_size_start); - } else { - if (parser->content_length == 0) { - /* Content-Length header given but zero: Content-Length: 0\r\n */ - UPDATE_STATE(NEW_MESSAGE()); - CALLBACK_NOTIFY(message_complete); - } else if (parser->content_length != ULLONG_MAX) { - /* Content-Length header given and non-zero */ - UPDATE_STATE(s_body_identity); - } else { - if (!http_message_needs_eof(parser)) { - /* Assume content-length 0 - read the next */ - UPDATE_STATE(NEW_MESSAGE()); - CALLBACK_NOTIFY(message_complete); + if (parser->flags & F_SKIPBODY) { + UPDATE_STATE(NEW_MESSAGE()); + CALLBACK_NOTIFY(message_complete); + } else if (parser->flags & F_CHUNKED) { + /* chunked encoding - ignore Content-Length header */ + UPDATE_STATE(s_chunk_size_start); } else { - /* Read body until EOF */ - UPDATE_STATE(s_body_identity_eof); + if (parser->content_length == 0) { + /* Content-Length header given but zero: Content-Length: 0\r\n */ + UPDATE_STATE(NEW_MESSAGE()); + CALLBACK_NOTIFY(message_complete); + } else if (parser->content_length != ULLONG_MAX) { + /* Content-Length header given and non-zero */ + UPDATE_STATE(s_body_identity); + } else { + if (!http_message_needs_eof(parser)) { + /* Assume content-length 0 - read the next */ + UPDATE_STATE(NEW_MESSAGE()); + CALLBACK_NOTIFY(message_complete); + } else { + /* Read body until EOF */ + UPDATE_STATE(s_body_identity_eof); + } + } } - } - } - break; - } + break; + } - case s_body_identity: - { - uint64_t to_read = MIN(parser->content_length, - (uint64_t) ((data + len) - p)); + case s_body_identity: { + uint64_t to_read = MIN(parser->content_length, + (uint64_t)((data + len) - p)); - assert(parser->content_length != 0 - && parser->content_length != ULLONG_MAX); + assert(parser->content_length != 0 + && parser->content_length != ULLONG_MAX); - /* The difference between advancing content_length and p is because + /* The difference between advancing content_length and p is because * the latter will automaticaly advance on the next loop iteration. * Further, if content_length ends up at 0, we want to see the last * byte again for our message complete callback. */ - MARK(body); - parser->content_length -= to_read; - p += to_read - 1; + MARK(body); + parser->content_length -= to_read; + p += to_read - 1; - if (parser->content_length == 0) { - UPDATE_STATE(s_message_done); + if (parser->content_length == 0) { + UPDATE_STATE(s_message_done); - /* Mimic CALLBACK_DATA_NOADVANCE() but with one extra byte. + /* Mimic CALLBACK_DATA_NOADVANCE() but with one extra byte. * * The alternative to doing this is to wait for the next byte to * trigger the data callback, just as in every other case. The @@ -1907,157 +1887,152 @@ size_t http_parser_execute (http_parser *parser, * complete-on-length. It's not clear that this distinction is * important for applications, but let's keep it for now. */ - CALLBACK_DATA_(body, p - body_mark + 1, p - data); - REEXECUTE(); + CALLBACK_DATA_(body, p - body_mark + 1, p - data); + REEXECUTE(); + } + + break; } - break; - } + /* read until EOF */ + case s_body_identity_eof: + MARK(body); + p = data + len - 1; - /* read until EOF */ - case s_body_identity_eof: - MARK(body); - p = data + len - 1; + break; - break; + case s_message_done: + UPDATE_STATE(NEW_MESSAGE()); + CALLBACK_NOTIFY(message_complete); + if (parser->upgrade) { + /* Exit, the rest of the message is in a different protocol. */ + RETURN((p - data) + 1); + } + break; - case s_message_done: - UPDATE_STATE(NEW_MESSAGE()); - CALLBACK_NOTIFY(message_complete); - if (parser->upgrade) { - /* Exit, the rest of the message is in a different protocol. */ - RETURN((p - data) + 1); - } - break; + case s_chunk_size_start: { + assert(parser->nread == 1); + assert(parser->flags & F_CHUNKED); - case s_chunk_size_start: - { - assert(parser->nread == 1); - assert(parser->flags & F_CHUNKED); + unhex_val = unhex[(unsigned char)ch]; + if (UNLIKELY(unhex_val == -1)) { + SET_ERRNO(HPE_INVALID_CHUNK_SIZE); + goto error; + } - unhex_val = unhex[(unsigned char)ch]; - if (UNLIKELY(unhex_val == -1)) { - SET_ERRNO(HPE_INVALID_CHUNK_SIZE); - goto error; + parser->content_length = unhex_val; + UPDATE_STATE(s_chunk_size); + break; } - parser->content_length = unhex_val; - UPDATE_STATE(s_chunk_size); - break; - } + case s_chunk_size: { + uint64_t t; - case s_chunk_size: - { - uint64_t t; + assert(parser->flags & F_CHUNKED); - assert(parser->flags & F_CHUNKED); + if (ch == CR) { + UPDATE_STATE(s_chunk_size_almost_done); + break; + } - if (ch == CR) { - UPDATE_STATE(s_chunk_size_almost_done); - break; - } + unhex_val = unhex[(unsigned char)ch]; - unhex_val = unhex[(unsigned char)ch]; + if (unhex_val == -1) { + if (ch == ';' || ch == ' ') { + UPDATE_STATE(s_chunk_parameters); + break; + } - if (unhex_val == -1) { - if (ch == ';' || ch == ' ') { - UPDATE_STATE(s_chunk_parameters); - break; - } + SET_ERRNO(HPE_INVALID_CHUNK_SIZE); + goto error; + } - SET_ERRNO(HPE_INVALID_CHUNK_SIZE); - goto error; - } + t = parser->content_length; + t *= 16; + t += unhex_val; - t = parser->content_length; - t *= 16; - t += unhex_val; + /* Overflow? Test against a conservative limit for simplicity. */ + if (UNLIKELY((ULLONG_MAX - 16) / 16 < parser->content_length)) { + SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); + goto error; + } - /* Overflow? Test against a conservative limit for simplicity. */ - if (UNLIKELY((ULLONG_MAX - 16) / 16 < parser->content_length)) { - SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); - goto error; + parser->content_length = t; + break; } - parser->content_length = t; - break; - } - - case s_chunk_parameters: - { - assert(parser->flags & F_CHUNKED); - /* just ignore this shit. TODO check for overflow */ - if (ch == CR) { - UPDATE_STATE(s_chunk_size_almost_done); - break; + case s_chunk_parameters: { + assert(parser->flags & F_CHUNKED); + /* just ignore this shit. TODO check for overflow */ + if (ch == CR) { + UPDATE_STATE(s_chunk_size_almost_done); + break; + } + break; } - break; - } - case s_chunk_size_almost_done: - { - assert(parser->flags & F_CHUNKED); - STRICT_CHECK(ch != LF); + case s_chunk_size_almost_done: { + assert(parser->flags & F_CHUNKED); + STRICT_CHECK(ch != LF); - parser->nread = 0; + parser->nread = 0; - if (parser->content_length == 0) { - parser->flags |= F_TRAILING; - UPDATE_STATE(s_header_field_start); - } else { - UPDATE_STATE(s_chunk_data); + if (parser->content_length == 0) { + parser->flags |= F_TRAILING; + UPDATE_STATE(s_header_field_start); + } else { + UPDATE_STATE(s_chunk_data); + } + CALLBACK_NOTIFY(chunk_header); + break; } - CALLBACK_NOTIFY(chunk_header); - break; - } - case s_chunk_data: - { - uint64_t to_read = MIN(parser->content_length, - (uint64_t) ((data + len) - p)); + case s_chunk_data: { + uint64_t to_read = MIN(parser->content_length, + (uint64_t)((data + len) - p)); - assert(parser->flags & F_CHUNKED); - assert(parser->content_length != 0 - && parser->content_length != ULLONG_MAX); + assert(parser->flags & F_CHUNKED); + assert(parser->content_length != 0 + && parser->content_length != ULLONG_MAX); - /* See the explanation in s_body_identity for why the content + /* See the explanation in s_body_identity for why the content * length and data pointers are managed this way. */ - MARK(body); - parser->content_length -= to_read; - p += to_read - 1; + MARK(body); + parser->content_length -= to_read; + p += to_read - 1; - if (parser->content_length == 0) { - UPDATE_STATE(s_chunk_data_almost_done); + if (parser->content_length == 0) { + UPDATE_STATE(s_chunk_data_almost_done); + } + + break; } - break; - } - - case s_chunk_data_almost_done: - assert(parser->flags & F_CHUNKED); - assert(parser->content_length == 0); - STRICT_CHECK(ch != CR); - UPDATE_STATE(s_chunk_data_done); - CALLBACK_DATA(body); - break; + case s_chunk_data_almost_done: + assert(parser->flags & F_CHUNKED); + assert(parser->content_length == 0); + STRICT_CHECK(ch != CR); + UPDATE_STATE(s_chunk_data_done); + CALLBACK_DATA(body); + break; - case s_chunk_data_done: - assert(parser->flags & F_CHUNKED); - STRICT_CHECK(ch != LF); - parser->nread = 0; - UPDATE_STATE(s_chunk_size_start); - CALLBACK_NOTIFY(chunk_complete); - break; + case s_chunk_data_done: + assert(parser->flags & F_CHUNKED); + STRICT_CHECK(ch != LF); + parser->nread = 0; + UPDATE_STATE(s_chunk_size_start); + CALLBACK_NOTIFY(chunk_complete); + break; - default: - assert(0 && "unhandled state"); - SET_ERRNO(HPE_INVALID_INTERNAL_STATE); - goto error; + default: + assert(0 && "unhandled state"); + SET_ERRNO(HPE_INVALID_INTERNAL_STATE); + goto error; + } } - } - /* Run callbacks for any marks that we have leftover after we ran our of + /* Run callbacks for any marks that we have leftover after we ran our of * bytes. There should be at most one of these set, so it's OK to invoke * them in series (unset marks will not result in callbacks). * @@ -2067,255 +2042,246 @@ size_t http_parser_execute (http_parser *parser, * value that's in-bounds). */ - assert(((header_field_mark ? 1 : 0) + - (header_value_mark ? 1 : 0) + - (url_mark ? 1 : 0) + - (body_mark ? 1 : 0) + - (status_mark ? 1 : 0)) <= 1); + assert(((header_field_mark ? 1 : 0) + (header_value_mark ? 1 : 0) + (url_mark ? 1 : 0) + (body_mark ? 1 : 0) + (status_mark ? 1 : 0)) <= 1); - CALLBACK_DATA_NOADVANCE(header_field); - CALLBACK_DATA_NOADVANCE(header_value); - CALLBACK_DATA_NOADVANCE(url); - CALLBACK_DATA_NOADVANCE(body); - CALLBACK_DATA_NOADVANCE(status); + CALLBACK_DATA_NOADVANCE(header_field); + CALLBACK_DATA_NOADVANCE(header_value); + CALLBACK_DATA_NOADVANCE(url); + CALLBACK_DATA_NOADVANCE(body); + CALLBACK_DATA_NOADVANCE(status); - RETURN(len); + RETURN(len); error: - if (HTTP_PARSER_ERRNO(parser) == HPE_OK) { - SET_ERRNO(HPE_UNKNOWN); - } + if (HTTP_PARSER_ERRNO(parser) == HPE_OK) { + SET_ERRNO(HPE_UNKNOWN); + } - RETURN(p - data); + RETURN(p - data); } - /* Does the parser need to see an EOF to find the end of the message? */ -int -http_message_needs_eof (const http_parser *parser) +int http_message_needs_eof(const http_parser* parser) { - if (parser->type == HTTP_REQUEST) { - return 0; - } + if (parser->type == HTTP_REQUEST) { + return 0; + } - /* See RFC 2616 section 4.4 */ - if (parser->status_code / 100 == 1 || /* 1xx e.g. Continue */ - parser->status_code == 204 || /* No Content */ - parser->status_code == 304 || /* Not Modified */ - parser->flags & F_SKIPBODY) { /* response to a HEAD request */ - return 0; - } + /* See RFC 2616 section 4.4 */ + if (parser->status_code / 100 == 1 || /* 1xx e.g. Continue */ + parser->status_code == 204 || /* No Content */ + parser->status_code == 304 || /* Not Modified */ + parser->flags & F_SKIPBODY) { /* response to a HEAD request */ + return 0; + } - if ((parser->flags & F_CHUNKED) || parser->content_length != ULLONG_MAX) { - return 0; - } + if ((parser->flags & F_CHUNKED) || parser->content_length != ULLONG_MAX) { + return 0; + } - return 1; + return 1; } - -int -http_should_keep_alive (const http_parser *parser) +int http_should_keep_alive(const http_parser* parser) { - if (parser->http_major > 0 && parser->http_minor > 0) { - /* HTTP/1.1 */ - if (parser->flags & F_CONNECTION_CLOSE) { - return 0; - } - } else { - /* HTTP/1.0 or earlier */ - if (!(parser->flags & F_CONNECTION_KEEP_ALIVE)) { - return 0; + if (parser->http_major > 0 && parser->http_minor > 0) { + /* HTTP/1.1 */ + if (parser->flags & F_CONNECTION_CLOSE) { + return 0; + } + } else { + /* HTTP/1.0 or earlier */ + if (!(parser->flags & F_CONNECTION_KEEP_ALIVE)) { + return 0; + } } - } - return !http_message_needs_eof(parser); + return !http_message_needs_eof(parser); } - -const char * -http_method_str (enum http_method m) +const char* +http_method_str(enum http_method m) { - return ELEM_AT(method_strings, m, ""); + return ELEM_AT(method_strings, m, ""); } - -void -http_parser_init (http_parser *parser, enum http_parser_type t) +void http_parser_init(http_parser* parser, enum http_parser_type t) { - void *data = parser->data; /* preserve application data */ - memset(parser, 0, sizeof(*parser)); - parser->data = data; - parser->type = t; - parser->state = (t == HTTP_REQUEST ? s_start_req : (t == HTTP_RESPONSE ? s_start_res : s_start_req_or_res)); - parser->http_errno = HPE_OK; + void* data = parser->data; /* preserve application data */ + memset(parser, 0, sizeof(*parser)); + parser->data = data; + parser->type = t; + parser->state = (t == HTTP_REQUEST ? s_start_req : (t == HTTP_RESPONSE ? s_start_res : s_start_req_or_res)); + parser->http_errno = HPE_OK; } -void -http_parser_settings_init(http_parser_settings *settings) +void http_parser_settings_init(http_parser_settings* settings) { - memset(settings, 0, sizeof(*settings)); + memset(settings, 0, sizeof(*settings)); } -const char * -http_errno_name(enum http_errno err) { - assert(((size_t) err) < ARRAY_SIZE(http_strerror_tab)); - return http_strerror_tab[err].name; +const char* +http_errno_name(enum http_errno err) +{ + assert(((size_t)err) < ARRAY_SIZE(http_strerror_tab)); + return http_strerror_tab[err].name; } -const char * -http_errno_description(enum http_errno err) { - assert(((size_t) err) < ARRAY_SIZE(http_strerror_tab)); - return http_strerror_tab[err].description; +const char* +http_errno_description(enum http_errno err) +{ + assert(((size_t)err) < ARRAY_SIZE(http_strerror_tab)); + return http_strerror_tab[err].description; } static enum http_host_state -http_parse_host_char(enum http_host_state s, const char ch) { - switch(s) { +http_parse_host_char(enum http_host_state s, const char ch) +{ + switch (s) { case s_http_userinfo: case s_http_userinfo_start: - if (ch == '@') { - return s_http_host_start; - } + if (ch == '@') { + return s_http_host_start; + } - if (IS_USERINFO_CHAR(ch)) { - return s_http_userinfo; - } - break; + if (IS_USERINFO_CHAR(ch)) { + return s_http_userinfo; + } + break; case s_http_host_start: - if (ch == '[') { - return s_http_host_v6_start; - } + if (ch == '[') { + return s_http_host_v6_start; + } - if (IS_HOST_CHAR(ch)) { - return s_http_host; - } + if (IS_HOST_CHAR(ch)) { + return s_http_host; + } - break; + break; case s_http_host: - if (IS_HOST_CHAR(ch)) { - return s_http_host; - } + if (IS_HOST_CHAR(ch)) { + return s_http_host; + } /* FALLTHROUGH */ case s_http_host_v6_end: - if (ch == ':') { - return s_http_host_port_start; - } + if (ch == ':') { + return s_http_host_port_start; + } - break; + break; case s_http_host_v6: - if (ch == ']') { - return s_http_host_v6_end; - } + if (ch == ']') { + return s_http_host_v6_end; + } /* FALLTHROUGH */ case s_http_host_v6_start: - if (IS_HEX(ch) || ch == ':' || ch == '.') { - return s_http_host_v6; - } + if (IS_HEX(ch) || ch == ':' || ch == '.') { + return s_http_host_v6; + } - if (s == s_http_host_v6 && ch == '%') { - return s_http_host_v6_zone_start; - } - break; + if (s == s_http_host_v6 && ch == '%') { + return s_http_host_v6_zone_start; + } + break; case s_http_host_v6_zone: - if (ch == ']') { - return s_http_host_v6_end; - } + if (ch == ']') { + return s_http_host_v6_end; + } /* FALLTHROUGH */ case s_http_host_v6_zone_start: - /* RFC 6874 Zone ID consists of 1*( unreserved / pct-encoded) */ - if (IS_ALPHANUM(ch) || ch == '%' || ch == '.' || ch == '-' || ch == '_' || - ch == '~') { - return s_http_host_v6_zone; - } - break; + /* RFC 6874 Zone ID consists of 1*( unreserved / pct-encoded) */ + if (IS_ALPHANUM(ch) || ch == '%' || ch == '.' || ch == '-' || ch == '_' || ch == '~') { + return s_http_host_v6_zone; + } + break; case s_http_host_port: case s_http_host_port_start: - if (IS_NUM(ch)) { - return s_http_host_port; - } + if (IS_NUM(ch)) { + return s_http_host_port; + } - break; + break; default: - break; - } - return s_http_host_dead; + break; + } + return s_http_host_dead; } static int -http_parse_host(const char * buf, struct http_parser_url *u, int found_at) { - assert(u->field_set & (1 << UF_HOST)); - enum http_host_state s; - - const char *p; - size_t buflen = u->field_data[UF_HOST].off + u->field_data[UF_HOST].len; +http_parse_host(const char* buf, struct http_parser_url* u, int found_at) +{ + assert(u->field_set & (1 << UF_HOST)); + enum http_host_state s; - u->field_data[UF_HOST].len = 0; + const char* p; + size_t buflen = u->field_data[UF_HOST].off + u->field_data[UF_HOST].len; - s = found_at ? s_http_userinfo_start : s_http_host_start; + u->field_data[UF_HOST].len = 0; - for (p = buf + u->field_data[UF_HOST].off; p < buf + buflen; p++) { - enum http_host_state new_s = http_parse_host_char(s, *p); + s = found_at ? s_http_userinfo_start : s_http_host_start; - if (new_s == s_http_host_dead) { - return 1; - } + for (p = buf + u->field_data[UF_HOST].off; p < buf + buflen; p++) { + enum http_host_state new_s = http_parse_host_char(s, *p); - switch(new_s) { - case s_http_host: - if (s != s_http_host) { - u->field_data[UF_HOST].off = p - buf; + if (new_s == s_http_host_dead) { + return 1; } - u->field_data[UF_HOST].len++; - break; - case s_http_host_v6: - if (s != s_http_host_v6) { - u->field_data[UF_HOST].off = p - buf; - } - u->field_data[UF_HOST].len++; - break; + switch (new_s) { + case s_http_host: + if (s != s_http_host) { + u->field_data[UF_HOST].off = p - buf; + } + u->field_data[UF_HOST].len++; + break; - case s_http_host_v6_zone_start: - case s_http_host_v6_zone: - u->field_data[UF_HOST].len++; - break; + case s_http_host_v6: + if (s != s_http_host_v6) { + u->field_data[UF_HOST].off = p - buf; + } + u->field_data[UF_HOST].len++; + break; - case s_http_host_port: - if (s != s_http_host_port) { - u->field_data[UF_PORT].off = p - buf; - u->field_data[UF_PORT].len = 0; - u->field_set |= (1 << UF_PORT); - } - u->field_data[UF_PORT].len++; - break; + case s_http_host_v6_zone_start: + case s_http_host_v6_zone: + u->field_data[UF_HOST].len++; + break; - case s_http_userinfo: - if (s != s_http_userinfo) { - u->field_data[UF_USERINFO].off = p - buf ; - u->field_data[UF_USERINFO].len = 0; - u->field_set |= (1 << UF_USERINFO); - } - u->field_data[UF_USERINFO].len++; - break; + case s_http_host_port: + if (s != s_http_host_port) { + u->field_data[UF_PORT].off = p - buf; + u->field_data[UF_PORT].len = 0; + u->field_set |= (1 << UF_PORT); + } + u->field_data[UF_PORT].len++; + break; - default: - break; + case s_http_userinfo: + if (s != s_http_userinfo) { + u->field_data[UF_USERINFO].off = p - buf; + u->field_data[UF_USERINFO].len = 0; + u->field_set |= (1 << UF_USERINFO); + } + u->field_data[UF_USERINFO].len++; + break; + + default: + break; + } + s = new_s; } - s = new_s; - } - /* Make sure we don't end somewhere unexpected */ - switch (s) { + /* Make sure we don't end somewhere unexpected */ + switch (s) { case s_http_host_start: case s_http_host_v6_start: case s_http_host_v6: @@ -2324,145 +2290,141 @@ http_parse_host(const char * buf, struct http_parser_url *u, int found_at) { case s_http_host_port_start: case s_http_userinfo: case s_http_userinfo_start: - return 1; + return 1; default: - break; - } + break; + } - return 0; + return 0; } -void -http_parser_url_init(struct http_parser_url *u) { - memset(u, 0, sizeof(*u)); +void http_parser_url_init(struct http_parser_url* u) +{ + memset(u, 0, sizeof(*u)); } -int -http_parser_parse_url(const char *buf, size_t buflen, int is_connect, - struct http_parser_url *u) +int http_parser_parse_url(const char* buf, size_t buflen, int is_connect, + struct http_parser_url* u) { - enum state s; - const char *p; - enum http_parser_url_fields uf, old_uf; - int found_at = 0; - - u->port = u->field_set = 0; - s = is_connect ? s_req_server_start : s_req_spaces_before_url; - old_uf = UF_MAX; + enum state s; + const char* p; + enum http_parser_url_fields uf, old_uf; + int found_at = 0; + + u->port = u->field_set = 0; + s = is_connect ? s_req_server_start : s_req_spaces_before_url; + old_uf = UF_MAX; + + for (p = buf; p < buf + buflen; p++) { + s = parse_url_char(s, *p); + + /* Figure out the next field that we're operating on */ + switch (s) { + case s_dead: + return 1; + + /* Skip delimeters */ + case s_req_schema_slash: + case s_req_schema_slash_slash: + case s_req_server_start: + case s_req_query_string_start: + case s_req_fragment_start: + continue; + + case s_req_schema: + uf = UF_SCHEMA; + break; - for (p = buf; p < buf + buflen; p++) { - s = parse_url_char(s, *p); + case s_req_server_with_at: + found_at = 1; - /* Figure out the next field that we're operating on */ - switch (s) { - case s_dead: - return 1; + /* FALLTROUGH */ + case s_req_server: + uf = UF_HOST; + break; - /* Skip delimeters */ - case s_req_schema_slash: - case s_req_schema_slash_slash: - case s_req_server_start: - case s_req_query_string_start: - case s_req_fragment_start: - continue; + case s_req_path: + uf = UF_PATH; + break; - case s_req_schema: - uf = UF_SCHEMA; - break; + case s_req_query_string: + uf = UF_QUERY; + break; - case s_req_server_with_at: - found_at = 1; + case s_req_fragment: + uf = UF_FRAGMENT; + break; - /* FALLTROUGH */ - case s_req_server: - uf = UF_HOST; - break; + default: + assert(!"Unexpected state"); + return 1; + } - case s_req_path: - uf = UF_PATH; - break; + /* Nothing's changed; soldier on */ + if (uf == old_uf) { + u->field_data[uf].len++; + continue; + } - case s_req_query_string: - uf = UF_QUERY; - break; + u->field_data[uf].off = p - buf; + u->field_data[uf].len = 1; - case s_req_fragment: - uf = UF_FRAGMENT; - break; + u->field_set |= (1 << uf); + old_uf = uf; + } - default: - assert(!"Unexpected state"); + /* host must be present if there is a schema */ + /* parsing http:///toto will fail */ + if ((u->field_set & (1 << UF_SCHEMA)) && (u->field_set & (1 << UF_HOST)) == 0) { return 1; } - /* Nothing's changed; soldier on */ - if (uf == old_uf) { - u->field_data[uf].len++; - continue; + if (u->field_set & (1 << UF_HOST)) { + if (http_parse_host(buf, u, found_at) != 0) { + return 1; + } } - u->field_data[uf].off = p - buf; - u->field_data[uf].len = 1; - - u->field_set |= (1 << uf); - old_uf = uf; - } - - /* host must be present if there is a schema */ - /* parsing http:///toto will fail */ - if ((u->field_set & (1 << UF_SCHEMA)) && - (u->field_set & (1 << UF_HOST)) == 0) { - return 1; - } - - if (u->field_set & (1 << UF_HOST)) { - if (http_parse_host(buf, u, found_at) != 0) { - return 1; + /* CONNECT requests can only contain "hostname:port" */ + if (is_connect && u->field_set != ((1 << UF_HOST) | (1 << UF_PORT))) { + return 1; } - } - /* CONNECT requests can only contain "hostname:port" */ - if (is_connect && u->field_set != ((1 << UF_HOST)|(1 << UF_PORT))) { - return 1; - } + if (u->field_set & (1 << UF_PORT)) { + /* Don't bother with endp; we've already validated the string */ + unsigned long v = strtoul(buf + u->field_data[UF_PORT].off, NULL, 10); - if (u->field_set & (1 << UF_PORT)) { - /* Don't bother with endp; we've already validated the string */ - unsigned long v = strtoul(buf + u->field_data[UF_PORT].off, NULL, 10); + /* Ports have a max value of 2^16 */ + if (v > 0xffff) { + return 1; + } - /* Ports have a max value of 2^16 */ - if (v > 0xffff) { - return 1; + u->port = (uint16_t)v; } - u->port = (uint16_t) v; - } - - return 0; + return 0; } -void -http_parser_pause(http_parser *parser, int paused) { - /* Users should only be pausing/unpausing a parser that is not in an error +void http_parser_pause(http_parser* parser, int paused) +{ + /* Users should only be pausing/unpausing a parser that is not in an error * state. In non-debug builds, there's not much that we can do about this * other than ignore it. */ - if (HTTP_PARSER_ERRNO(parser) == HPE_OK || - HTTP_PARSER_ERRNO(parser) == HPE_PAUSED) { - SET_ERRNO((paused) ? HPE_PAUSED : HPE_OK); - } else { - assert(0 && "Attempting to pause parser in error state"); - } + if (HTTP_PARSER_ERRNO(parser) == HPE_OK || HTTP_PARSER_ERRNO(parser) == HPE_PAUSED) { + SET_ERRNO((paused) ? HPE_PAUSED : HPE_OK); + } else { + assert(0 && "Attempting to pause parser in error state"); + } } -int -http_body_is_final(const struct http_parser *parser) { +int http_body_is_final(const struct http_parser* parser) +{ return parser->state == s_message_done; } unsigned long -http_parser_version(void) { - return HTTP_PARSER_VERSION_MAJOR * 0x10000 | - HTTP_PARSER_VERSION_MINOR * 0x00100 | - HTTP_PARSER_VERSION_PATCH * 0x00001; +http_parser_version(void) +{ + return HTTP_PARSER_VERSION_MAJOR * 0x10000 | HTTP_PARSER_VERSION_MINOR * 0x00100 | HTTP_PARSER_VERSION_PATCH * 0x00001; } diff --git a/node/src/http_parser.h b/node/src/http_parser.h index f6e11977ca..b00708145d 100644 --- a/node/src/http_parser.h +++ b/node/src/http_parser.h @@ -30,8 +30,7 @@ extern "C" { #define HTTP_PARSER_VERSION_PATCH 0 #include -#if defined(_WIN32) && !defined(__MINGW32__) && \ - (!defined(_MSC_VER) || _MSC_VER<1600) && !defined(__WINE__) +#if defined(_WIN32) && !defined(__MINGW32__) && (!defined(_MSC_VER) || _MSC_VER < 1600) && !defined(__WINE__) #include #include typedef __int8 int8_t; @@ -64,13 +63,12 @@ typedef unsigned __int64 uint64_t; * to a very large number (e.g. -DHTTP_MAX_HEADER_SIZE=0x7fffffff) */ #ifndef HTTP_MAX_HEADER_SIZE -# define HTTP_MAX_HEADER_SIZE (80*1024) +#define HTTP_MAX_HEADER_SIZE (80 * 1024) #endif typedef struct http_parser http_parser; typedef struct http_parser_settings http_parser_settings; - /* Callbacks should return non-zero to indicate an error. The parser will * then halt execution. * @@ -89,198 +87,187 @@ typedef struct http_parser_settings http_parser_settings; * many times for each string. E.G. you might get 10 callbacks for "on_url" * each providing just a few characters more data. */ -typedef int (*http_data_cb) (http_parser*, const char *at, size_t length); -typedef int (*http_cb) (http_parser*); - +typedef int (*http_data_cb)(http_parser*, const char* at, size_t length); +typedef int (*http_cb)(http_parser*); /* Request Methods */ -#define HTTP_METHOD_MAP(XX) \ - XX(0, DELETE, DELETE) \ - XX(1, GET, GET) \ - XX(2, HEAD, HEAD) \ - XX(3, POST, POST) \ - XX(4, PUT, PUT) \ - /* pathological */ \ - XX(5, CONNECT, CONNECT) \ - XX(6, OPTIONS, OPTIONS) \ - XX(7, TRACE, TRACE) \ - /* WebDAV */ \ - XX(8, COPY, COPY) \ - XX(9, LOCK, LOCK) \ - XX(10, MKCOL, MKCOL) \ - XX(11, MOVE, MOVE) \ - XX(12, PROPFIND, PROPFIND) \ - XX(13, PROPPATCH, PROPPATCH) \ - XX(14, SEARCH, SEARCH) \ - XX(15, UNLOCK, UNLOCK) \ - XX(16, BIND, BIND) \ - XX(17, REBIND, REBIND) \ - XX(18, UNBIND, UNBIND) \ - XX(19, ACL, ACL) \ - /* subversion */ \ - XX(20, REPORT, REPORT) \ - XX(21, MKACTIVITY, MKACTIVITY) \ - XX(22, CHECKOUT, CHECKOUT) \ - XX(23, MERGE, MERGE) \ - /* upnp */ \ - XX(24, MSEARCH, M-SEARCH) \ - XX(25, NOTIFY, NOTIFY) \ - XX(26, SUBSCRIBE, SUBSCRIBE) \ - XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \ - /* RFC-5789 */ \ - XX(28, PATCH, PATCH) \ - XX(29, PURGE, PURGE) \ - /* CalDAV */ \ - XX(30, MKCALENDAR, MKCALENDAR) \ - /* RFC-2068, section 19.6.1.2 */ \ - XX(31, LINK, LINK) \ - XX(32, UNLINK, UNLINK) \ - -enum http_method - { +#define HTTP_METHOD_MAP(XX) \ + XX(0, DELETE, DELETE) \ + XX(1, GET, GET) \ + XX(2, HEAD, HEAD) \ + XX(3, POST, POST) \ + XX(4, PUT, PUT) \ + /* pathological */ \ + XX(5, CONNECT, CONNECT) \ + XX(6, OPTIONS, OPTIONS) \ + XX(7, TRACE, TRACE) \ + /* WebDAV */ \ + XX(8, COPY, COPY) \ + XX(9, LOCK, LOCK) \ + XX(10, MKCOL, MKCOL) \ + XX(11, MOVE, MOVE) \ + XX(12, PROPFIND, PROPFIND) \ + XX(13, PROPPATCH, PROPPATCH) \ + XX(14, SEARCH, SEARCH) \ + XX(15, UNLOCK, UNLOCK) \ + XX(16, BIND, BIND) \ + XX(17, REBIND, REBIND) \ + XX(18, UNBIND, UNBIND) \ + XX(19, ACL, ACL) \ + /* subversion */ \ + XX(20, REPORT, REPORT) \ + XX(21, MKACTIVITY, MKACTIVITY) \ + XX(22, CHECKOUT, CHECKOUT) \ + XX(23, MERGE, MERGE) \ + /* upnp */ \ + XX(24, MSEARCH, M - SEARCH) \ + XX(25, NOTIFY, NOTIFY) \ + XX(26, SUBSCRIBE, SUBSCRIBE) \ + XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \ + /* RFC-5789 */ \ + XX(28, PATCH, PATCH) \ + XX(29, PURGE, PURGE) \ + /* CalDAV */ \ + XX(30, MKCALENDAR, MKCALENDAR) \ + /* RFC-2068, section 19.6.1.2 */ \ + XX(31, LINK, LINK) \ + XX(32, UNLINK, UNLINK) + +enum http_method { #define XX(num, name, string) HTTP_##name = num, - HTTP_METHOD_MAP(XX) + HTTP_METHOD_MAP(XX) #undef XX - }; - - -enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH }; +}; +enum http_parser_type { HTTP_REQUEST, + HTTP_RESPONSE, + HTTP_BOTH }; /* Flag values for http_parser.flags field */ -enum flags - { F_CHUNKED = 1 << 0 - , F_CONNECTION_KEEP_ALIVE = 1 << 1 - , F_CONNECTION_CLOSE = 1 << 2 - , F_CONNECTION_UPGRADE = 1 << 3 - , F_TRAILING = 1 << 4 - , F_UPGRADE = 1 << 5 - , F_SKIPBODY = 1 << 6 - , F_CONTENTLENGTH = 1 << 7 - }; - +enum flags { F_CHUNKED = 1 << 0, + F_CONNECTION_KEEP_ALIVE = 1 << 1, + F_CONNECTION_CLOSE = 1 << 2, + F_CONNECTION_UPGRADE = 1 << 3, + F_TRAILING = 1 << 4, + F_UPGRADE = 1 << 5, + F_SKIPBODY = 1 << 6, + F_CONTENTLENGTH = 1 << 7 +}; /* Map for errno-related constants * * The provided argument should be a macro that takes 2 arguments. */ -#define HTTP_ERRNO_MAP(XX) \ - /* No error */ \ - XX(OK, "success") \ - \ - /* Callback-related errors */ \ - XX(CB_message_begin, "the on_message_begin callback failed") \ - XX(CB_url, "the on_url callback failed") \ - XX(CB_header_field, "the on_header_field callback failed") \ - XX(CB_header_value, "the on_header_value callback failed") \ - XX(CB_headers_complete, "the on_headers_complete callback failed") \ - XX(CB_body, "the on_body callback failed") \ - XX(CB_message_complete, "the on_message_complete callback failed") \ - XX(CB_status, "the on_status callback failed") \ - XX(CB_chunk_header, "the on_chunk_header callback failed") \ - XX(CB_chunk_complete, "the on_chunk_complete callback failed") \ - \ - /* Parsing-related errors */ \ - XX(INVALID_EOF_STATE, "stream ended at an unexpected time") \ - XX(HEADER_OVERFLOW, \ - "too many header bytes seen; overflow detected") \ - XX(CLOSED_CONNECTION, \ - "data received after completed connection: close message") \ - XX(INVALID_VERSION, "invalid HTTP version") \ - XX(INVALID_STATUS, "invalid HTTP status code") \ - XX(INVALID_METHOD, "invalid HTTP method") \ - XX(INVALID_URL, "invalid URL") \ - XX(INVALID_HOST, "invalid host") \ - XX(INVALID_PORT, "invalid port") \ - XX(INVALID_PATH, "invalid path") \ - XX(INVALID_QUERY_STRING, "invalid query string") \ - XX(INVALID_FRAGMENT, "invalid fragment") \ - XX(LF_EXPECTED, "LF character expected") \ - XX(INVALID_HEADER_TOKEN, "invalid character in header") \ - XX(INVALID_CONTENT_LENGTH, \ - "invalid character in content-length header") \ - XX(UNEXPECTED_CONTENT_LENGTH, \ - "unexpected content-length header") \ - XX(INVALID_CHUNK_SIZE, \ - "invalid character in chunk size header") \ - XX(INVALID_CONSTANT, "invalid constant string") \ - XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\ - XX(STRICT, "strict mode assertion failed") \ - XX(PAUSED, "parser is paused") \ - XX(UNKNOWN, "an unknown error occurred") - +#define HTTP_ERRNO_MAP(XX) \ + /* No error */ \ + XX(OK, "success") \ + \ + /* Callback-related errors */ \ + XX(CB_message_begin, "the on_message_begin callback failed") \ + XX(CB_url, "the on_url callback failed") \ + XX(CB_header_field, "the on_header_field callback failed") \ + XX(CB_header_value, "the on_header_value callback failed") \ + XX(CB_headers_complete, "the on_headers_complete callback failed") \ + XX(CB_body, "the on_body callback failed") \ + XX(CB_message_complete, "the on_message_complete callback failed") \ + XX(CB_status, "the on_status callback failed") \ + XX(CB_chunk_header, "the on_chunk_header callback failed") \ + XX(CB_chunk_complete, "the on_chunk_complete callback failed") \ + \ + /* Parsing-related errors */ \ + XX(INVALID_EOF_STATE, "stream ended at an unexpected time") \ + XX(HEADER_OVERFLOW, \ + "too many header bytes seen; overflow detected") \ + XX(CLOSED_CONNECTION, \ + "data received after completed connection: close message") \ + XX(INVALID_VERSION, "invalid HTTP version") \ + XX(INVALID_STATUS, "invalid HTTP status code") \ + XX(INVALID_METHOD, "invalid HTTP method") \ + XX(INVALID_URL, "invalid URL") \ + XX(INVALID_HOST, "invalid host") \ + XX(INVALID_PORT, "invalid port") \ + XX(INVALID_PATH, "invalid path") \ + XX(INVALID_QUERY_STRING, "invalid query string") \ + XX(INVALID_FRAGMENT, "invalid fragment") \ + XX(LF_EXPECTED, "LF character expected") \ + XX(INVALID_HEADER_TOKEN, "invalid character in header") \ + XX(INVALID_CONTENT_LENGTH, \ + "invalid character in content-length header") \ + XX(UNEXPECTED_CONTENT_LENGTH, \ + "unexpected content-length header") \ + XX(INVALID_CHUNK_SIZE, \ + "invalid character in chunk size header") \ + XX(INVALID_CONSTANT, "invalid constant string") \ + XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state") \ + XX(STRICT, "strict mode NODE_ASSERTion failed") \ + XX(PAUSED, "parser is paused") \ + XX(UNKNOWN, "an unknown error occurred") /* Define HPE_* values for each errno value above */ #define HTTP_ERRNO_GEN(n, s) HPE_##n, enum http_errno { - HTTP_ERRNO_MAP(HTTP_ERRNO_GEN) + HTTP_ERRNO_MAP(HTTP_ERRNO_GEN) }; #undef HTTP_ERRNO_GEN - /* Get an http_errno value from an http_parser */ -#define HTTP_PARSER_ERRNO(p) ((enum http_errno) (p)->http_errno) - +#define HTTP_PARSER_ERRNO(p) ((enum http_errno)(p)->http_errno) struct http_parser { - /** PRIVATE **/ - unsigned int type : 2; /* enum http_parser_type */ - unsigned int flags : 8; /* F_* values from 'flags' enum; semi-public */ - unsigned int state : 7; /* enum state from http_parser.c */ - unsigned int header_state : 7; /* enum header_state from http_parser.c */ - unsigned int index : 7; /* index into current matcher */ - unsigned int lenient_http_headers : 1; - - uint32_t nread; /* # bytes read in various scenarios */ - uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */ - - /** READ-ONLY **/ - unsigned short http_major; - unsigned short http_minor; - unsigned int status_code : 16; /* responses only */ - unsigned int method : 8; /* requests only */ - unsigned int http_errno : 7; - - /* 1 = Upgrade header was present and the parser has exited because of that. + /** PRIVATE **/ + unsigned int type : 2; /* enum http_parser_type */ + unsigned int flags : 8; /* F_* values from 'flags' enum; semi-public */ + unsigned int state : 7; /* enum state from http_parser.c */ + unsigned int header_state : 7; /* enum header_state from http_parser.c */ + unsigned int index : 7; /* index into current matcher */ + unsigned int lenient_http_headers : 1; + + uint32_t nread; /* # bytes read in various scenarios */ + uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */ + + /** READ-ONLY **/ + unsigned short http_major; + unsigned short http_minor; + unsigned int status_code : 16; /* responses only */ + unsigned int method : 8; /* requests only */ + unsigned int http_errno : 7; + + /* 1 = Upgrade header was present and the parser has exited because of that. * 0 = No upgrade header present. * Should be checked when http_parser_execute() returns in addition to * error checking. */ - unsigned int upgrade : 1; + unsigned int upgrade : 1; - /** PUBLIC **/ - void *data; /* A pointer to get hook to the "connection" or "socket" object */ + /** PUBLIC **/ + void* data; /* A pointer to get hook to the "connection" or "socket" object */ }; - struct http_parser_settings { - http_cb on_message_begin; - http_data_cb on_url; - http_data_cb on_status; - http_data_cb on_header_field; - http_data_cb on_header_value; - http_cb on_headers_complete; - http_data_cb on_body; - http_cb on_message_complete; - /* When on_chunk_header is called, the current chunk length is stored + http_cb on_message_begin; + http_data_cb on_url; + http_data_cb on_status; + http_data_cb on_header_field; + http_data_cb on_header_value; + http_cb on_headers_complete; + http_data_cb on_body; + http_cb on_message_complete; + /* When on_chunk_header is called, the current chunk length is stored * in parser->content_length. */ - http_cb on_chunk_header; - http_cb on_chunk_complete; + http_cb on_chunk_header; + http_cb on_chunk_complete; }; - -enum http_parser_url_fields - { UF_SCHEMA = 0 - , UF_HOST = 1 - , UF_PORT = 2 - , UF_PATH = 3 - , UF_QUERY = 4 - , UF_FRAGMENT = 5 - , UF_USERINFO = 6 - , UF_MAX = 7 - }; - +enum http_parser_url_fields { UF_SCHEMA = 0, + UF_HOST = 1, + UF_PORT = 2, + UF_PATH = 3, + UF_QUERY = 4, + UF_FRAGMENT = 5, + UF_USERINFO = 6, + UF_MAX = 7 +}; /* Result structure for http_parser_parse_url(). * @@ -290,16 +277,15 @@ enum http_parser_url_fields * a uint16_t. */ struct http_parser_url { - uint16_t field_set; /* Bitmask of (1 << UF_*) values */ - uint16_t port; /* Converted UF_PORT string */ + uint16_t field_set; /* Bitmask of (1 << UF_*) values */ + uint16_t port; /* Converted UF_PORT string */ - struct { - uint16_t off; /* Offset into buffer in which field starts */ - uint16_t len; /* Length of run in buffer */ - } field_data[UF_MAX]; + struct { + uint16_t off; /* Offset into buffer in which field starts */ + uint16_t len; /* Length of run in buffer */ + } field_data[UF_MAX]; }; - /* Returns the library version. Bits 16-23 contain the major version number, * bits 8-15 the minor version number and bits 0-7 the patch level. * Usage example: @@ -312,21 +298,18 @@ struct http_parser_url { */ unsigned long http_parser_version(void); -void http_parser_init(http_parser *parser, enum http_parser_type type); - +void http_parser_init(http_parser* parser, enum http_parser_type type); /* Initialize http_parser_settings members to 0 */ -void http_parser_settings_init(http_parser_settings *settings); - +void http_parser_settings_init(http_parser_settings* settings); /* Executes the parser. Returns number of parsed bytes. Sets * `parser->http_errno` on error. */ -size_t http_parser_execute(http_parser *parser, - const http_parser_settings *settings, - const char *data, - size_t len); - +size_t http_parser_execute(http_parser* parser, + const http_parser_settings* settings, + const char* data, + size_t len); /* If http_should_keep_alive() in the on_headers_complete or * on_message_complete callback returns 0, then this should be @@ -334,30 +317,30 @@ size_t http_parser_execute(http_parser *parser, * If you are the server, respond with the "Connection: close" header. * If you are the client, close the connection. */ -int http_should_keep_alive(const http_parser *parser); +int http_should_keep_alive(const http_parser* parser); /* Returns a string version of the HTTP method. */ -const char *http_method_str(enum http_method m); +const char* http_method_str(enum http_method m); /* Return a string name of the given error */ -const char *http_errno_name(enum http_errno err); +const char* http_errno_name(enum http_errno err); /* Return a string description of the given error */ -const char *http_errno_description(enum http_errno err); +const char* http_errno_description(enum http_errno err); /* Initialize all http_parser_url members to 0 */ -void http_parser_url_init(struct http_parser_url *u); +void http_parser_url_init(struct http_parser_url* u); /* Parse a URL; return nonzero on failure */ -int http_parser_parse_url(const char *buf, size_t buflen, - int is_connect, - struct http_parser_url *u); +int http_parser_parse_url(const char* buf, size_t buflen, + int is_connect, + struct http_parser_url* u); /* Pause or un-pause the parser; a nonzero value pauses */ -void http_parser_pause(http_parser *parser, int paused); +void http_parser_pause(http_parser* parser, int paused); /* Checks if this is the final chunk of the body. */ -int http_body_is_final(const http_parser *parser); +int http_body_is_final(const http_parser* parser); #ifdef __cplusplus } diff --git a/node/src/inspector_agent.cc b/node/src/inspector_agent.cc index 11ee568b5e..b6af5e20a7 100644 --- a/node/src/inspector_agent.cc +++ b/node/src/inspector_agent.cc @@ -26,855 +26,923 @@ #include #include - namespace node { namespace inspector { -namespace { - -const char TAG_CONNECT[] = "#connect"; -const char TAG_DISCONNECT[] = "#disconnect"; - -static const uint8_t PROTOCOL_JSON[] = { -#include "v8_inspector_protocol_json.h" // NOLINT(build/include_order) -}; - -std::string GetWsUrl(int port, const std::string& id) { - char buf[1024]; - snprintf(buf, sizeof(buf), "127.0.0.1:%d/%s", port, id.c_str()); - return buf; -} - -void PrintDebuggerReadyMessage(int port, const std::string& id) { - fprintf(stderr, "Debugger listening on port %d.\n" - "Warning: This is an experimental feature and could change at any time.\n" - "To start debugging, open the following URL in Chrome:\n" - " chrome-devtools://devtools/remote/serve_file/" - "@" V8_INSPECTOR_REVISION "/inspector.html?" - "experiments=true&v8only=true&ws=%s\n", - port, GetWsUrl(port, id).c_str()); - fflush(stderr); -} - -std::string MapToString(const std::map object) { - std::ostringstream json; - json << "[ {\n"; - bool first = true; - for (const auto& name_value : object) { - if (!first) - json << ",\n"; - json << " \"" << name_value.first << "\": \""; - json << name_value.second << "\""; - first = false; - } - json << "\n} ]"; - return json.str(); -} - -void Escape(std::string* string) { - for (char& c : *string) { - c = (c == '\"' || c == '\\') ? '_' : c; - } -} - -void DisposeInspector(InspectorSocket* socket, int status) { - delete socket; -} - -void DisconnectAndDisposeIO(InspectorSocket* socket) { - if (socket) { - inspector_close(socket, DisposeInspector); - } -} - -void OnBufferAlloc(uv_handle_t* handle, size_t len, uv_buf_t* buf) { - buf->base = new char[len]; - buf->len = len; -} - -void SendHttpResponse(InspectorSocket* socket, const std::string& response) { - const char HEADERS[] = "HTTP/1.0 200 OK\r\n" - "Content-Type: application/json; charset=UTF-8\r\n" - "Cache-Control: no-cache\r\n" - "Content-Length: %zu\r\n" - "\r\n"; - char header[sizeof(HEADERS) + 20]; - int header_len = snprintf(header, sizeof(header), HEADERS, response.size()); - inspector_write(socket, header, header_len); - inspector_write(socket, response.data(), response.size()); -} - -void SendVersionResponse(InspectorSocket* socket) { - std::map response; - response["Browser"] = "node.js/" NODE_VERSION; - response["Protocol-Version"] = "1.1"; - SendHttpResponse(socket, MapToString(response)); -} - -std::string GetProcessTitle() { - // uv_get_process_title will trim the title if it is too long. - char title[2048]; - int err = uv_get_process_title(title, sizeof(title)); - if (err == 0) { - return title; - } else { - return "Node.js"; - } -} - -void SendProtocolJson(InspectorSocket* socket) { - z_stream strm; - strm.zalloc = Z_NULL; - strm.zfree = Z_NULL; - strm.opaque = Z_NULL; - CHECK_EQ(Z_OK, inflateInit(&strm)); - static const size_t kDecompressedSize = - PROTOCOL_JSON[0] * 0x10000u + - PROTOCOL_JSON[1] * 0x100u + - PROTOCOL_JSON[2]; - strm.next_in = const_cast(PROTOCOL_JSON + 3); - strm.avail_in = sizeof(PROTOCOL_JSON) - 3; - std::string data(kDecompressedSize, '\0'); - strm.next_out = reinterpret_cast(&data[0]); - strm.avail_out = data.size(); - CHECK_EQ(Z_STREAM_END, inflate(&strm, Z_FINISH)); - CHECK_EQ(0, strm.avail_out); - CHECK_EQ(Z_OK, inflateEnd(&strm)); - SendHttpResponse(socket, data); -} - -const char* match_path_segment(const char* path, const char* expected) { - size_t len = strlen(expected); - if (StringEqualNoCaseN(path, expected, len)) { - if (path[len] == '/') return path + len + 1; - if (path[len] == '\0') return path + len; - } - return nullptr; -} - -// UUID RFC: https://www.ietf.org/rfc/rfc4122.txt -// Used ver 4 - with numbers -std::string GenerateID() { - uint16_t buffer[8]; - CHECK(crypto::EntropySource(reinterpret_cast(buffer), - sizeof(buffer))); - - char uuid[256]; - snprintf(uuid, sizeof(uuid), "%04x%04x-%04x-%04x-%04x-%04x%04x%04x", - buffer[0], // time_low - buffer[1], // time_mid - buffer[2], // time_low - (buffer[3] & 0x0fff) | 0x4000, // time_hi_and_version - (buffer[4] & 0x3fff) | 0x8000, // clk_seq_hi clk_seq_low - buffer[5], // node - buffer[6], - buffer[7]); - return uuid; -} -} // namespace - - -class V8NodeInspector; - -class AgentImpl { - public: - explicit AgentImpl(node::Environment* env); - ~AgentImpl(); - - // Start the inspector agent thread - bool Start(v8::Platform* platform, const char* path, int port, bool wait); - // Stop the inspector agent - void Stop(); - - bool IsStarted(); - bool IsConnected() { return state_ == State::kConnected; } - void WaitForDisconnect(); - - void FatalException(v8::Local error, - v8::Local message); - - private: - using MessageQueue = std::vector>; - enum class State { kNew, kAccepting, kConnected, kDone, kError }; - - static void ThreadCbIO(void* agent); - static void OnSocketConnectionIO(uv_stream_t* server, int status); - static bool OnInspectorHandshakeIO(InspectorSocket* socket, - enum inspector_handshake_event state, - const std::string& path); - static void WriteCbIO(uv_async_t* async); - - void InstallInspectorOnProcess(); - - void WorkerRunIO(); - void OnInspectorConnectionIO(InspectorSocket* socket); - void OnRemoteDataIO(InspectorSocket* stream, ssize_t read, - const uv_buf_t* b); - void SetConnected(bool connected); - void DispatchMessages(); - void Write(int session_id, const String16& message); - bool AppendMessage(MessageQueue* vector, int session_id, - const String16& message); - void SwapBehindLock(MessageQueue* vector1, MessageQueue* vector2); - void PostIncomingMessage(const String16& message); - void WaitForFrontendMessage(); - void NotifyMessageReceived(); - State ToState(State state); - void SendTargentsListResponse(InspectorSocket* socket); - bool RespondToGet(InspectorSocket* socket, const std::string& path); - - uv_sem_t start_sem_; - ConditionVariable incoming_message_cond_; - Mutex state_lock_; - uv_thread_t thread_; - uv_loop_t child_loop_; - - int port_; - bool wait_; - bool shutting_down_; - State state_; - node::Environment* parent_env_; - - uv_async_t* data_written_; - uv_async_t io_thread_req_; - InspectorSocket* client_socket_; - V8NodeInspector* inspector_; - v8::Platform* platform_; - MessageQueue incoming_message_queue_; - MessageQueue outgoing_message_queue_; - bool dispatching_messages_; - int frontend_session_id_; - int backend_session_id_; - - std::string script_name_; - std::string script_path_; - const std::string id_; - - friend class ChannelImpl; - friend class DispatchOnInspectorBackendTask; - friend class SetConnectedTask; - friend class V8NodeInspector; - friend void InterruptCallback(v8::Isolate*, void* agent); - friend void DataCallback(uv_stream_t* stream, ssize_t read, - const uv_buf_t* buf); -}; - -void InterruptCallback(v8::Isolate*, void* agent) { - static_cast(agent)->DispatchMessages(); -} - -void DataCallback(uv_stream_t* stream, ssize_t read, const uv_buf_t* buf) { - InspectorSocket* socket = inspector_from_stream(stream); - static_cast(socket->data)->OnRemoteDataIO(socket, read, buf); -} - -class DispatchOnInspectorBackendTask : public v8::Task { - public: - explicit DispatchOnInspectorBackendTask(AgentImpl* agent) : agent_(agent) {} - - void Run() override { - agent_->DispatchMessages(); - } - - private: - AgentImpl* agent_; -}; - -class ChannelImpl final : public blink::protocol::FrontendChannel { - public: - explicit ChannelImpl(AgentImpl* agent): agent_(agent) {} - virtual ~ChannelImpl() {} - private: - void sendProtocolResponse(int callId, const String16& message) override { - sendMessageToFrontend(message); - } - - void sendProtocolNotification(const String16& message) override { - sendMessageToFrontend(message); - } - - void flushProtocolNotifications() override { } - - void sendMessageToFrontend(const String16& message) { - agent_->Write(agent_->frontend_session_id_, message); - } - - AgentImpl* const agent_; -}; + namespace { + + const char TAG_CONNECT[] = "#connect"; + const char TAG_DISCONNECT[] = "#disconnect"; + + static const uint8_t PROTOCOL_JSON[] = { +#include "v8_inspector_protocol_json.h" // NOLINT(build/include_order) + }; + + std::string GetWsUrl(int port, const std::string& id) + { + char buf[1024]; + snprintf(buf, sizeof(buf), "127.0.0.1:%d/%s", port, id.c_str()); + return buf; + } + + void PrintDebuggerReadyMessage(int port, const std::string& id) + { + fprintf(stderr, "Debugger listening on port %d.\n" + "Warning: This is an experimental feature and could change at any time.\n" + "To start debugging, open the following URL in Chrome:\n" + " chrome-devtools://devtools/remote/serve_file/" + "@" V8_INSPECTOR_REVISION "/inspector.html?" + "experiments=true&v8only=true&ws=%s\n", + port, GetWsUrl(port, id).c_str()); + fflush(stderr); + } + + std::string MapToString(const std::map object) + { + std::ostringstream json; + json << "[ {\n"; + bool first = true; + for (const auto& name_value : object) { + if (!first) + json << ",\n"; + json << " \"" << name_value.first << "\": \""; + json << name_value.second << "\""; + first = false; + } + json << "\n} ]"; + return json.str(); + } + + void Escape(std::string* string) + { + for (char& c : *string) { + c = (c == '\"' || c == '\\') ? '_' : c; + } + } + + void DisposeInspector(InspectorSocket* socket, int status) + { + delete socket; + } + + void DisconnectAndDisposeIO(InspectorSocket* socket) + { + if (socket) { + inspector_close(socket, DisposeInspector); + } + } + + void OnBufferAlloc(uv_handle_t* handle, size_t len, uv_buf_t* buf) + { + buf->base = new char[len]; + buf->len = len; + } + + void SendHttpResponse(InspectorSocket* socket, const std::string& response) + { + const char HEADERS[] = "HTTP/1.0 200 OK\r\n" + "Content-Type: application/json; charset=UTF-8\r\n" + "Cache-Control: no-cache\r\n" + "Content-Length: %zu\r\n" + "\r\n"; + char header[sizeof(HEADERS) + 20]; + int header_len = snprintf(header, sizeof(header), HEADERS, response.size()); + inspector_write(socket, header, header_len); + inspector_write(socket, response.data(), response.size()); + } + + void SendVersionResponse(InspectorSocket* socket) + { + std::map response; + response["Browser"] = "node.js/" NODE_VERSION; + response["Protocol-Version"] = "1.1"; + SendHttpResponse(socket, MapToString(response)); + } + + std::string GetProcessTitle() + { + // uv_get_process_title will trim the title if it is too long. + char title[2048]; + int err = uv_get_process_title(title, sizeof(title)); + if (err == 0) { + return title; + } else { + return "Node.js"; + } + } + + void SendProtocolJson(InspectorSocket* socket) + { + z_stream strm; + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + NODE_CHECK_EQ(Z_OK, inflateInit(&strm)); + static const size_t kDecompressedSize = PROTOCOL_JSON[0] * 0x10000u + PROTOCOL_JSON[1] * 0x100u + PROTOCOL_JSON[2]; + strm.next_in = const_cast(PROTOCOL_JSON + 3); + strm.avail_in = sizeof(PROTOCOL_JSON) - 3; + std::string data(kDecompressedSize, '\0'); + strm.next_out = reinterpret_cast(&data[0]); + strm.avail_out = data.size(); + NODE_CHECK_EQ(Z_STREAM_END, inflate(&strm, Z_FINISH)); + NODE_CHECK_EQ(0, strm.avail_out); + NODE_CHECK_EQ(Z_OK, inflateEnd(&strm)); + SendHttpResponse(socket, data); + } + + const char* match_path_segment(const char* path, const char* expected) + { + size_t len = strlen(expected); + if (StringEqualNoCaseN(path, expected, len)) { + if (path[len] == '/') + return path + len + 1; + if (path[len] == '\0') + return path + len; + } + return nullptr; + } + + // UUID RFC: https://www.ietf.org/rfc/rfc4122.txt + // Used ver 4 - with numbers + std::string GenerateID() + { + uint16_t buffer[8]; + NODE_CHECK(crypto::EntropySource(reinterpret_cast(buffer), + sizeof(buffer))); + + char uuid[256]; + snprintf(uuid, sizeof(uuid), "%04x%04x-%04x-%04x-%04x-%04x%04x%04x", + buffer[0], // time_low + buffer[1], // time_mid + buffer[2], // time_low + (buffer[3] & 0x0fff) | 0x4000, // time_hi_and_version + (buffer[4] & 0x3fff) | 0x8000, // clk_seq_hi clk_seq_low + buffer[5], // node + buffer[6], + buffer[7]); + return uuid; + } + } // namespace + + class V8NodeInspector; + + class AgentImpl { + public: + explicit AgentImpl(node::Environment* env); + ~AgentImpl(); + + // Start the inspector agent thread + bool Start(v8::Platform* platform, const char* path, int port, bool wait); + // Stop the inspector agent + void Stop(); + + bool IsStarted(); + bool IsConnected() { return state_ == State::kConnected; } + void WaitForDisconnect(); + + void FatalException(v8::Local error, + v8::Local message); + + private: + using MessageQueue = std::vector>; + enum class State { kNew, + kAccepting, + kConnected, + kDone, + kError }; + + static void ThreadCbIO(void* agent); + static void OnSocketConnectionIO(uv_stream_t* server, int status); + static bool OnInspectorHandshakeIO(InspectorSocket* socket, + enum inspector_handshake_event state, + const std::string& path); + static void WriteCbIO(uv_async_t* async); + + void InstallInspectorOnProcess(); + + void WorkerRunIO(); + void OnInspectorConnectionIO(InspectorSocket* socket); + void OnRemoteDataIO(InspectorSocket* stream, ssize_t read, + const uv_buf_t* b); + void SetConnected(bool connected); + void DispatchMessages(); + void Write(int session_id, const String16& message); + bool AppendMessage(MessageQueue* vector, int session_id, + const String16& message); + void SwapBehindLock(MessageQueue* vector1, MessageQueue* vector2); + void PostIncomingMessage(const String16& message); + void WaitForFrontendMessage(); + void NotifyMessageReceived(); + State ToState(State state); + void SendTargentsListResponse(InspectorSocket* socket); + bool RespondToGet(InspectorSocket* socket, const std::string& path); + + uv_sem_t start_sem_; + ConditionVariable incoming_message_cond_; + Mutex state_lock_; + uv_thread_t thread_; + uv_loop_t child_loop_; + + int port_; + bool wait_; + bool shutting_down_; + State state_; + node::Environment* parent_env_; + + uv_async_t* data_written_; + uv_async_t io_thread_req_; + InspectorSocket* client_socket_; + V8NodeInspector* inspector_; + v8::Platform* platform_; + MessageQueue incoming_message_queue_; + MessageQueue outgoing_message_queue_; + bool dispatching_messages_; + int frontend_session_id_; + int backend_session_id_; + + std::string script_name_; + std::string script_path_; + const std::string id_; + + friend class ChannelImpl; + friend class DispatchOnInspectorBackendTask; + friend class SetConnectedTask; + friend class V8NodeInspector; + friend void InterruptCallback(v8::Isolate*, void* agent); + friend void DataCallback(uv_stream_t* stream, ssize_t read, + const uv_buf_t* buf); + }; + + void InterruptCallback(v8::Isolate*, void* agent) + { + static_cast(agent)->DispatchMessages(); + } + + void DataCallback(uv_stream_t* stream, ssize_t read, const uv_buf_t* buf) + { + InspectorSocket* socket = inspector_from_stream(stream); + static_cast(socket->data)->OnRemoteDataIO(socket, read, buf); + } + + class DispatchOnInspectorBackendTask : public v8::Task { + public: + explicit DispatchOnInspectorBackendTask(AgentImpl* agent) + : agent_(agent) + { + } + + void Run() override + { + agent_->DispatchMessages(); + } + + private: + AgentImpl* agent_; + }; + + class ChannelImpl final : public blink::protocol::FrontendChannel { + public: + explicit ChannelImpl(AgentImpl* agent) + : agent_(agent) + { + } + virtual ~ChannelImpl() { } + + private: + void sendProtocolResponse(int callId, const String16& message) override + { + sendMessageToFrontend(message); + } + + void sendProtocolNotification(const String16& message) override + { + sendMessageToFrontend(message); + } + + void flushProtocolNotifications() override { } + + void sendMessageToFrontend(const String16& message) + { + agent_->Write(agent_->frontend_session_id_, message); + } + + AgentImpl* const agent_; + }; // Used in V8NodeInspector::currentTimeMS() below. #define NANOS_PER_MSEC 1000000 -using V8Inspector = v8_inspector::V8Inspector; - -class V8NodeInspector : public v8_inspector::V8InspectorClient { - public: - V8NodeInspector(AgentImpl* agent, node::Environment* env, - v8::Platform* platform) - : agent_(agent), - env_(env), - platform_(platform), - terminated_(false), - running_nested_loop_(false), - inspector_(V8Inspector::create(env->isolate(), this)) { - inspector_->contextCreated( - v8_inspector::V8ContextInfo(env->context(), 1, "NodeJS Main Context")); - } - - void runMessageLoopOnPause(int context_group_id) override { - if (running_nested_loop_) - return; - terminated_ = false; - running_nested_loop_ = true; - while (!terminated_) { - agent_->WaitForFrontendMessage(); - while (v8::platform::PumpMessageLoop(platform_, env_->isolate())) - {} - } - terminated_ = false; - running_nested_loop_ = false; - } - - double currentTimeMS() override { - return uv_hrtime() * 1.0 / NANOS_PER_MSEC; - } - - void quitMessageLoopOnPause() override { - terminated_ = true; - } - - void connectFrontend() { - session_ = inspector_->connect(1, new ChannelImpl(agent_), nullptr); - } - - void disconnectFrontend() { - session_.reset(); - } - - void dispatchMessageFromFrontend(const String16& message) { - CHECK(session_); - session_->dispatchProtocolMessage(message); - } - - v8::Local ensureDefaultContextInGroup(int contextGroupId) - override { - return env_->context(); - } - - V8Inspector* inspector() { - return inspector_.get(); - } - - private: - AgentImpl* agent_; - node::Environment* env_; - v8::Platform* platform_; - bool terminated_; - bool running_nested_loop_; - std::unique_ptr inspector_; - std::unique_ptr session_; -}; - -AgentImpl::AgentImpl(Environment* env) : port_(0), - wait_(false), - shutting_down_(false), - state_(State::kNew), - parent_env_(env), - data_written_(new uv_async_t()), - client_socket_(nullptr), - inspector_(nullptr), - platform_(nullptr), - dispatching_messages_(false), - frontend_session_id_(0), - backend_session_id_(0), - id_(GenerateID()) { - CHECK_EQ(0, uv_sem_init(&start_sem_, 0)); - memset(&io_thread_req_, 0, sizeof(io_thread_req_)); - CHECK_EQ(0, uv_async_init(env->event_loop(), data_written_, nullptr)); - uv_unref(reinterpret_cast(data_written_)); -} - -AgentImpl::~AgentImpl() { - auto close_cb = [](uv_handle_t* handle) { - delete reinterpret_cast(handle); - }; - uv_close(reinterpret_cast(data_written_), close_cb); - data_written_ = nullptr; -} - -void InspectorConsoleCall(const v8::FunctionCallbackInfo& info) { - v8::Isolate* isolate = info.GetIsolate(); - v8::Local context = isolate->GetCurrentContext(); - - CHECK(info.Data()->IsArray()); - v8::Local args = info.Data().As(); - CHECK_EQ(args->Length(), 3); - - v8::Local inspector_method = - args->Get(context, 0).ToLocalChecked(); - CHECK(inspector_method->IsFunction()); - v8::Local node_method = - args->Get(context, 1).ToLocalChecked(); - CHECK(node_method->IsFunction()); - v8::Local config_value = - args->Get(context, 2).ToLocalChecked(); - CHECK(config_value->IsObject()); - v8::Local config_object = config_value.As(); - - std::vector> call_args(info.Length()); - for (int i = 0; i < info.Length(); ++i) { - call_args[i] = info[i]; - } - - v8::Local in_call_key = OneByteString(isolate, "in_call"); - bool in_call = config_object->Has(context, in_call_key).FromMaybe(false); - if (!in_call) { - CHECK(config_object->Set(context, - in_call_key, - v8::True(isolate)).FromJust()); - CHECK(!inspector_method.As()->Call( - context, - info.Holder(), - call_args.size(), - call_args.data()).IsEmpty()); - } - - v8::TryCatch try_catch(info.GetIsolate()); - static_cast(node_method.As()->Call(context, - info.Holder(), - call_args.size(), - call_args.data())); - CHECK(config_object->Delete(context, in_call_key).FromJust()); - if (try_catch.HasCaught()) - try_catch.ReThrow(); -} - -void InspectorWrapConsoleCall(const v8::FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - - if (args.Length() != 3 || !args[0]->IsFunction() || - !args[1]->IsFunction() || !args[2]->IsObject()) { - return env->ThrowError("inspector.wrapConsoleCall takes exactly 3 " - "arguments: two functions and an object."); - } - - v8::Local array = v8::Array::New(env->isolate(), args.Length()); - CHECK(array->Set(env->context(), 0, args[0]).FromJust()); - CHECK(array->Set(env->context(), 1, args[1]).FromJust()); - CHECK(array->Set(env->context(), 2, args[2]).FromJust()); - args.GetReturnValue().Set(v8::Function::New(env->context(), - InspectorConsoleCall, - array).ToLocalChecked()); -} - -bool AgentImpl::Start(v8::Platform* platform, const char* path, - int port, bool wait) { - auto env = parent_env_; - inspector_ = new V8NodeInspector(this, env, platform); - platform_ = platform; - if (path != nullptr) - script_name_ = path; - - InstallInspectorOnProcess(); - - int err = uv_loop_init(&child_loop_); - CHECK_EQ(err, 0); - - port_ = port; - wait_ = wait; - - err = uv_thread_create(&thread_, AgentImpl::ThreadCbIO, this); - CHECK_EQ(err, 0); - uv_sem_wait(&start_sem_); - - if (state_ == State::kError) { - Stop(); - return false; - } - state_ = State::kAccepting; - if (wait) { - DispatchMessages(); - } - return true; -} - -void AgentImpl::Stop() { - int err = uv_thread_join(&thread_); - CHECK_EQ(err, 0); - delete inspector_; -} - -bool AgentImpl::IsStarted() { - return !!platform_; -} - -void AgentImpl::WaitForDisconnect() { - shutting_down_ = true; - fprintf(stderr, "Waiting for the debugger to disconnect...\n"); - fflush(stderr); - inspector_->runMessageLoopOnPause(0); -} - -#define READONLY_PROPERTY(obj, str, var) \ - do { \ - obj->DefineOwnProperty(env->context(), \ - OneByteString(env->isolate(), str), \ - var, \ - v8::ReadOnly).FromJust(); \ - } while (0) - -void AgentImpl::InstallInspectorOnProcess() { - auto env = parent_env_; - v8::Local process = env->process_object(); - v8::Local inspector = v8::Object::New(env->isolate()); - READONLY_PROPERTY(process, "inspector", inspector); - env->SetMethod(inspector, "wrapConsoleCall", InspectorWrapConsoleCall); -} - -String16 ToProtocolString(v8::Local value) { - if (value.IsEmpty() || value->IsNull() || value->IsUndefined() || - !value->IsString()) { - return String16(); - } - v8::Local string_value = v8::Local::Cast(value); - std::basic_string buffer(string_value->Length(), '\0'); - string_value->Write(&buffer[0], 0, string_value->Length()); - return String16(buffer); -} - -void AgentImpl::FatalException(v8::Local error, - v8::Local message) { - if (!IsStarted()) - return; - auto env = parent_env_; - v8::Local context = env->context(); - - int script_id = message->GetScriptOrigin().ScriptID()->Value(); - std::unique_ptr stack_trace = - inspector_->inspector()->createStackTrace(message->GetStackTrace()); - - if (stack_trace && !stack_trace->isEmpty() && - String16::fromInteger(script_id) == stack_trace->topScriptId()) { - script_id = 0; - } - - inspector_->inspector()->exceptionThrown( - context, - "Uncaught", - error, - ToProtocolString(message->Get()), - ToProtocolString(message->GetScriptResourceName()), - message->GetLineNumber(context).FromMaybe(0), - message->GetStartColumn(context).FromMaybe(0), - std::move(stack_trace), - script_id); - WaitForDisconnect(); -} - -// static -void AgentImpl::ThreadCbIO(void* agent) { - static_cast(agent)->WorkerRunIO(); -} - -// static -void AgentImpl::OnSocketConnectionIO(uv_stream_t* server, int status) { - if (status == 0) { - InspectorSocket* socket = new InspectorSocket(); - socket->data = server->data; - if (inspector_accept(server, socket, - AgentImpl::OnInspectorHandshakeIO) != 0) { - delete socket; - } - } -} - -// static -bool AgentImpl::OnInspectorHandshakeIO(InspectorSocket* socket, - enum inspector_handshake_event state, - const std::string& path) { - AgentImpl* agent = static_cast(socket->data); - switch (state) { - case kInspectorHandshakeHttpGet: - return agent->RespondToGet(socket, path); - case kInspectorHandshakeUpgrading: - return path.length() == agent->id_.length() + 1 && - path.find(agent->id_) == 1; - case kInspectorHandshakeUpgraded: - agent->OnInspectorConnectionIO(socket); - return true; - case kInspectorHandshakeFailed: - delete socket; - return false; - default: - UNREACHABLE(); - return false; - } -} - -void AgentImpl::OnRemoteDataIO(InspectorSocket* socket, - ssize_t read, - const uv_buf_t* buf) { - if (read > 0) { - String16 str = String16::fromUTF8(buf->base, read); - // TODO(pfeldman): Instead of blocking execution while debugger - // engages, node should wait for the run callback from the remote client - // and initiate its startup. This is a change to node.cc that should be - // upstreamed separately. - if (wait_&& str.find("\"Runtime.runIfWaitingForDebugger\"") - != std::string::npos) { - wait_ = false; - uv_sem_post(&start_sem_); - } - PostIncomingMessage(str); - } else { - // EOF - if (client_socket_ == socket) { - String16 message(TAG_DISCONNECT, sizeof(TAG_DISCONNECT) - 1); - client_socket_ = nullptr; - PostIncomingMessage(message); - } - DisconnectAndDisposeIO(socket); - } - if (buf) { - delete[] buf->base; - } -} - -void AgentImpl::SendTargentsListResponse(InspectorSocket* socket) { - std::map response; - response["description"] = "node.js instance"; - response["faviconUrl"] = "https://nodejs.org/static/favicon.ico"; - response["id"] = id_; - response["title"] = script_name_.empty() ? GetProcessTitle() : script_name_; - Escape(&response["title"]); - response["type"] = "node"; - // This attribute value is a "best effort" URL that is passed as a JSON - // string. It is not guaranteed to resolve to a valid resource. - response["url"] = "file://" + script_path_; - Escape(&response["url"]); - - if (!client_socket_) { - std::string address = GetWsUrl(port_, id_); - - std::ostringstream frontend_url; - frontend_url << "https://chrome-devtools-frontend.appspot.com/serve_file/@"; - frontend_url << V8_INSPECTOR_REVISION; - frontend_url << "/inspector.html?experiments=true&v8only=true&ws="; - frontend_url << address; - - response["devtoolsFrontendUrl"] += frontend_url.str(); - response["webSocketDebuggerUrl"] = "ws://" + address; - } - SendHttpResponse(socket, MapToString(response)); -} - -bool AgentImpl::RespondToGet(InspectorSocket* socket, const std::string& path) { - const char* command = match_path_segment(path.c_str(), "/json"); - if (command == nullptr) - return false; - - if (match_path_segment(command, "list") || command[0] == '\0') { - SendTargentsListResponse(socket); - return true; - } else if (match_path_segment(command, "protocol")) { - SendProtocolJson(socket); - return true; - } else if (match_path_segment(command, "version")) { - SendVersionResponse(socket); - return true; - } else if (const char* pid = match_path_segment(command, "activate")) { - if (pid != id_) - return false; - SendHttpResponse(socket, "Target activated"); - return true; - } - return false; -} - -// static -void AgentImpl::WriteCbIO(uv_async_t* async) { - AgentImpl* agent = static_cast(async->data); - InspectorSocket* socket = agent->client_socket_; - if (socket) { - MessageQueue outgoing_messages; - agent->SwapBehindLock(&agent->outgoing_message_queue_, &outgoing_messages); - for (const MessageQueue::value_type& outgoing : outgoing_messages) { - if (outgoing.first == agent->frontend_session_id_) { - std::string message = outgoing.second.utf8(); - inspector_write(socket, message.c_str(), message.length()); - } - } - } -} - -void AgentImpl::WorkerRunIO() { - sockaddr_in addr; - uv_tcp_t server; - int err = uv_loop_init(&child_loop_); - CHECK_EQ(err, 0); - err = uv_async_init(&child_loop_, &io_thread_req_, AgentImpl::WriteCbIO); - CHECK_EQ(err, 0); - io_thread_req_.data = this; - if (!script_name_.empty()) { - uv_fs_t req; - if (0 == uv_fs_realpath(&child_loop_, &req, script_name_.c_str(), nullptr)) - script_path_ = std::string(reinterpret_cast(req.ptr)); - uv_fs_req_cleanup(&req); - } - uv_tcp_init(&child_loop_, &server); - uv_ip4_addr("0.0.0.0", port_, &addr); - server.data = this; - err = uv_tcp_bind(&server, - reinterpret_cast(&addr), 0); - if (err == 0) { - err = uv_listen(reinterpret_cast(&server), 1, - OnSocketConnectionIO); - } - if (err != 0) { - fprintf(stderr, "Unable to open devtools socket: %s\n", uv_strerror(err)); - state_ = State::kError; // Safe, main thread is waiting on semaphore - uv_close(reinterpret_cast(&io_thread_req_), nullptr); - uv_close(reinterpret_cast(&server), nullptr); - uv_loop_close(&child_loop_); - uv_sem_post(&start_sem_); - return; - } - PrintDebuggerReadyMessage(port_, id_); - if (!wait_) { - uv_sem_post(&start_sem_); - } - uv_run(&child_loop_, UV_RUN_DEFAULT); - uv_close(reinterpret_cast(&io_thread_req_), nullptr); - uv_close(reinterpret_cast(&server), nullptr); - DisconnectAndDisposeIO(client_socket_); - uv_run(&child_loop_, UV_RUN_NOWAIT); - err = uv_loop_close(&child_loop_); - CHECK_EQ(err, 0); -} - -bool AgentImpl::AppendMessage(MessageQueue* queue, int session_id, - const String16& message) { - Mutex::ScopedLock scoped_lock(state_lock_); - bool trigger_pumping = queue->empty(); - queue->push_back(std::make_pair(session_id, message)); - return trigger_pumping; -} - -void AgentImpl::SwapBehindLock(MessageQueue* vector1, MessageQueue* vector2) { - Mutex::ScopedLock scoped_lock(state_lock_); - vector1->swap(*vector2); -} - -void AgentImpl::PostIncomingMessage(const String16& message) { - if (AppendMessage(&incoming_message_queue_, frontend_session_id_, message)) { - v8::Isolate* isolate = parent_env_->isolate(); - platform_->CallOnForegroundThread(isolate, - new DispatchOnInspectorBackendTask(this)); - isolate->RequestInterrupt(InterruptCallback, this); - uv_async_send(data_written_); - } - NotifyMessageReceived(); -} - -void AgentImpl::WaitForFrontendMessage() { - Mutex::ScopedLock scoped_lock(state_lock_); - if (incoming_message_queue_.empty()) - incoming_message_cond_.Wait(scoped_lock); -} - -void AgentImpl::NotifyMessageReceived() { - Mutex::ScopedLock scoped_lock(state_lock_); - incoming_message_cond_.Broadcast(scoped_lock); -} - -void AgentImpl::OnInspectorConnectionIO(InspectorSocket* socket) { - if (client_socket_) { - DisconnectAndDisposeIO(socket); - return; - } - client_socket_ = socket; - inspector_read_start(socket, OnBufferAlloc, DataCallback); - frontend_session_id_++; - PostIncomingMessage(String16(TAG_CONNECT, sizeof(TAG_CONNECT) - 1)); -} - -void AgentImpl::DispatchMessages() { - // This function can be reentered if there was an incoming message while - // V8 was processing another inspector request (e.g. if the user is - // evaluating a long-running JS code snippet). This can happen only at - // specific points (e.g. the lines that call inspector_ methods) - if (dispatching_messages_) - return; - dispatching_messages_ = true; - MessageQueue tasks; - do { - tasks.clear(); - SwapBehindLock(&incoming_message_queue_, &tasks); - for (const MessageQueue::value_type& pair : tasks) { - const String16& message = pair.second; - if (message == TAG_CONNECT) { - CHECK_EQ(State::kAccepting, state_); - backend_session_id_++; - state_ = State::kConnected; - fprintf(stderr, "Debugger attached.\n"); - inspector_->connectFrontend(); - } else if (message == TAG_DISCONNECT) { - CHECK_EQ(State::kConnected, state_); - if (shutting_down_) { - state_ = State::kDone; + using V8Inspector = v8_inspector::V8Inspector; + + class V8NodeInspector : public v8_inspector::V8InspectorClient { + public: + V8NodeInspector(AgentImpl* agent, node::Environment* env, + v8::Platform* platform) + : agent_(agent) + , env_(env) + , platform_(platform) + , terminated_(false) + , running_nested_loop_(false) + , inspector_(V8Inspector::create(env->isolate(), this)) + { + inspector_->contextCreated( + v8_inspector::V8ContextInfo(env->context(), 1, "NodeJS Main Context")); + } + + void runMessageLoopOnPause(int context_group_id) override + { + if (running_nested_loop_) + return; + terminated_ = false; + running_nested_loop_ = true; + while (!terminated_) { + agent_->WaitForFrontendMessage(); + while (v8::platform::PumpMessageLoop(platform_, env_->isolate())) { } + } + terminated_ = false; + running_nested_loop_ = false; + } + + double currentTimeMS() override + { + return uv_hrtime() * 1.0 / NANOS_PER_MSEC; + } + + void quitMessageLoopOnPause() override + { + terminated_ = true; + } + + void connectFrontend() + { + session_ = inspector_->connect(1, new ChannelImpl(agent_), nullptr); + } + + void disconnectFrontend() + { + session_.reset(); + } + + void dispatchMessageFromFrontend(const String16& message) + { + NODE_CHECK(session_); + session_->dispatchProtocolMessage(message); + } + + v8::Local ensureDefaultContextInGroup(int contextGroupId) + override + { + return env_->context(); + } + + V8Inspector* inspector() + { + return inspector_.get(); + } + + private: + AgentImpl* agent_; + node::Environment* env_; + v8::Platform* platform_; + bool terminated_; + bool running_nested_loop_; + std::unique_ptr inspector_; + std::unique_ptr session_; + }; + + AgentImpl::AgentImpl(Environment* env) + : port_(0) + , wait_(false) + , shutting_down_(false) + , state_(State::kNew) + , parent_env_(env) + , data_written_(new uv_async_t()) + , client_socket_(nullptr) + , inspector_(nullptr) + , platform_(nullptr) + , dispatching_messages_(false) + , frontend_session_id_(0) + , backend_session_id_(0) + , id_(GenerateID()) + { + NODE_CHECK_EQ(0, uv_sem_init(&start_sem_, 0)); + memset(&io_thread_req_, 0, sizeof(io_thread_req_)); + NODE_CHECK_EQ(0, uv_async_init(env->event_loop(), data_written_, nullptr)); + uv_unref(reinterpret_cast(data_written_)); + } + + AgentImpl::~AgentImpl() + { + auto close_cb = [](uv_handle_t* handle) { + delete reinterpret_cast(handle); + }; + uv_close(reinterpret_cast(data_written_), close_cb); + data_written_ = nullptr; + } + + void InspectorConsoleCall(const v8::FunctionCallbackInfo& info) + { + v8::Isolate* isolate = info.GetIsolate(); + v8::Local context = isolate->GetCurrentContext(); + + NODE_CHECK(info.Data()->IsArray()); + v8::Local args = info.Data().As(); + NODE_CHECK_EQ(args->Length(), 3); + + v8::Local inspector_method = args->Get(context, 0).ToLocalChecked(); + NODE_CHECK(inspector_method->IsFunction()); + v8::Local node_method = args->Get(context, 1).ToLocalChecked(); + NODE_CHECK(node_method->IsFunction()); + v8::Local config_value = args->Get(context, 2).ToLocalChecked(); + NODE_CHECK(config_value->IsObject()); + v8::Local config_object = config_value.As(); + + std::vector> call_args(info.Length()); + for (int i = 0; i < info.Length(); ++i) { + call_args[i] = info[i]; + } + + v8::Local in_call_key = OneByteString(isolate, "in_call"); + bool in_call = config_object->Has(context, in_call_key).FromMaybe(false); + if (!in_call) { + NODE_CHECK(config_object->Set(context, + in_call_key, + v8::True(isolate)) + .FromJust()); + NODE_CHECK(!inspector_method.As()->Call( + context, + info.Holder(), + call_args.size(), + call_args.data()) + .IsEmpty()); + } + + v8::TryCatch try_catch(info.GetIsolate()); + static_cast(node_method.As()->Call(context, + info.Holder(), + call_args.size(), + call_args.data())); + NODE_CHECK(config_object->Delete(context, in_call_key).FromJust()); + if (try_catch.HasCaught()) + try_catch.ReThrow(); + } + + void InspectorWrapConsoleCall(const v8::FunctionCallbackInfo& args) + { + Environment* env = Environment::GetCurrent(args); + + if (args.Length() != 3 || !args[0]->IsFunction() || !args[1]->IsFunction() || !args[2]->IsObject()) { + return env->ThrowError("inspector.wrapConsoleCall takes exactly 3 " + "arguments: two functions and an object."); + } + + v8::Local array = v8::Array::New(env->isolate(), args.Length()); + NODE_CHECK(array->Set(env->context(), 0, args[0]).FromJust()); + NODE_CHECK(array->Set(env->context(), 1, args[1]).FromJust()); + NODE_CHECK(array->Set(env->context(), 2, args[2]).FromJust()); + args.GetReturnValue().Set(v8::Function::New(env->context(), + InspectorConsoleCall, + array) + .ToLocalChecked()); + } + + bool AgentImpl::Start(v8::Platform* platform, const char* path, + int port, bool wait) + { + auto env = parent_env_; + inspector_ = new V8NodeInspector(this, env, platform); + platform_ = platform; + if (path != nullptr) + script_name_ = path; + + InstallInspectorOnProcess(); + + int err = uv_loop_init(&child_loop_); + NODE_CHECK_EQ(err, 0); + + port_ = port; + wait_ = wait; + + err = uv_thread_create(&thread_, AgentImpl::ThreadCbIO, this); + NODE_CHECK_EQ(err, 0); + uv_sem_wait(&start_sem_); + + if (state_ == State::kError) { + Stop(); + return false; + } + state_ = State::kAccepting; + if (wait) { + DispatchMessages(); + } + return true; + } + + void AgentImpl::Stop() + { + int err = uv_thread_join(&thread_); + NODE_CHECK_EQ(err, 0); + delete inspector_; + } + + bool AgentImpl::IsStarted() + { + return !!platform_; + } + + void AgentImpl::WaitForDisconnect() + { + shutting_down_ = true; + fprintf(stderr, "Waiting for the debugger to disconnect...\n"); + fflush(stderr); + inspector_->runMessageLoopOnPause(0); + } + +#define READONLY_PROPERTY(obj, str, var) \ + do { \ + obj->DefineOwnProperty(env->context(), \ + OneByteString(env->isolate(), str), \ + var, \ + v8::ReadOnly) \ + .FromJust(); \ + } while (0) + + void AgentImpl::InstallInspectorOnProcess() + { + auto env = parent_env_; + v8::Local process = env->process_object(); + v8::Local inspector = v8::Object::New(env->isolate()); + READONLY_PROPERTY(process, "inspector", inspector); + env->SetMethod(inspector, "wrapConsoleCall", InspectorWrapConsoleCall); + } + + String16 ToProtocolString(v8::Local value) + { + if (value.IsEmpty() || value->IsNull() || value->IsUndefined() || !value->IsString()) { + return String16(); + } + v8::Local string_value = v8::Local::Cast(value); + std::basic_string buffer(string_value->Length(), '\0'); + string_value->Write(&buffer[0], 0, string_value->Length()); + return String16(buffer); + } + + void AgentImpl::FatalException(v8::Local error, + v8::Local message) + { + if (!IsStarted()) + return; + auto env = parent_env_; + v8::Local context = env->context(); + + int script_id = message->GetScriptOrigin().ScriptID()->Value(); + std::unique_ptr stack_trace = inspector_->inspector()->createStackTrace(message->GetStackTrace()); + + if (stack_trace && !stack_trace->isEmpty() && String16::fromInteger(script_id) == stack_trace->topScriptId()) { + script_id = 0; + } + + inspector_->inspector()->exceptionThrown( + context, + "Uncaught", + error, + ToProtocolString(message->Get()), + ToProtocolString(message->GetScriptResourceName()), + message->GetLineNumber(context).FromMaybe(0), + message->GetStartColumn(context).FromMaybe(0), + std::move(stack_trace), + script_id); + WaitForDisconnect(); + } + + // static + void AgentImpl::ThreadCbIO(void* agent) + { + static_cast(agent)->WorkerRunIO(); + } + + // static + void AgentImpl::OnSocketConnectionIO(uv_stream_t* server, int status) + { + if (status == 0) { + InspectorSocket* socket = new InspectorSocket(); + socket->data = server->data; + if (inspector_accept(server, socket, + AgentImpl::OnInspectorHandshakeIO) + != 0) { + delete socket; + } + } + } + + // static + bool AgentImpl::OnInspectorHandshakeIO(InspectorSocket* socket, + enum inspector_handshake_event state, + const std::string& path) + { + AgentImpl* agent = static_cast(socket->data); + switch (state) { + case kInspectorHandshakeHttpGet: + return agent->RespondToGet(socket, path); + case kInspectorHandshakeUpgrading: + return path.length() == agent->id_.length() + 1 && path.find(agent->id_) == 1; + case kInspectorHandshakeUpgraded: + agent->OnInspectorConnectionIO(socket); + return true; + case kInspectorHandshakeFailed: + delete socket; + return false; + default: + UNREACHABLE(); + return false; + } + } + + void AgentImpl::OnRemoteDataIO(InspectorSocket* socket, + ssize_t read, + const uv_buf_t* buf) + { + if (read > 0) { + String16 str = String16::fromUTF8(buf->base, read); + // TODO(pfeldman): Instead of blocking execution while debugger + // engages, node should wait for the run callback from the remote client + // and initiate its startup. This is a change to node.cc that should be + // upstreamed separately. + if (wait_ && str.find("\"Runtime.runIfWaitingForDebugger\"") != std::string::npos) { + wait_ = false; + uv_sem_post(&start_sem_); + } + PostIncomingMessage(str); } else { - PrintDebuggerReadyMessage(port_, id_); - state_ = State::kAccepting; - } - inspector_->quitMessageLoopOnPause(); - inspector_->disconnectFrontend(); - } else { - inspector_->dispatchMessageFromFrontend(message); - } - } - } while (!tasks.empty()); - uv_async_send(data_written_); - dispatching_messages_ = false; -} - -void AgentImpl::Write(int session_id, const String16& message) { - AppendMessage(&outgoing_message_queue_, session_id, message); - int err = uv_async_send(&io_thread_req_); - CHECK_EQ(0, err); -} - -// Exported class Agent -Agent::Agent(node::Environment* env) : impl(new AgentImpl(env)) {} - -Agent::~Agent() { - delete impl; -} - -bool Agent::Start(v8::Platform* platform, const char* path, - int port, bool wait) { - return impl->Start(platform, path, port, wait); -} - -void Agent::Stop() { - impl->Stop(); -} - -bool Agent::IsStarted() { - return impl->IsStarted(); -} - -bool Agent::IsConnected() { - return impl->IsConnected(); -} - -void Agent::WaitForDisconnect() { - impl->WaitForDisconnect(); -} - -void Agent::FatalException(v8::Local error, - v8::Local message) { - impl->FatalException(error, message); -} - - -} // namespace inspector -} // namespace node + // EOF + if (client_socket_ == socket) { + String16 message(TAG_DISCONNECT, sizeof(TAG_DISCONNECT) - 1); + client_socket_ = nullptr; + PostIncomingMessage(message); + } + DisconnectAndDisposeIO(socket); + } + if (buf) { + delete[] buf->base; + } + } + + void AgentImpl::SendTargentsListResponse(InspectorSocket* socket) + { + std::map response; + response["description"] = "node.js instance"; + response["faviconUrl"] = "https://nodejs.org/static/favicon.ico"; + response["id"] = id_; + response["title"] = script_name_.empty() ? GetProcessTitle() : script_name_; + Escape(&response["title"]); + response["type"] = "node"; + // This attribute value is a "best effort" URL that is passed as a JSON + // string. It is not guaranteed to resolve to a valid resource. + response["url"] = "file://" + script_path_; + Escape(&response["url"]); + + if (!client_socket_) { + std::string address = GetWsUrl(port_, id_); + + std::ostringstream frontend_url; + frontend_url << "https://chrome-devtools-frontend.appspot.com/serve_file/@"; + frontend_url << V8_INSPECTOR_REVISION; + frontend_url << "/inspector.html?experiments=true&v8only=true&ws="; + frontend_url << address; + + response["devtoolsFrontendUrl"] += frontend_url.str(); + response["webSocketDebuggerUrl"] = "ws://" + address; + } + SendHttpResponse(socket, MapToString(response)); + } + + bool AgentImpl::RespondToGet(InspectorSocket* socket, const std::string& path) + { + const char* command = match_path_segment(path.c_str(), "/json"); + if (command == nullptr) + return false; + + if (match_path_segment(command, "list") || command[0] == '\0') { + SendTargentsListResponse(socket); + return true; + } else if (match_path_segment(command, "protocol")) { + SendProtocolJson(socket); + return true; + } else if (match_path_segment(command, "version")) { + SendVersionResponse(socket); + return true; + } else if (const char* pid = match_path_segment(command, "activate")) { + if (pid != id_) + return false; + SendHttpResponse(socket, "Target activated"); + return true; + } + return false; + } + + // static + void AgentImpl::WriteCbIO(uv_async_t* async) + { + AgentImpl* agent = static_cast(async->data); + InspectorSocket* socket = agent->client_socket_; + if (socket) { + MessageQueue outgoing_messages; + agent->SwapBehindLock(&agent->outgoing_message_queue_, &outgoing_messages); + for (const MessageQueue::value_type& outgoing : outgoing_messages) { + if (outgoing.first == agent->frontend_session_id_) { + std::string message = outgoing.second.utf8(); + inspector_write(socket, message.c_str(), message.length()); + } + } + } + } + + void AgentImpl::WorkerRunIO() + { + sockaddr_in addr; + uv_tcp_t server; + int err = uv_loop_init(&child_loop_); + NODE_CHECK_EQ(err, 0); + err = uv_async_init(&child_loop_, &io_thread_req_, AgentImpl::WriteCbIO); + NODE_CHECK_EQ(err, 0); + io_thread_req_.data = this; + if (!script_name_.empty()) { + uv_fs_t req; + if (0 == uv_fs_realpath(&child_loop_, &req, script_name_.c_str(), nullptr)) + script_path_ = std::string(reinterpret_cast(req.ptr)); + uv_fs_req_cleanup(&req); + } + uv_tcp_init(&child_loop_, &server); + uv_ip4_addr("0.0.0.0", port_, &addr); + server.data = this; + err = uv_tcp_bind(&server, + reinterpret_cast(&addr), 0); + if (err == 0) { + err = uv_listen(reinterpret_cast(&server), 1, + OnSocketConnectionIO); + } + if (err != 0) { + fprintf(stderr, "Unable to open devtools socket: %s\n", uv_strerror(err)); + state_ = State::kError; // Safe, main thread is waiting on semaphore + uv_close(reinterpret_cast(&io_thread_req_), nullptr); + uv_close(reinterpret_cast(&server), nullptr); + uv_loop_close(&child_loop_); + uv_sem_post(&start_sem_); + return; + } + PrintDebuggerReadyMessage(port_, id_); + if (!wait_) { + uv_sem_post(&start_sem_); + } + uv_run(&child_loop_, UV_RUN_DEFAULT); + uv_close(reinterpret_cast(&io_thread_req_), nullptr); + uv_close(reinterpret_cast(&server), nullptr); + DisconnectAndDisposeIO(client_socket_); + uv_run(&child_loop_, UV_RUN_NOWAIT); + err = uv_loop_close(&child_loop_); + NODE_CHECK_EQ(err, 0); + } + + bool AgentImpl::AppendMessage(MessageQueue* queue, int session_id, + const String16& message) + { + Mutex::ScopedLock scoped_lock(state_lock_); + bool trigger_pumping = queue->empty(); + queue->push_back(std::make_pair(session_id, message)); + return trigger_pumping; + } + + void AgentImpl::SwapBehindLock(MessageQueue* vector1, MessageQueue* vector2) + { + Mutex::ScopedLock scoped_lock(state_lock_); + vector1->swap(*vector2); + } + + void AgentImpl::PostIncomingMessage(const String16& message) + { + if (AppendMessage(&incoming_message_queue_, frontend_session_id_, message)) { + v8::Isolate* isolate = parent_env_->isolate(); + platform_->CallOnForegroundThread(isolate, + new DispatchOnInspectorBackendTask(this)); + isolate->RequestInterrupt(InterruptCallback, this); + uv_async_send(data_written_); + } + NotifyMessageReceived(); + } + + void AgentImpl::WaitForFrontendMessage() + { + Mutex::ScopedLock scoped_lock(state_lock_); + if (incoming_message_queue_.empty()) + incoming_message_cond_.Wait(scoped_lock); + } + + void AgentImpl::NotifyMessageReceived() + { + Mutex::ScopedLock scoped_lock(state_lock_); + incoming_message_cond_.Broadcast(scoped_lock); + } + + void AgentImpl::OnInspectorConnectionIO(InspectorSocket* socket) + { + if (client_socket_) { + DisconnectAndDisposeIO(socket); + return; + } + client_socket_ = socket; + inspector_read_start(socket, OnBufferAlloc, DataCallback); + frontend_session_id_++; + PostIncomingMessage(String16(TAG_CONNECT, sizeof(TAG_CONNECT) - 1)); + } + + void AgentImpl::DispatchMessages() + { + // This function can be reentered if there was an incoming message while + // V8 was processing another inspector request (e.g. if the user is + // evaluating a long-running JS code snippet). This can happen only at + // specific points (e.g. the lines that call inspector_ methods) + if (dispatching_messages_) + return; + dispatching_messages_ = true; + MessageQueue tasks; + do { + tasks.clear(); + SwapBehindLock(&incoming_message_queue_, &tasks); + for (const MessageQueue::value_type& pair : tasks) { + const String16& message = pair.second; + if (message == TAG_CONNECT) { + NODE_CHECK_EQ(State::kAccepting, state_); + backend_session_id_++; + state_ = State::kConnected; + fprintf(stderr, "Debugger attached.\n"); + inspector_->connectFrontend(); + } else if (message == TAG_DISCONNECT) { + NODE_CHECK_EQ(State::kConnected, state_); + if (shutting_down_) { + state_ = State::kDone; + } else { + PrintDebuggerReadyMessage(port_, id_); + state_ = State::kAccepting; + } + inspector_->quitMessageLoopOnPause(); + inspector_->disconnectFrontend(); + } else { + inspector_->dispatchMessageFromFrontend(message); + } + } + } while (!tasks.empty()); + uv_async_send(data_written_); + dispatching_messages_ = false; + } + + void AgentImpl::Write(int session_id, const String16& message) + { + AppendMessage(&outgoing_message_queue_, session_id, message); + int err = uv_async_send(&io_thread_req_); + NODE_CHECK_EQ(0, err); + } + + // Exported class Agent + Agent::Agent(node::Environment* env) + : impl(new AgentImpl(env)) + { + } + + Agent::~Agent() + { + delete impl; + } + + bool Agent::Start(v8::Platform* platform, const char* path, + int port, bool wait) + { + return impl->Start(platform, path, port, wait); + } + + void Agent::Stop() + { + impl->Stop(); + } + + bool Agent::IsStarted() + { + return impl->IsStarted(); + } + + bool Agent::IsConnected() + { + return impl->IsConnected(); + } + + void Agent::WaitForDisconnect() + { + impl->WaitForDisconnect(); + } + + void Agent::FatalException(v8::Local error, + v8::Local message) + { + impl->FatalException(error, message); + } + +} // namespace inspector +} // namespace node diff --git a/node/src/inspector_agent.h b/node/src/inspector_agent.h index 3607cffba5..b66e718392 100644 --- a/node/src/inspector_agent.h +++ b/node/src/inspector_agent.h @@ -8,42 +8,43 @@ // Forward declaration to break recursive dependency chain with src/env.h. namespace node { class Environment; -} // namespace node +} // namespace node namespace v8 { class Platform; -template +template class Local; class Value; class Message; -} // namespace v8 +} // namespace v8 namespace node { namespace inspector { -class AgentImpl; + class AgentImpl; -class Agent { - public: - explicit Agent(node::Environment* env); - ~Agent(); + class Agent { + public: + explicit Agent(node::Environment* env); + ~Agent(); - // Start the inspector agent thread - bool Start(v8::Platform* platform, const char* path, int port, bool wait); - // Stop the inspector agent - void Stop(); + // Start the inspector agent thread + bool Start(v8::Platform* platform, const char* path, int port, bool wait); + // Stop the inspector agent + void Stop(); - bool IsStarted(); - bool IsConnected(); - void WaitForDisconnect(); + bool IsStarted(); + bool IsConnected(); + void WaitForDisconnect(); - void FatalException(v8::Local error, - v8::Local message); - private: - AgentImpl* impl; -}; + void FatalException(v8::Local error, + v8::Local message); -} // namespace inspector -} // namespace node + private: + AgentImpl* impl; + }; -#endif // SRC_INSPECTOR_AGENT_H_ +} // namespace inspector +} // namespace node + +#endif // SRC_INSPECTOR_AGENT_H_ diff --git a/node/src/inspector_socket.cc b/node/src/inspector_socket.cc index 6edc08248b..b41954b33e 100644 --- a/node/src/inspector_socket.cc +++ b/node/src/inspector_socket.cc @@ -5,7 +5,7 @@ #define NODE_WANT_INTERNALS 1 #include "base64.h" -#include "openssl/sha.h" // Sha-1 hash +#include "openssl/sha.h" // Sha-1 hash #include #include @@ -19,608 +19,642 @@ namespace node { namespace inspector { -static const char CLOSE_FRAME[] = {'\x88', '\x00'}; + static const char CLOSE_FRAME[] = { '\x88', '\x00' }; -enum ws_decode_result { - FRAME_OK, FRAME_INCOMPLETE, FRAME_CLOSE, FRAME_ERROR -}; + enum ws_decode_result { + FRAME_OK, + FRAME_INCOMPLETE, + FRAME_CLOSE, + FRAME_ERROR + }; #if DUMP_READS || DUMP_WRITES -static void dump_hex(const char* buf, size_t len) { - const char* ptr = buf; - const char* end = ptr + len; - const char* cptr; - char c; - int i; - - while (ptr < end) { - cptr = ptr; - for (i = 0; i < 16 && ptr < end; i++) { - printf("%2.2X ", static_cast(*(ptr++))); - } - for (i = 72 - (i * 4); i > 0; i--) { - printf(" "); - } - for (i = 0; i < 16 && cptr < end; i++) { - c = *(cptr++); - printf("%c", (c > 0x19) ? c : '.'); - } - printf("\n"); - } - printf("\n\n"); -} + static void dump_hex(const char* buf, size_t len) + { + const char* ptr = buf; + const char* end = ptr + len; + const char* cptr; + char c; + int i; + + while (ptr < end) { + cptr = ptr; + for (i = 0; i < 16 && ptr < end; i++) { + printf("%2.2X ", static_cast(*(ptr++))); + } + for (i = 72 - (i * 4); i > 0; i--) { + printf(" "); + } + for (i = 0; i < 16 && cptr < end; i++) { + c = *(cptr++); + printf("%c", (c > 0x19) ? c : '.'); + } + printf("\n"); + } + printf("\n\n"); + } #endif -static void remove_from_beginning(std::vector* buffer, size_t count) { - buffer->erase(buffer->begin(), buffer->begin() + count); -} - -static void dispose_inspector(uv_handle_t* handle) { - InspectorSocket* inspector = inspector_from_stream(handle); - inspector_cb close = - inspector->ws_mode ? inspector->ws_state->close_cb : nullptr; - inspector->buffer.clear(); - delete inspector->ws_state; - inspector->ws_state = nullptr; - if (close) { - close(inspector, 0); - } -} - -static void close_connection(InspectorSocket* inspector) { - uv_handle_t* socket = reinterpret_cast(&inspector->client); - if (!uv_is_closing(socket)) { - uv_read_stop(reinterpret_cast(socket)); - uv_close(socket, dispose_inspector); - } -} - -struct WriteRequest { - WriteRequest(InspectorSocket* inspector, const char* data, size_t size) - : inspector(inspector) - , storage(data, data + size) - , buf(uv_buf_init(&storage[0], storage.size())) {} - - static WriteRequest* from_write_req(uv_write_t* req) { - return node::ContainerOf(&WriteRequest::req, req); - } - - InspectorSocket* const inspector; - std::vector storage; - uv_write_t req; - uv_buf_t buf; -}; - -// Cleanup -static void write_request_cleanup(uv_write_t* req, int status) { - delete WriteRequest::from_write_req(req); -} - -static int write_to_client(InspectorSocket* inspector, - const char* msg, - size_t len, - uv_write_cb write_cb = write_request_cleanup) { + static void remove_from_beginning(std::vector* buffer, size_t count) + { + buffer->erase(buffer->begin(), buffer->begin() + count); + } + + static void dispose_inspector(uv_handle_t* handle) + { + InspectorSocket* inspector = inspector_from_stream(handle); + inspector_cb close = inspector->ws_mode ? inspector->ws_state->close_cb : nullptr; + inspector->buffer.clear(); + delete inspector->ws_state; + inspector->ws_state = nullptr; + if (close) { + close(inspector, 0); + } + } + + static void close_connection(InspectorSocket* inspector) + { + uv_handle_t* socket = reinterpret_cast(&inspector->client); + if (!uv_is_closing(socket)) { + uv_read_stop(reinterpret_cast(socket)); + uv_close(socket, dispose_inspector); + } + } + + struct WriteRequest { + WriteRequest(InspectorSocket* inspector, const char* data, size_t size) + : inspector(inspector) + , storage(data, data + size) + , buf(uv_buf_init(&storage[0], storage.size())) + { + } + + static WriteRequest* from_write_req(uv_write_t* req) + { + return node::ContainerOf(&WriteRequest::req, req); + } + + InspectorSocket* const inspector; + std::vector storage; + uv_write_t req; + uv_buf_t buf; + }; + + // Cleanup + static void write_request_cleanup(uv_write_t* req, int status) + { + delete WriteRequest::from_write_req(req); + } + + static int write_to_client(InspectorSocket* inspector, + const char* msg, + size_t len, + uv_write_cb write_cb = write_request_cleanup) + { #if DUMP_WRITES - printf("%s (%ld bytes):\n", __FUNCTION__, len); - dump_hex(msg, len); + printf("%s (%ld bytes):\n", __FUNCTION__, len); + dump_hex(msg, len); #endif - // Freed in write_request_cleanup - WriteRequest* wr = new WriteRequest(inspector, msg, len); - uv_stream_t* stream = reinterpret_cast(&inspector->client); - return uv_write(&wr->req, stream, &wr->buf, 1, write_cb) < 0; -} - -// Constants for hybi-10 frame format. - -typedef int OpCode; - -const OpCode kOpCodeContinuation = 0x0; -const OpCode kOpCodeText = 0x1; -const OpCode kOpCodeBinary = 0x2; -const OpCode kOpCodeClose = 0x8; -const OpCode kOpCodePing = 0x9; -const OpCode kOpCodePong = 0xA; - -const unsigned char kFinalBit = 0x80; -const unsigned char kReserved1Bit = 0x40; -const unsigned char kReserved2Bit = 0x20; -const unsigned char kReserved3Bit = 0x10; -const unsigned char kOpCodeMask = 0xF; -const unsigned char kMaskBit = 0x80; -const unsigned char kPayloadLengthMask = 0x7F; - -const size_t kMaxSingleBytePayloadLength = 125; -const size_t kTwoBytePayloadLengthField = 126; -const size_t kEightBytePayloadLengthField = 127; -const size_t kMaskingKeyWidthInBytes = 4; - -static std::vector encode_frame_hybi17(const char* message, - size_t data_length) { - std::vector frame; - OpCode op_code = kOpCodeText; - frame.push_back(kFinalBit | op_code); - if (data_length <= kMaxSingleBytePayloadLength) { - frame.push_back(static_cast(data_length)); - } else if (data_length <= 0xFFFF) { - frame.push_back(kTwoBytePayloadLengthField); - frame.push_back((data_length & 0xFF00) >> 8); - frame.push_back(data_length & 0xFF); - } else { - frame.push_back(kEightBytePayloadLengthField); - char extended_payload_length[8]; - size_t remaining = data_length; - // Fill the length into extended_payload_length in the network byte order. - for (int i = 0; i < 8; ++i) { - extended_payload_length[7 - i] = remaining & 0xFF; - remaining >>= 8; - } - frame.insert(frame.end(), extended_payload_length, - extended_payload_length + 8); - ASSERT_EQ(0, remaining); - } - frame.insert(frame.end(), message, message + data_length); - return frame; -} - -static ws_decode_result decode_frame_hybi17(const std::vector& buffer, - bool client_frame, - int* bytes_consumed, - std::vector* output, - bool* compressed) { - *bytes_consumed = 0; - if (buffer.size() < 2) - return FRAME_INCOMPLETE; - - auto it = buffer.begin(); - - unsigned char first_byte = *it++; - unsigned char second_byte = *it++; - - bool final = (first_byte & kFinalBit) != 0; - bool reserved1 = (first_byte & kReserved1Bit) != 0; - bool reserved2 = (first_byte & kReserved2Bit) != 0; - bool reserved3 = (first_byte & kReserved3Bit) != 0; - int op_code = first_byte & kOpCodeMask; - bool masked = (second_byte & kMaskBit) != 0; - *compressed = reserved1; - if (!final || reserved2 || reserved3) - return FRAME_ERROR; // Only compression extension is supported. - - bool closed = false; - switch (op_code) { - case kOpCodeClose: - closed = true; - break; - case kOpCodeText: - break; - case kOpCodeBinary: // We don't support binary frames yet. - case kOpCodeContinuation: // We don't support binary frames yet. - case kOpCodePing: // We don't support binary frames yet. - case kOpCodePong: // We don't support binary frames yet. - default: - return FRAME_ERROR; - } - - // In Hybi-17 spec client MUST mask its frame. - if (client_frame && !masked) { - return FRAME_ERROR; - } - - uint64_t payload_length64 = second_byte & kPayloadLengthMask; - if (payload_length64 > kMaxSingleBytePayloadLength) { - int extended_payload_length_size; - if (payload_length64 == kTwoBytePayloadLengthField) { - extended_payload_length_size = 2; - } else if (payload_length64 == kEightBytePayloadLengthField) { - extended_payload_length_size = 8; - } else { - return FRAME_ERROR; - } - if ((buffer.end() - it) < extended_payload_length_size) - return FRAME_INCOMPLETE; - payload_length64 = 0; - for (int i = 0; i < extended_payload_length_size; ++i) { - payload_length64 <<= 8; - payload_length64 |= static_cast(*it++); - } - } - - static const uint64_t max_payload_length = 0x7FFFFFFFFFFFFFFFull; - static const size_t max_length = SIZE_MAX; - if (payload_length64 > max_payload_length || - payload_length64 > max_length - kMaskingKeyWidthInBytes) { - // WebSocket frame length too large. - return FRAME_ERROR; - } - size_t payload_length = static_cast(payload_length64); - - if (buffer.size() - kMaskingKeyWidthInBytes < payload_length) - return FRAME_INCOMPLETE; - - std::vector::const_iterator masking_key = it; - std::vector::const_iterator payload = it + kMaskingKeyWidthInBytes; - for (size_t i = 0; i < payload_length; ++i) // Unmask the payload. - output->insert(output->end(), - payload[i] ^ masking_key[i % kMaskingKeyWidthInBytes]); - - size_t pos = it + kMaskingKeyWidthInBytes + payload_length - buffer.begin(); - *bytes_consumed = pos; - return closed ? FRAME_CLOSE : FRAME_OK; -} - -static void invoke_read_callback(InspectorSocket* inspector, - int status, const uv_buf_t* buf) { - if (inspector->ws_state->read_cb) { - inspector->ws_state->read_cb( - reinterpret_cast(&inspector->client), status, buf); - } -} - -static void shutdown_complete(InspectorSocket* inspector) { - close_connection(inspector); -} - -static void on_close_frame_written(uv_write_t* req, int status) { - WriteRequest* wr = WriteRequest::from_write_req(req); - InspectorSocket* inspector = wr->inspector; - delete wr; - inspector->ws_state->close_sent = true; - if (inspector->ws_state->received_close) { - shutdown_complete(inspector); - } -} - -static void close_frame_received(InspectorSocket* inspector) { - inspector->ws_state->received_close = true; - if (!inspector->ws_state->close_sent) { - invoke_read_callback(inspector, 0, 0); - write_to_client(inspector, CLOSE_FRAME, sizeof(CLOSE_FRAME), - on_close_frame_written); - } else { - shutdown_complete(inspector); - } -} - -static int parse_ws_frames(InspectorSocket* inspector) { - int bytes_consumed = 0; - std::vector output; - bool compressed = false; - - ws_decode_result r = decode_frame_hybi17(inspector->buffer, - true /* client_frame */, - &bytes_consumed, &output, - &compressed); - // Compressed frame means client is ignoring the headers and misbehaves - if (compressed || r == FRAME_ERROR) { - invoke_read_callback(inspector, UV_EPROTO, nullptr); - close_connection(inspector); - bytes_consumed = 0; - } else if (r == FRAME_CLOSE) { - close_frame_received(inspector); - bytes_consumed = 0; - } else if (r == FRAME_OK && inspector->ws_state->alloc_cb - && inspector->ws_state->read_cb) { - uv_buf_t buffer; - size_t len = output.size(); - inspector->ws_state->alloc_cb( - reinterpret_cast(&inspector->client), - len, &buffer); - CHECK_GE(buffer.len, len); - memcpy(buffer.base, &output[0], len); - invoke_read_callback(inspector, len, &buffer); - } - return bytes_consumed; -} - -static void prepare_buffer(uv_handle_t* stream, size_t len, uv_buf_t* buf) { - *buf = uv_buf_init(new char[len], len); -} - -static void reclaim_uv_buf(InspectorSocket* inspector, const uv_buf_t* buf, - ssize_t read) { - if (read > 0) { - std::vector& buffer = inspector->buffer; - buffer.insert(buffer.end(), buf->base, buf->base + read); - } - delete[] buf->base; -} - -static void websockets_data_cb(uv_stream_t* stream, ssize_t nread, - const uv_buf_t* buf) { - InspectorSocket* inspector = inspector_from_stream(stream); - reclaim_uv_buf(inspector, buf, nread); - if (nread < 0 || nread == UV_EOF) { - inspector->connection_eof = true; - if (!inspector->shutting_down && inspector->ws_state->read_cb) { - inspector->ws_state->read_cb(stream, nread, nullptr); - } - } else { - #if DUMP_READS - printf("%s read %ld bytes\n", __FUNCTION__, nread); - if (nread > 0) { - dump_hex(inspector->buffer.data() + inspector->buffer.size() - nread, - nread); - } - #endif - // 2. Parse. - int processed = 0; - do { - processed = parse_ws_frames(inspector); - // 3. Fix the buffer size & length - if (processed > 0) { - remove_from_beginning(&inspector->buffer, processed); - } - } while (processed > 0 && !inspector->buffer.empty()); - } -} - -int inspector_read_start(InspectorSocket* inspector, - uv_alloc_cb alloc_cb, uv_read_cb read_cb) { - ASSERT(inspector->ws_mode); - ASSERT(!inspector->shutting_down || read_cb == nullptr); - inspector->ws_state->close_sent = false; - inspector->ws_state->alloc_cb = alloc_cb; - inspector->ws_state->read_cb = read_cb; - int err = - uv_read_start(reinterpret_cast(&inspector->client), - prepare_buffer, - websockets_data_cb); - if (err < 0) { - close_connection(inspector); - } - return err; -} - -void inspector_read_stop(InspectorSocket* inspector) { - uv_read_stop(reinterpret_cast(&inspector->client)); - inspector->ws_state->alloc_cb = nullptr; - inspector->ws_state->read_cb = nullptr; -} - -static void generate_accept_string(const std::string& client_key, - char (*buffer)[ACCEPT_KEY_LENGTH]) { - // Magic string from websockets spec. - static const char ws_magic[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; - std::string input(client_key + ws_magic); - char hash[SHA_DIGEST_LENGTH]; - SHA1(reinterpret_cast(&input[0]), input.size(), - reinterpret_cast(hash)); - node::base64_encode(hash, sizeof(hash), *buffer, sizeof(*buffer)); -} - -static int header_value_cb(http_parser* parser, const char* at, size_t length) { - static const char SEC_WEBSOCKET_KEY_HEADER[] = "Sec-WebSocket-Key"; - auto inspector = static_cast(parser->data); - auto state = inspector->http_parsing_state; - state->parsing_value = true; - if (state->current_header.size() == sizeof(SEC_WEBSOCKET_KEY_HEADER) - 1 && - node::StringEqualNoCaseN(state->current_header.data(), - SEC_WEBSOCKET_KEY_HEADER, - sizeof(SEC_WEBSOCKET_KEY_HEADER) - 1)) { - state->ws_key.append(at, length); - } - return 0; -} - -static int header_field_cb(http_parser* parser, const char* at, size_t length) { - auto inspector = static_cast(parser->data); - auto state = inspector->http_parsing_state; - if (state->parsing_value) { - state->parsing_value = false; - state->current_header.clear(); - } - state->current_header.append(at, length); - return 0; -} - -static int path_cb(http_parser* parser, const char* at, size_t length) { - auto inspector = static_cast(parser->data); - auto state = inspector->http_parsing_state; - state->path.append(at, length); - return 0; -} - -static void handshake_complete(InspectorSocket* inspector) { - uv_read_stop(reinterpret_cast(&inspector->client)); - handshake_cb callback = inspector->http_parsing_state->callback; - inspector->ws_state = new ws_state_s(); - inspector->ws_mode = true; - callback(inspector, kInspectorHandshakeUpgraded, - inspector->http_parsing_state->path); -} - -static void cleanup_http_parsing_state(InspectorSocket* inspector) { - delete inspector->http_parsing_state; - inspector->http_parsing_state = nullptr; -} - -static void report_handshake_failure_cb(uv_handle_t* handle) { - dispose_inspector(handle); - InspectorSocket* inspector = inspector_from_stream(handle); - handshake_cb cb = inspector->http_parsing_state->callback; - cleanup_http_parsing_state(inspector); - cb(inspector, kInspectorHandshakeFailed, std::string()); -} - -static void close_and_report_handshake_failure(InspectorSocket* inspector) { - uv_handle_t* socket = reinterpret_cast(&inspector->client); - if (uv_is_closing(socket)) { - report_handshake_failure_cb(socket); - } else { - uv_read_stop(reinterpret_cast(socket)); - uv_close(socket, report_handshake_failure_cb); - } -} - -static void then_close_and_report_failure(uv_write_t* req, int status) { - InspectorSocket* inspector = WriteRequest::from_write_req(req)->inspector; - write_request_cleanup(req, status); - close_and_report_handshake_failure(inspector); -} - -static void handshake_failed(InspectorSocket* inspector) { - const char HANDSHAKE_FAILED_RESPONSE[] = - "HTTP/1.0 400 Bad Request\r\n" - "Content-Type: text/html; charset=UTF-8\r\n\r\n" - "WebSockets request was expected\r\n"; - write_to_client(inspector, HANDSHAKE_FAILED_RESPONSE, - sizeof(HANDSHAKE_FAILED_RESPONSE) - 1, - then_close_and_report_failure); -} - -// init_handshake references message_complete_cb -static void init_handshake(InspectorSocket* inspector); - -static int message_complete_cb(http_parser* parser) { - InspectorSocket* inspector = static_cast(parser->data); - struct http_parsing_state_s* state = inspector->http_parsing_state; - if (parser->method != HTTP_GET) { - handshake_failed(inspector); - } else if (!parser->upgrade) { - if (state->callback(inspector, kInspectorHandshakeHttpGet, state->path)) { - init_handshake(inspector); - } else { - handshake_failed(inspector); - } - } else if (state->ws_key.empty()) { - handshake_failed(inspector); - } else if (state->callback(inspector, kInspectorHandshakeUpgrading, - state->path)) { - char accept_string[ACCEPT_KEY_LENGTH]; - generate_accept_string(state->ws_key, &accept_string); - const char accept_ws_prefix[] = "HTTP/1.1 101 Switching Protocols\r\n" - "Upgrade: websocket\r\n" - "Connection: Upgrade\r\n" - "Sec-WebSocket-Accept: "; - const char accept_ws_suffix[] = "\r\n\r\n"; - std::string reply(accept_ws_prefix, sizeof(accept_ws_prefix) - 1); - reply.append(accept_string, sizeof(accept_string)); - reply.append(accept_ws_suffix, sizeof(accept_ws_suffix) - 1); - if (write_to_client(inspector, &reply[0], reply.size()) >= 0) { - handshake_complete(inspector); - inspector->http_parsing_state->done = true; - } else { - close_and_report_handshake_failure(inspector); - } - } else { - handshake_failed(inspector); - } - return 0; -} - -static void data_received_cb(uv_stream_s* client, ssize_t nread, - const uv_buf_t* buf) { + // Freed in write_request_cleanup + WriteRequest* wr = new WriteRequest(inspector, msg, len); + uv_stream_t* stream = reinterpret_cast(&inspector->client); + return uv_write(&wr->req, stream, &wr->buf, 1, write_cb) < 0; + } + + // Constants for hybi-10 frame format. + + typedef int OpCode; + + const OpCode kOpCodeContinuation = 0x0; + const OpCode kOpCodeText = 0x1; + const OpCode kOpCodeBinary = 0x2; + const OpCode kOpCodeClose = 0x8; + const OpCode kOpCodePing = 0x9; + const OpCode kOpCodePong = 0xA; + + const unsigned char kFinalBit = 0x80; + const unsigned char kReserved1Bit = 0x40; + const unsigned char kReserved2Bit = 0x20; + const unsigned char kReserved3Bit = 0x10; + const unsigned char kOpCodeMask = 0xF; + const unsigned char kMaskBit = 0x80; + const unsigned char kPayloadLengthMask = 0x7F; + + const size_t kMaxSingleBytePayloadLength = 125; + const size_t kTwoBytePayloadLengthField = 126; + const size_t kEightBytePayloadLengthField = 127; + const size_t kMaskingKeyWidthInBytes = 4; + + static std::vector encode_frame_hybi17(const char* message, + size_t data_length) + { + std::vector frame; + OpCode op_code = kOpCodeText; + frame.push_back(kFinalBit | op_code); + if (data_length <= kMaxSingleBytePayloadLength) { + frame.push_back(static_cast(data_length)); + } else if (data_length <= 0xFFFF) { + frame.push_back(kTwoBytePayloadLengthField); + frame.push_back((data_length & 0xFF00) >> 8); + frame.push_back(data_length & 0xFF); + } else { + frame.push_back(kEightBytePayloadLengthField); + char extended_payload_length[8]; + size_t remaining = data_length; + // Fill the length into extended_payload_length in the network byte order. + for (int i = 0; i < 8; ++i) { + extended_payload_length[7 - i] = remaining & 0xFF; + remaining >>= 8; + } + frame.insert(frame.end(), extended_payload_length, + extended_payload_length + 8); + NODE_ASSERT_EQ(0, remaining); + } + frame.insert(frame.end(), message, message + data_length); + return frame; + } + + static ws_decode_result decode_frame_hybi17(const std::vector& buffer, + bool client_frame, + int* bytes_consumed, + std::vector* output, + bool* compressed) + { + *bytes_consumed = 0; + if (buffer.size() < 2) + return FRAME_INCOMPLETE; + + auto it = buffer.begin(); + + unsigned char first_byte = *it++; + unsigned char second_byte = *it++; + + bool final = (first_byte & kFinalBit) != 0; + bool reserved1 = (first_byte & kReserved1Bit) != 0; + bool reserved2 = (first_byte & kReserved2Bit) != 0; + bool reserved3 = (first_byte & kReserved3Bit) != 0; + int op_code = first_byte & kOpCodeMask; + bool masked = (second_byte & kMaskBit) != 0; + *compressed = reserved1; + if (!final || reserved2 || reserved3) + return FRAME_ERROR; // Only compression extension is supported. + + bool closed = false; + switch (op_code) { + case kOpCodeClose: + closed = true; + break; + case kOpCodeText: + break; + case kOpCodeBinary: // We don't support binary frames yet. + case kOpCodeContinuation: // We don't support binary frames yet. + case kOpCodePing: // We don't support binary frames yet. + case kOpCodePong: // We don't support binary frames yet. + default: + return FRAME_ERROR; + } + + // In Hybi-17 spec client MUST mask its frame. + if (client_frame && !masked) { + return FRAME_ERROR; + } + + uint64_t payload_length64 = second_byte & kPayloadLengthMask; + if (payload_length64 > kMaxSingleBytePayloadLength) { + int extended_payload_length_size; + if (payload_length64 == kTwoBytePayloadLengthField) { + extended_payload_length_size = 2; + } else if (payload_length64 == kEightBytePayloadLengthField) { + extended_payload_length_size = 8; + } else { + return FRAME_ERROR; + } + if ((buffer.end() - it) < extended_payload_length_size) + return FRAME_INCOMPLETE; + payload_length64 = 0; + for (int i = 0; i < extended_payload_length_size; ++i) { + payload_length64 <<= 8; + payload_length64 |= static_cast(*it++); + } + } + + static const uint64_t max_payload_length = 0x7FFFFFFFFFFFFFFFull; + static const size_t max_length = SIZE_MAX; + if (payload_length64 > max_payload_length || payload_length64 > max_length - kMaskingKeyWidthInBytes) { + // WebSocket frame length too large. + return FRAME_ERROR; + } + size_t payload_length = static_cast(payload_length64); + + if (buffer.size() - kMaskingKeyWidthInBytes < payload_length) + return FRAME_INCOMPLETE; + + std::vector::const_iterator masking_key = it; + std::vector::const_iterator payload = it + kMaskingKeyWidthInBytes; + for (size_t i = 0; i < payload_length; ++i) // Unmask the payload. + output->insert(output->end(), + payload[i] ^ masking_key[i % kMaskingKeyWidthInBytes]); + + size_t pos = it + kMaskingKeyWidthInBytes + payload_length - buffer.begin(); + *bytes_consumed = pos; + return closed ? FRAME_CLOSE : FRAME_OK; + } + + static void invoke_read_callback(InspectorSocket* inspector, + int status, const uv_buf_t* buf) + { + if (inspector->ws_state->read_cb) { + inspector->ws_state->read_cb( + reinterpret_cast(&inspector->client), status, buf); + } + } + + static void shutdown_complete(InspectorSocket* inspector) + { + close_connection(inspector); + } + + static void on_close_frame_written(uv_write_t* req, int status) + { + WriteRequest* wr = WriteRequest::from_write_req(req); + InspectorSocket* inspector = wr->inspector; + delete wr; + inspector->ws_state->close_sent = true; + if (inspector->ws_state->received_close) { + shutdown_complete(inspector); + } + } + + static void close_frame_received(InspectorSocket* inspector) + { + inspector->ws_state->received_close = true; + if (!inspector->ws_state->close_sent) { + invoke_read_callback(inspector, 0, 0); + write_to_client(inspector, CLOSE_FRAME, sizeof(CLOSE_FRAME), + on_close_frame_written); + } else { + shutdown_complete(inspector); + } + } + + static int parse_ws_frames(InspectorSocket* inspector) + { + int bytes_consumed = 0; + std::vector output; + bool compressed = false; + + ws_decode_result r = decode_frame_hybi17(inspector->buffer, + true /* client_frame */, + &bytes_consumed, &output, + &compressed); + // Compressed frame means client is ignoring the headers and misbehaves + if (compressed || r == FRAME_ERROR) { + invoke_read_callback(inspector, UV_EPROTO, nullptr); + close_connection(inspector); + bytes_consumed = 0; + } else if (r == FRAME_CLOSE) { + close_frame_received(inspector); + bytes_consumed = 0; + } else if (r == FRAME_OK && inspector->ws_state->alloc_cb + && inspector->ws_state->read_cb) { + uv_buf_t buffer; + size_t len = output.size(); + inspector->ws_state->alloc_cb( + reinterpret_cast(&inspector->client), + len, &buffer); + NODE_CHECK_GE(buffer.len, len); + memcpy(buffer.base, &output[0], len); + invoke_read_callback(inspector, len, &buffer); + } + return bytes_consumed; + } + + static void prepare_buffer(uv_handle_t* stream, size_t len, uv_buf_t* buf) + { + *buf = uv_buf_init(new char[len], len); + } + + static void reclaim_uv_buf(InspectorSocket* inspector, const uv_buf_t* buf, + ssize_t read) + { + if (read > 0) { + std::vector& buffer = inspector->buffer; + buffer.insert(buffer.end(), buf->base, buf->base + read); + } + delete[] buf->base; + } + + static void websockets_data_cb(uv_stream_t* stream, ssize_t nread, + const uv_buf_t* buf) + { + InspectorSocket* inspector = inspector_from_stream(stream); + reclaim_uv_buf(inspector, buf, nread); + if (nread < 0 || nread == UV_EOF) { + inspector->connection_eof = true; + if (!inspector->shutting_down && inspector->ws_state->read_cb) { + inspector->ws_state->read_cb(stream, nread, nullptr); + } + } else { #if DUMP_READS - if (nread >= 0) { - printf("%s (%ld bytes)\n", __FUNCTION__, nread); - dump_hex(buf->base, nread); - } else { - printf("[%s:%d] %s\n", __FUNCTION__, __LINE__, uv_err_name(nread)); - } + printf("%s read %ld bytes\n", __FUNCTION__, nread); + if (nread > 0) { + dump_hex(inspector->buffer.data() + inspector->buffer.size() - nread, + nread); + } #endif - InspectorSocket* inspector = inspector_from_stream(client); - reclaim_uv_buf(inspector, buf, nread); - if (nread < 0 || nread == UV_EOF) { - close_and_report_handshake_failure(inspector); - } else { - http_parsing_state_s* state = inspector->http_parsing_state; - http_parser* parser = &state->parser; - http_parser_execute(parser, &state->parser_settings, - inspector->buffer.data(), nread); - remove_from_beginning(&inspector->buffer, nread); - if (parser->http_errno != HPE_OK) { - handshake_failed(inspector); - } - if (inspector->http_parsing_state->done) { - cleanup_http_parsing_state(inspector); - } - } -} - -static void init_handshake(InspectorSocket* inspector) { - http_parsing_state_s* state = inspector->http_parsing_state; - CHECK_NE(state, nullptr); - state->current_header.clear(); - state->ws_key.clear(); - state->path.clear(); - state->done = false; - http_parser_init(&state->parser, HTTP_REQUEST); - state->parser.data = inspector; - http_parser_settings* settings = &state->parser_settings; - http_parser_settings_init(settings); - settings->on_header_field = header_field_cb; - settings->on_header_value = header_value_cb; - settings->on_message_complete = message_complete_cb; - settings->on_url = path_cb; -} - -int inspector_accept(uv_stream_t* server, InspectorSocket* inspector, - handshake_cb callback) { - ASSERT_NE(callback, nullptr); - CHECK_EQ(inspector->http_parsing_state, nullptr); - - inspector->http_parsing_state = new http_parsing_state_s(); - uv_stream_t* client = reinterpret_cast(&inspector->client); - int err = uv_tcp_init(server->loop, &inspector->client); - - if (err == 0) { - err = uv_accept(server, client); - } - if (err == 0) { - init_handshake(inspector); - inspector->http_parsing_state->callback = callback; - err = uv_read_start(client, prepare_buffer, - data_received_cb); - } - if (err != 0) { - uv_close(reinterpret_cast(client), NULL); - } - return err; -} - -void inspector_write(InspectorSocket* inspector, const char* data, - size_t len) { - if (inspector->ws_mode) { - std::vector output = encode_frame_hybi17(data, len); - write_to_client(inspector, &output[0], output.size()); - } else { - write_to_client(inspector, data, len); - } -} - -void inspector_close(InspectorSocket* inspector, - inspector_cb callback) { - // libuv throws assertions when closing stream that's already closed - we - // need to do the same. - ASSERT(!uv_is_closing(reinterpret_cast(&inspector->client))); - ASSERT(!inspector->shutting_down); - inspector->shutting_down = true; - inspector->ws_state->close_cb = callback; - if (inspector->connection_eof) { - close_connection(inspector); - } else { - inspector_read_stop(inspector); - write_to_client(inspector, CLOSE_FRAME, sizeof(CLOSE_FRAME), - on_close_frame_written); - inspector_read_start(inspector, nullptr, nullptr); - } -} - -bool inspector_is_active(const InspectorSocket* inspector) { - const uv_handle_t* client = - reinterpret_cast(&inspector->client); - return !inspector->shutting_down && !uv_is_closing(client); -} - -void InspectorSocket::reinit() { - http_parsing_state = nullptr; - ws_state = nullptr; - buffer.clear(); - ws_mode = false; - shutting_down = false; - connection_eof = false; -} - -} // namespace inspector -} // namespace node + // 2. Parse. + int processed = 0; + do { + processed = parse_ws_frames(inspector); + // 3. Fix the buffer size & length + if (processed > 0) { + remove_from_beginning(&inspector->buffer, processed); + } + } while (processed > 0 && !inspector->buffer.empty()); + } + } + + int inspector_read_start(InspectorSocket* inspector, + uv_alloc_cb alloc_cb, uv_read_cb read_cb) + { + NODE_ASSERT(inspector->ws_mode); + NODE_ASSERT(!inspector->shutting_down || read_cb == nullptr); + inspector->ws_state->close_sent = false; + inspector->ws_state->alloc_cb = alloc_cb; + inspector->ws_state->read_cb = read_cb; + int err = uv_read_start(reinterpret_cast(&inspector->client), + prepare_buffer, + websockets_data_cb); + if (err < 0) { + close_connection(inspector); + } + return err; + } + + void inspector_read_stop(InspectorSocket* inspector) + { + uv_read_stop(reinterpret_cast(&inspector->client)); + inspector->ws_state->alloc_cb = nullptr; + inspector->ws_state->read_cb = nullptr; + } + + static void generate_accept_string(const std::string& client_key, + char (*buffer)[ACCEPT_KEY_LENGTH]) + { + // Magic string from websockets spec. + static const char ws_magic[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + std::string input(client_key + ws_magic); + char hash[SHA_DIGEST_LENGTH]; + SHA1(reinterpret_cast(&input[0]), input.size(), + reinterpret_cast(hash)); + node::base64_encode(hash, sizeof(hash), *buffer, sizeof(*buffer)); + } + + static int header_value_cb(http_parser* parser, const char* at, size_t length) + { + static const char SEC_WEBSOCKET_KEY_HEADER[] = "Sec-WebSocket-Key"; + auto inspector = static_cast(parser->data); + auto state = inspector->http_parsing_state; + state->parsing_value = true; + if (state->current_header.size() == sizeof(SEC_WEBSOCKET_KEY_HEADER) - 1 && node::StringEqualNoCaseN(state->current_header.data(), SEC_WEBSOCKET_KEY_HEADER, sizeof(SEC_WEBSOCKET_KEY_HEADER) - 1)) { + state->ws_key.append(at, length); + } + return 0; + } + + static int header_field_cb(http_parser* parser, const char* at, size_t length) + { + auto inspector = static_cast(parser->data); + auto state = inspector->http_parsing_state; + if (state->parsing_value) { + state->parsing_value = false; + state->current_header.clear(); + } + state->current_header.append(at, length); + return 0; + } + + static int path_cb(http_parser* parser, const char* at, size_t length) + { + auto inspector = static_cast(parser->data); + auto state = inspector->http_parsing_state; + state->path.append(at, length); + return 0; + } + + static void handshake_complete(InspectorSocket* inspector) + { + uv_read_stop(reinterpret_cast(&inspector->client)); + handshake_cb callback = inspector->http_parsing_state->callback; + inspector->ws_state = new ws_state_s(); + inspector->ws_mode = true; + callback(inspector, kInspectorHandshakeUpgraded, + inspector->http_parsing_state->path); + } + + static void cleanup_http_parsing_state(InspectorSocket* inspector) + { + delete inspector->http_parsing_state; + inspector->http_parsing_state = nullptr; + } + + static void report_handshake_failure_cb(uv_handle_t* handle) + { + dispose_inspector(handle); + InspectorSocket* inspector = inspector_from_stream(handle); + handshake_cb cb = inspector->http_parsing_state->callback; + cleanup_http_parsing_state(inspector); + cb(inspector, kInspectorHandshakeFailed, std::string()); + } + + static void close_and_report_handshake_failure(InspectorSocket* inspector) + { + uv_handle_t* socket = reinterpret_cast(&inspector->client); + if (uv_is_closing(socket)) { + report_handshake_failure_cb(socket); + } else { + uv_read_stop(reinterpret_cast(socket)); + uv_close(socket, report_handshake_failure_cb); + } + } + + static void then_close_and_report_failure(uv_write_t* req, int status) + { + InspectorSocket* inspector = WriteRequest::from_write_req(req)->inspector; + write_request_cleanup(req, status); + close_and_report_handshake_failure(inspector); + } + + static void handshake_failed(InspectorSocket* inspector) + { + const char HANDSHAKE_FAILED_RESPONSE[] = "HTTP/1.0 400 Bad Request\r\n" + "Content-Type: text/html; charset=UTF-8\r\n\r\n" + "WebSockets request was expected\r\n"; + write_to_client(inspector, HANDSHAKE_FAILED_RESPONSE, + sizeof(HANDSHAKE_FAILED_RESPONSE) - 1, + then_close_and_report_failure); + } + + // init_handshake references message_complete_cb + static void init_handshake(InspectorSocket* inspector); + + static int message_complete_cb(http_parser* parser) + { + InspectorSocket* inspector = static_cast(parser->data); + struct http_parsing_state_s* state = inspector->http_parsing_state; + if (parser->method != HTTP_GET) { + handshake_failed(inspector); + } else if (!parser->upgrade) { + if (state->callback(inspector, kInspectorHandshakeHttpGet, state->path)) { + init_handshake(inspector); + } else { + handshake_failed(inspector); + } + } else if (state->ws_key.empty()) { + handshake_failed(inspector); + } else if (state->callback(inspector, kInspectorHandshakeUpgrading, + state->path)) { + char accept_string[ACCEPT_KEY_LENGTH]; + generate_accept_string(state->ws_key, &accept_string); + const char accept_ws_prefix[] = "HTTP/1.1 101 Switching Protocols\r\n" + "Upgrade: websocket\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Accept: "; + const char accept_ws_suffix[] = "\r\n\r\n"; + std::string reply(accept_ws_prefix, sizeof(accept_ws_prefix) - 1); + reply.append(accept_string, sizeof(accept_string)); + reply.append(accept_ws_suffix, sizeof(accept_ws_suffix) - 1); + if (write_to_client(inspector, &reply[0], reply.size()) >= 0) { + handshake_complete(inspector); + inspector->http_parsing_state->done = true; + } else { + close_and_report_handshake_failure(inspector); + } + } else { + handshake_failed(inspector); + } + return 0; + } + + static void data_received_cb(uv_stream_s* client, ssize_t nread, + const uv_buf_t* buf) + { +#if DUMP_READS + if (nread >= 0) { + printf("%s (%ld bytes)\n", __FUNCTION__, nread); + dump_hex(buf->base, nread); + } else { + printf("[%s:%d] %s\n", __FUNCTION__, __LINE__, uv_err_name(nread)); + } +#endif + InspectorSocket* inspector = inspector_from_stream(client); + reclaim_uv_buf(inspector, buf, nread); + if (nread < 0 || nread == UV_EOF) { + close_and_report_handshake_failure(inspector); + } else { + http_parsing_state_s* state = inspector->http_parsing_state; + http_parser* parser = &state->parser; + http_parser_execute(parser, &state->parser_settings, + inspector->buffer.data(), nread); + remove_from_beginning(&inspector->buffer, nread); + if (parser->http_errno != HPE_OK) { + handshake_failed(inspector); + } + if (inspector->http_parsing_state->done) { + cleanup_http_parsing_state(inspector); + } + } + } + + static void init_handshake(InspectorSocket* inspector) + { + http_parsing_state_s* state = inspector->http_parsing_state; + NODE_CHECK_NE(state, nullptr); + state->current_header.clear(); + state->ws_key.clear(); + state->path.clear(); + state->done = false; + http_parser_init(&state->parser, HTTP_REQUEST); + state->parser.data = inspector; + http_parser_settings* settings = &state->parser_settings; + http_parser_settings_init(settings); + settings->on_header_field = header_field_cb; + settings->on_header_value = header_value_cb; + settings->on_message_complete = message_complete_cb; + settings->on_url = path_cb; + } + + int inspector_accept(uv_stream_t* server, InspectorSocket* inspector, + handshake_cb callback) + { + NODE_ASSERT_NE(callback, nullptr); + NODE_CHECK_EQ(inspector->http_parsing_state, nullptr); + + inspector->http_parsing_state = new http_parsing_state_s(); + uv_stream_t* client = reinterpret_cast(&inspector->client); + int err = uv_tcp_init(server->loop, &inspector->client); + + if (err == 0) { + err = uv_accept(server, client); + } + if (err == 0) { + init_handshake(inspector); + inspector->http_parsing_state->callback = callback; + err = uv_read_start(client, prepare_buffer, + data_received_cb); + } + if (err != 0) { + uv_close(reinterpret_cast(client), NULL); + } + return err; + } + + void inspector_write(InspectorSocket* inspector, const char* data, + size_t len) + { + if (inspector->ws_mode) { + std::vector output = encode_frame_hybi17(data, len); + write_to_client(inspector, &output[0], output.size()); + } else { + write_to_client(inspector, data, len); + } + } + + void inspector_close(InspectorSocket* inspector, + inspector_cb callback) + { + // libuv throws NODE_ASSERTions when closing stream that's already closed - we + // need to do the same. + NODE_ASSERT(!uv_is_closing(reinterpret_cast(&inspector->client))); + NODE_ASSERT(!inspector->shutting_down); + inspector->shutting_down = true; + inspector->ws_state->close_cb = callback; + if (inspector->connection_eof) { + close_connection(inspector); + } else { + inspector_read_stop(inspector); + write_to_client(inspector, CLOSE_FRAME, sizeof(CLOSE_FRAME), + on_close_frame_written); + inspector_read_start(inspector, nullptr, nullptr); + } + } + + bool inspector_is_active(const InspectorSocket* inspector) + { + const uv_handle_t* client = reinterpret_cast(&inspector->client); + return !inspector->shutting_down && !uv_is_closing(client); + } + + void InspectorSocket::reinit() + { + http_parsing_state = nullptr; + ws_state = nullptr; + buffer.clear(); + ws_mode = false; + shutting_down = false; + connection_eof = false; + } + +} // namespace inspector +} // namespace node diff --git a/node/src/inspector_socket.h b/node/src/inspector_socket.h index 558d87bcb7..8001f95240 100644 --- a/node/src/inspector_socket.h +++ b/node/src/inspector_socket.h @@ -12,89 +12,99 @@ namespace node { namespace inspector { -enum inspector_handshake_event { - kInspectorHandshakeUpgrading, - kInspectorHandshakeUpgraded, - kInspectorHandshakeHttpGet, - kInspectorHandshakeFailed -}; - -class InspectorSocket; - -typedef void (*inspector_cb)(InspectorSocket*, int); -// Notifies as handshake is progressing. Returning false as a response to -// kInspectorHandshakeUpgrading or kInspectorHandshakeHttpGet event will abort -// the connection. inspector_write can be used from the callback. -typedef bool (*handshake_cb)(InspectorSocket*, - enum inspector_handshake_event state, - const std::string& path); - -struct http_parsing_state_s { - http_parser parser; - http_parser_settings parser_settings; - handshake_cb callback; - bool done; - bool parsing_value; - std::string ws_key; - std::string path; - std::string current_header; -}; - -struct ws_state_s { - uv_alloc_cb alloc_cb; - uv_read_cb read_cb; - inspector_cb close_cb; - bool close_sent; - bool received_close; -}; - -class InspectorSocket { - public: - InspectorSocket() : data(nullptr), http_parsing_state(nullptr), - ws_state(nullptr), buffer(0), ws_mode(false), - shutting_down(false), connection_eof(false) { } - void reinit(); - void* data; - struct http_parsing_state_s* http_parsing_state; - struct ws_state_s* ws_state; - std::vector buffer; - uv_tcp_t client; - bool ws_mode; - bool shutting_down; - bool connection_eof; - private: - DISALLOW_COPY_AND_ASSIGN(InspectorSocket); -}; - -int inspector_accept(uv_stream_t* server, InspectorSocket* inspector, - handshake_cb callback); - -void inspector_close(InspectorSocket* inspector, - inspector_cb callback); - -// Callbacks will receive stream handles. Use inspector_from_stream to get -// InspectorSocket* from the stream handle. -int inspector_read_start(InspectorSocket* inspector, uv_alloc_cb, - uv_read_cb); -void inspector_read_stop(InspectorSocket* inspector); -void inspector_write(InspectorSocket* inspector, - const char* data, size_t len); -bool inspector_is_active(const InspectorSocket* inspector); - -inline InspectorSocket* inspector_from_stream(uv_tcp_t* stream) { - return node::ContainerOf(&InspectorSocket::client, stream); -} - -inline InspectorSocket* inspector_from_stream(uv_stream_t* stream) { - return inspector_from_stream(reinterpret_cast(stream)); -} - -inline InspectorSocket* inspector_from_stream(uv_handle_t* stream) { - return inspector_from_stream(reinterpret_cast(stream)); -} - -} // namespace inspector -} // namespace node - - -#endif // SRC_INSPECTOR_SOCKET_H_ + enum inspector_handshake_event { + kInspectorHandshakeUpgrading, + kInspectorHandshakeUpgraded, + kInspectorHandshakeHttpGet, + kInspectorHandshakeFailed + }; + + class InspectorSocket; + + typedef void (*inspector_cb)(InspectorSocket*, int); + // Notifies as handshake is progressing. Returning false as a response to + // kInspectorHandshakeUpgrading or kInspectorHandshakeHttpGet event will abort + // the connection. inspector_write can be used from the callback. + typedef bool (*handshake_cb)(InspectorSocket*, + enum inspector_handshake_event state, + const std::string& path); + + struct http_parsing_state_s { + http_parser parser; + http_parser_settings parser_settings; + handshake_cb callback; + bool done; + bool parsing_value; + std::string ws_key; + std::string path; + std::string current_header; + }; + + struct ws_state_s { + uv_alloc_cb alloc_cb; + uv_read_cb read_cb; + inspector_cb close_cb; + bool close_sent; + bool received_close; + }; + + class InspectorSocket { + public: + InspectorSocket() + : data(nullptr) + , http_parsing_state(nullptr) + , ws_state(nullptr) + , buffer(0) + , ws_mode(false) + , shutting_down(false) + , connection_eof(false) + { + } + void reinit(); + void* data; + struct http_parsing_state_s* http_parsing_state; + struct ws_state_s* ws_state; + std::vector buffer; + uv_tcp_t client; + bool ws_mode; + bool shutting_down; + bool connection_eof; + + private: + DISALLOW_COPY_AND_ASSIGN(InspectorSocket); + }; + + int inspector_accept(uv_stream_t* server, InspectorSocket* inspector, + handshake_cb callback); + + void inspector_close(InspectorSocket* inspector, + inspector_cb callback); + + // Callbacks will receive stream handles. Use inspector_from_stream to get + // InspectorSocket* from the stream handle. + int inspector_read_start(InspectorSocket* inspector, uv_alloc_cb, + uv_read_cb); + void inspector_read_stop(InspectorSocket* inspector); + void inspector_write(InspectorSocket* inspector, + const char* data, size_t len); + bool inspector_is_active(const InspectorSocket* inspector); + + inline InspectorSocket* inspector_from_stream(uv_tcp_t* stream) + { + return node::ContainerOf(&InspectorSocket::client, stream); + } + + inline InspectorSocket* inspector_from_stream(uv_stream_t* stream) + { + return inspector_from_stream(reinterpret_cast(stream)); + } + + inline InspectorSocket* inspector_from_stream(uv_handle_t* stream) + { + return inspector_from_stream(reinterpret_cast(stream)); + } + +} // namespace inspector +} // namespace node + +#endif // SRC_INSPECTOR_SOCKET_H_ diff --git a/node/src/js_stream.cc b/node/src/js_stream.cc index df7dec73f3..4aa67076db 100644 --- a/node/src/js_stream.cc +++ b/node/src/js_stream.cc @@ -20,221 +20,220 @@ using v8::Local; using v8::Object; using v8::Value; - JSStream::JSStream(Environment* env, Local obj, AsyncWrap* parent) - : AsyncWrap(env, obj, AsyncWrap::PROVIDER_JSSTREAM, parent), - StreamBase(env) { - node::Wrap(obj, this); - MakeWeak(this); + : AsyncWrap(env, obj, AsyncWrap::PROVIDER_JSSTREAM, parent) + , StreamBase(env) +{ + node::Wrap(obj, this); + MakeWeak(this); } - -JSStream::~JSStream() { +JSStream::~JSStream() +{ } - -void* JSStream::Cast() { - return static_cast(this); +void* JSStream::Cast() +{ + return static_cast(this); } - -AsyncWrap* JSStream::GetAsyncWrap() { - return static_cast(this); +AsyncWrap* JSStream::GetAsyncWrap() +{ + return static_cast(this); } - -bool JSStream::IsAlive() { - v8::Local fn = object()->Get(env()->isalive_string()); - if (!fn->IsFunction()) - return false; - return MakeCallback(fn.As(), 0, nullptr)->IsTrue(); +bool JSStream::IsAlive() +{ + v8::Local fn = object()->Get(env()->isalive_string()); + if (!fn->IsFunction()) + return false; + return MakeCallback(fn.As(), 0, nullptr)->IsTrue(); } - -bool JSStream::IsClosing() { - return MakeCallback(env()->isclosing_string(), 0, nullptr)->IsTrue(); +bool JSStream::IsClosing() +{ + return MakeCallback(env()->isclosing_string(), 0, nullptr)->IsTrue(); } - -int JSStream::ReadStart() { - return MakeCallback(env()->onreadstart_string(), 0, nullptr)->Int32Value(); +int JSStream::ReadStart() +{ + return MakeCallback(env()->onreadstart_string(), 0, nullptr)->Int32Value(); } - -int JSStream::ReadStop() { - return MakeCallback(env()->onreadstop_string(), 0, nullptr)->Int32Value(); +int JSStream::ReadStop() +{ + return MakeCallback(env()->onreadstop_string(), 0, nullptr)->Int32Value(); } +int JSStream::DoShutdown(ShutdownWrap* req_wrap) +{ + HandleScope scope(env()->isolate()); -int JSStream::DoShutdown(ShutdownWrap* req_wrap) { - HandleScope scope(env()->isolate()); + Local argv[] = { + req_wrap->object() + }; - Local argv[] = { - req_wrap->object() - }; + req_wrap->Dispatched(); + Local res = MakeCallback(env()->onshutdown_string(), arraysize(argv), argv); - req_wrap->Dispatched(); - Local res = - MakeCallback(env()->onshutdown_string(), arraysize(argv), argv); - - return res->Int32Value(); + return res->Int32Value(); } - int JSStream::DoWrite(WriteWrap* w, - uv_buf_t* bufs, - size_t count, - uv_stream_t* send_handle) { - CHECK_EQ(send_handle, nullptr); - - HandleScope scope(env()->isolate()); - - Local bufs_arr = Array::New(env()->isolate(), count); - Local buf; - for (size_t i = 0; i < count; i++) { - buf = Buffer::Copy(env(), bufs[i].base, bufs[i].len).ToLocalChecked(); - bufs_arr->Set(i, buf); - } - - Local argv[] = { - w->object(), - bufs_arr - }; - - w->Dispatched(); - Local res = - MakeCallback(env()->onwrite_string(), arraysize(argv), argv); - - return res->Int32Value(); -} - - -void JSStream::New(const FunctionCallbackInfo& args) { - // This constructor should not be exposed to public javascript. - // Therefore we assert that we are not trying to call this as a - // normal function. - CHECK(args.IsConstructCall()); - Environment* env = Environment::GetCurrent(args); - JSStream* wrap = NULL; - - if (args.Length() == 0) { - wrap = new JSStream(env, args.This(), nullptr); - } else if (args[0]->IsExternal()) { - void* ptr = args[0].As()->Value(); - wrap = new JSStream(env, args.This(), static_cast(ptr)); - } else { - UNREACHABLE(); - } - CHECK(wrap); -} - - -static void FreeCallback(char* data, void* hint) { - // Intentional no-op -} - + uv_buf_t* bufs, + size_t count, + uv_stream_t* send_handle) +{ + NODE_CHECK_EQ(send_handle, nullptr); + + HandleScope scope(env()->isolate()); + + Local bufs_arr = Array::New(env()->isolate(), count); + Local buf; + for (size_t i = 0; i < count; i++) { + buf = Buffer::Copy(env(), bufs[i].base, bufs[i].len).ToLocalChecked(); + bufs_arr->Set(i, buf); + } + + Local argv[] = { + w->object(), + bufs_arr + }; + + w->Dispatched(); + Local res = MakeCallback(env()->onwrite_string(), arraysize(argv), argv); + + return res->Int32Value(); +} + +void JSStream::New(const FunctionCallbackInfo& args) +{ + // This constructor should not be exposed to public javascript. + // Therefore we NODE_ASSERT that we are not trying to call this as a + // normal function. + NODE_CHECK(args.IsConstructCall()); + Environment* env = Environment::GetCurrent(args); + JSStream* wrap = NULL; + + if (args.Length() == 0) { + wrap = new JSStream(env, args.This(), nullptr); + } else if (args[0]->IsExternal()) { + void* ptr = args[0].As()->Value(); + wrap = new JSStream(env, args.This(), static_cast(ptr)); + } else { + UNREACHABLE(); + } + NODE_CHECK(wrap); +} + +static void FreeCallback(char* data, void* hint) +{ + // Intentional no-op +} + +void JSStream::DoAlloc(const FunctionCallbackInfo& args) +{ + JSStream* wrap; + ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); -void JSStream::DoAlloc(const FunctionCallbackInfo& args) { - JSStream* wrap; - ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); - - uv_buf_t buf; - wrap->OnAlloc(args[0]->Int32Value(), &buf); - Local vbuf = Buffer::New( - wrap->env(), - buf.base, - buf.len, - FreeCallback, - nullptr).ToLocalChecked(); - return args.GetReturnValue().Set(vbuf); + uv_buf_t buf; + wrap->OnAlloc(args[0]->Int32Value(), &buf); + Local vbuf = Buffer::New( + wrap->env(), + buf.base, + buf.len, + FreeCallback, + nullptr) + .ToLocalChecked(); + return args.GetReturnValue().Set(vbuf); } +void JSStream::DoRead(const FunctionCallbackInfo& args) +{ + JSStream* wrap; + ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); -void JSStream::DoRead(const FunctionCallbackInfo& args) { - JSStream* wrap; - ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); - - CHECK(Buffer::HasInstance(args[1])); - uv_buf_t buf = uv_buf_init(Buffer::Data(args[1]), Buffer::Length(args[1])); - wrap->OnRead(args[0]->Int32Value(), &buf); + NODE_CHECK(Buffer::HasInstance(args[1])); + uv_buf_t buf = uv_buf_init(Buffer::Data(args[1]), Buffer::Length(args[1])); + wrap->OnRead(args[0]->Int32Value(), &buf); } +void JSStream::DoAfterWrite(const FunctionCallbackInfo& args) +{ + JSStream* wrap; + NODE_CHECK(args[0]->IsObject()); + WriteWrap* w; + ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); + ASSIGN_OR_RETURN_UNWRAP(&w, args[0].As()); -void JSStream::DoAfterWrite(const FunctionCallbackInfo& args) { - JSStream* wrap; - CHECK(args[0]->IsObject()); - WriteWrap* w; - ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); - ASSIGN_OR_RETURN_UNWRAP(&w, args[0].As()); - - wrap->OnAfterWrite(w); + wrap->OnAfterWrite(w); } - template -void JSStream::Finish(const FunctionCallbackInfo& args) { - Wrap* w; - CHECK(args[0]->IsObject()); - ASSIGN_OR_RETURN_UNWRAP(&w, args[0].As()); +void JSStream::Finish(const FunctionCallbackInfo& args) +{ + Wrap* w; + NODE_CHECK(args[0]->IsObject()); + ASSIGN_OR_RETURN_UNWRAP(&w, args[0].As()); - w->Done(args[1]->Int32Value()); + w->Done(args[1]->Int32Value()); } +void JSStream::ReadBuffer(const FunctionCallbackInfo& args) +{ + JSStream* wrap; + ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); -void JSStream::ReadBuffer(const FunctionCallbackInfo& args) { - JSStream* wrap; - ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); + NODE_CHECK(Buffer::HasInstance(args[0])); + char* data = Buffer::Data(args[0]); + int len = Buffer::Length(args[0]); - CHECK(Buffer::HasInstance(args[0])); - char* data = Buffer::Data(args[0]); - int len = Buffer::Length(args[0]); + do { + uv_buf_t buf; + ssize_t avail = len; + wrap->OnAlloc(len, &buf); + if (static_cast(buf.len) < avail) + avail = buf.len; - do { - uv_buf_t buf; - ssize_t avail = len; - wrap->OnAlloc(len, &buf); - if (static_cast(buf.len) < avail) - avail = buf.len; - - memcpy(buf.base, data, avail); - data += avail; - len -= avail; - wrap->OnRead(avail, &buf); - } while (len != 0); + memcpy(buf.base, data, avail); + data += avail; + len -= avail; + wrap->OnRead(avail, &buf); + } while (len != 0); } +void JSStream::EmitEOF(const FunctionCallbackInfo& args) +{ + JSStream* wrap; + ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); -void JSStream::EmitEOF(const FunctionCallbackInfo& args) { - JSStream* wrap; - ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); - - wrap->OnRead(UV_EOF, nullptr); + wrap->OnRead(UV_EOF, nullptr); } - void JSStream::Initialize(Local target, - Local unused, - Local context) { - Environment* env = Environment::GetCurrent(context); - - Local t = env->NewFunctionTemplate(New); - t->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "JSStream")); - t->InstanceTemplate()->SetInternalFieldCount(1); - - env->SetProtoMethod(t, "doAlloc", DoAlloc); - env->SetProtoMethod(t, "doRead", DoRead); - env->SetProtoMethod(t, "doAfterWrite", DoAfterWrite); - env->SetProtoMethod(t, "finishWrite", Finish); - env->SetProtoMethod(t, "finishShutdown", Finish); - env->SetProtoMethod(t, "readBuffer", ReadBuffer); - env->SetProtoMethod(t, "emitEOF", EmitEOF); - - StreamBase::AddMethods(env, t, StreamBase::kFlagHasWritev); - target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "JSStream"), - t->GetFunction()); - env->set_jsstream_constructor_template(t); -} - -} // namespace node + Local unused, + Local context) +{ + Environment* env = Environment::GetCurrent(context); + + Local t = env->NewFunctionTemplate(New); + t->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "JSStream")); + t->InstanceTemplate()->SetInternalFieldCount(1); + + env->SetProtoMethod(t, "doAlloc", DoAlloc); + env->SetProtoMethod(t, "doRead", DoRead); + env->SetProtoMethod(t, "doAfterWrite", DoAfterWrite); + env->SetProtoMethod(t, "finishWrite", Finish); + env->SetProtoMethod(t, "finishShutdown", Finish); + env->SetProtoMethod(t, "readBuffer", ReadBuffer); + env->SetProtoMethod(t, "emitEOF", EmitEOF); + + StreamBase::AddMethods(env, t, StreamBase::kFlagHasWritev); + target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "JSStream"), + t->GetFunction()); + env->set_jsstream_constructor_template(t); +} + +} // namespace node NODE_MODULE_CONTEXT_AWARE_BUILTIN(js_stream, node::JSStream::Initialize) diff --git a/node/src/js_stream.h b/node/src/js_stream.h index 5a1244bc46..da02712091 100644 --- a/node/src/js_stream.h +++ b/node/src/js_stream.h @@ -11,45 +11,45 @@ namespace node { class JSStream : public AsyncWrap, public StreamBase { - public: - static void Initialize(v8::Local target, - v8::Local unused, - v8::Local context); +public: + static void Initialize(v8::Local target, + v8::Local unused, + v8::Local context); - ~JSStream(); + ~JSStream(); - void* Cast() override; - bool IsAlive() override; - bool IsClosing() override; - int ReadStart() override; - int ReadStop() override; + void* Cast() override; + bool IsAlive() override; + bool IsClosing() override; + int ReadStart() override; + int ReadStop() override; - int DoShutdown(ShutdownWrap* req_wrap) override; - int DoWrite(WriteWrap* w, - uv_buf_t* bufs, - size_t count, - uv_stream_t* send_handle) override; + int DoShutdown(ShutdownWrap* req_wrap) override; + int DoWrite(WriteWrap* w, + uv_buf_t* bufs, + size_t count, + uv_stream_t* send_handle) override; - size_t self_size() const override { return sizeof(*this); } + size_t self_size() const override { return sizeof(*this); } - protected: - JSStream(Environment* env, v8::Local obj, AsyncWrap* parent); +protected: + JSStream(Environment* env, v8::Local obj, AsyncWrap* parent); - AsyncWrap* GetAsyncWrap() override; + AsyncWrap* GetAsyncWrap() override; - static void New(const v8::FunctionCallbackInfo& args); - static void DoAlloc(const v8::FunctionCallbackInfo& args); - static void DoRead(const v8::FunctionCallbackInfo& args); - static void DoAfterWrite(const v8::FunctionCallbackInfo& args); - static void ReadBuffer(const v8::FunctionCallbackInfo& args); - static void EmitEOF(const v8::FunctionCallbackInfo& args); + static void New(const v8::FunctionCallbackInfo& args); + static void DoAlloc(const v8::FunctionCallbackInfo& args); + static void DoRead(const v8::FunctionCallbackInfo& args); + static void DoAfterWrite(const v8::FunctionCallbackInfo& args); + static void ReadBuffer(const v8::FunctionCallbackInfo& args); + static void EmitEOF(const v8::FunctionCallbackInfo& args); - template - static void Finish(const v8::FunctionCallbackInfo& args); + template + static void Finish(const v8::FunctionCallbackInfo& args); }; -} // namespace node +} // namespace node -#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS +#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS -#endif // SRC_JS_STREAM_H_ +#endif // SRC_JS_STREAM_H_ diff --git a/node/src/node.h b/node/src/node.h index e9c355f91d..eaedec02c1 100644 --- a/node/src/node.h +++ b/node/src/node.h @@ -2,31 +2,31 @@ #define SRC_NODE_H_ #ifdef _WIN32 -# ifndef BUILDING_NODE_EXTENSION -# define NODE_EXTERN __declspec(dllexport) -# else -# define NODE_EXTERN __declspec(dllimport) -# endif +#ifndef BUILDING_NODE_EXTENSION +#define NODE_EXTERN __declspec(dllexport) #else -# define NODE_EXTERN /* nothing */ +#define NODE_EXTERN __declspec(dllimport) +#endif +#else +#define NODE_EXTERN /* nothing */ #endif #ifdef BUILDING_NODE_EXTENSION -# undef BUILDING_V8_SHARED -# undef BUILDING_UV_SHARED -# define USING_V8_SHARED 1 -# define USING_UV_SHARED 1 +#undef BUILDING_V8_SHARED +#undef BUILDING_UV_SHARED +#define USING_V8_SHARED 1 +#define USING_UV_SHARED 1 #endif // This should be defined in make system. // See issue https://github.com/joyent/node/issues/1236 #if defined(__MINGW32__) || defined(_MSC_VER) #ifndef _WIN32_WINNT -# define _WIN32_WINNT 0x0501 +#define _WIN32_WINNT 0x0600 #endif #ifndef NOMINMAX -# define NOMINMAX +#define NOMINMAX #endif #endif @@ -36,39 +36,37 @@ #endif #ifdef _WIN32 -# define SIGKILL 9 +#define SIGKILL 9 #endif -#include "v8.h" // NOLINT(build/include_order) -#include "node_version.h" // NODE_MODULE_VERSION +#include "v8.h" // NOLINT(build/include_order) +#include "node_version.h" // NODE_MODULE_VERSION -#define NODE_MAKE_VERSION(major, minor, patch) \ - ((major) * 0x1000 + (minor) * 0x100 + (patch)) +#define NODE_MAKE_VERSION(major, minor, patch) \ + ((major)*0x1000 + (minor)*0x100 + (patch)) #ifdef __clang__ -# define NODE_CLANG_AT_LEAST(major, minor, patch) \ - (NODE_MAKE_VERSION(major, minor, patch) <= \ - NODE_MAKE_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__)) +#define NODE_CLANG_AT_LEAST(major, minor, patch) \ + (NODE_MAKE_VERSION(major, minor, patch) <= NODE_MAKE_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__)) #else -# define NODE_CLANG_AT_LEAST(major, minor, patch) (0) +#define NODE_CLANG_AT_LEAST(major, minor, patch) (0) #endif #ifdef __GNUC__ -# define NODE_GNUC_AT_LEAST(major, minor, patch) \ - (NODE_MAKE_VERSION(major, minor, patch) <= \ - NODE_MAKE_VERSION(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)) +#define NODE_GNUC_AT_LEAST(major, minor, patch) \ + (NODE_MAKE_VERSION(major, minor, patch) <= NODE_MAKE_VERSION(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)) #else -# define NODE_GNUC_AT_LEAST(major, minor, patch) (0) +#define NODE_GNUC_AT_LEAST(major, minor, patch) (0) #endif #if NODE_CLANG_AT_LEAST(2, 9, 0) || NODE_GNUC_AT_LEAST(4, 5, 0) -# define NODE_DEPRECATED(message, declarator) \ +#define NODE_DEPRECATED(message, declarator) \ __attribute__((deprecated(message))) declarator #elif defined(_MSC_VER) -# define NODE_DEPRECATED(message, declarator) \ +#define NODE_DEPRECATED(message, declarator) \ __declspec(deprecated) declarator #else -# define NODE_DEPRECATED(message, declarator) \ +#define NODE_DEPRECATED(message, declarator) \ declarator #endif @@ -80,44 +78,46 @@ struct uv_loop_s; namespace node { NODE_EXTERN v8::Local ErrnoException(v8::Isolate* isolate, - int errorno, - const char* syscall = NULL, - const char* message = NULL, - const char* path = NULL); + int errorno, + const char* syscall = NULL, + const char* message = NULL, + const char* path = NULL); NODE_EXTERN v8::Local UVException(v8::Isolate* isolate, - int errorno, - const char* syscall = NULL, - const char* message = NULL, - const char* path = NULL); + int errorno, + const char* syscall = NULL, + const char* message = NULL, + const char* path = NULL); NODE_EXTERN v8::Local UVException(v8::Isolate* isolate, - int errorno, - const char* syscall, - const char* message, - const char* path, - const char* dest); - -NODE_DEPRECATED("Use ErrnoException(isolate, ...)", - inline v8::Local ErrnoException( - int errorno, - const char* syscall = NULL, - const char* message = NULL, - const char* path = NULL) { - return ErrnoException(v8::Isolate::GetCurrent(), - errorno, - syscall, - message, - path); -}) + int errorno, + const char* syscall, + const char* message, + const char* path, + const char* dest); + +NODE_DEPRECATED( + "Use ErrnoException(isolate, ...)", + inline v8::Local ErrnoException( + int errorno, + const char* syscall = NULL, + const char* message = NULL, + const char* path = NULL) { + return ErrnoException(v8::Isolate::GetCurrent(), + errorno, + syscall, + message, + path); + }) inline v8::Local UVException(int errorno, - const char* syscall = NULL, - const char* message = NULL, - const char* path = NULL) { - return UVException(v8::Isolate::GetCurrent(), - errorno, - syscall, - message, - path); + const char* syscall = NULL, + const char* message = NULL, + const char* path = NULL) +{ + return UVException(v8::Isolate::GetCurrent(), + errorno, + syscall, + message, + path); } /* @@ -150,7 +150,7 @@ NODE_EXTERN v8::Local MakeCallback( int argc, v8::Local* argv); -} // namespace node +} // namespace node #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS #include "node_internals.h" @@ -168,13 +168,12 @@ NODE_EXTERN v8::Local MakeCallback( // TODO(tjfontaine) consider changing the usage of ssize_t to ptrdiff_t #if !defined(_SSIZE_T_) && !defined(_SSIZE_T_DEFINED) typedef intptr_t ssize_t; -# define _SSIZE_T_ -# define _SSIZE_T_DEFINED +#define _SSIZE_T_ +#define _SSIZE_T_DEFINED #endif -#else // !_WIN32 -# include // size_t, ssize_t -#endif // _WIN32 - +#else // !_WIN32 +#include // size_t, ssize_t +#endif // _WIN32 namespace node { @@ -185,19 +184,19 @@ NODE_EXTERN extern bool force_fips_crypto; #endif NODE_EXTERN void Init(int* argc, - const char** argv, - int* exec_argc, - const char*** exec_argv); + const char** argv, + int* exec_argc, + const char*** exec_argv); class Environment; NODE_EXTERN Environment* CreateEnvironment(v8::Isolate* isolate, - struct uv_loop_s* loop, - v8::Local context, - int argc, - const char* const* argv, - int exec_argc, - const char* const* exec_argv); + struct uv_loop_s* loop, + v8::Local context, + int argc, + const char* const* argv, + int exec_argc, + const char* const* exec_argv); NODE_EXTERN void LoadEnvironment(Environment* env); NODE_EXTERN void FreeEnvironment(Environment* env); @@ -205,11 +204,11 @@ NODE_EXTERN void FreeEnvironment(Environment* env); // CreateEnvironment() + LoadEnvironment() from above. // `uv_default_loop()` will be passed as `loop`. NODE_EXTERN Environment* CreateEnvironment(v8::Isolate* isolate, - v8::Local context, - int argc, - const char* const* argv, - int exec_argc, - const char* const* exec_argv); + v8::Local context, + int argc, + const char* const* argv, + int exec_argc, + const char* const* exec_argv); // ִjs NODE_EXTERN v8::Local ExecuteString(Environment* env, v8::Local source, v8::Local filename); @@ -218,172 +217,184 @@ NODE_EXTERN void EmitBeforeExit(Environment* env); NODE_EXTERN int EmitExit(Environment* env); NODE_EXTERN void RunAtExit(Environment* env); -NODE_EXTERN void AddLiveSet(intptr_t obj); +NODE_EXTERN void AddLiveSet(intptr_t obj); NODE_EXTERN void RemoveLiveSet(intptr_t obj); NODE_EXTERN bool IsLiveObj(intptr_t obj); +NODE_EXTERN void CleanNodeEnv(void* env); + /* Converts a unixtime to V8 Date */ -#define NODE_UNIXTIME_V8(t) v8::Date::New(v8::Isolate::GetCurrent(), \ +#define NODE_UNIXTIME_V8(t) v8::Date::New(v8::Isolate::GetCurrent(), \ 1000 * static_cast(t)) -#define NODE_V8_UNIXTIME(v) (static_cast((v)->NumberValue())/1000.0); +#define NODE_V8_UNIXTIME(v) (static_cast((v)->NumberValue()) / 1000.0); // Used to be a macro, hence the uppercase name. -#define NODE_DEFINE_CONSTANT(target, constant) \ - do { \ - v8::Isolate* isolate = target->GetIsolate(); \ - v8::Local context = isolate->GetCurrentContext(); \ - v8::Local constant_name = \ - v8::String::NewFromUtf8(isolate, #constant); \ - v8::Local constant_value = \ - v8::Number::New(isolate, static_cast(constant)); \ - v8::PropertyAttribute constant_attributes = \ - static_cast(v8::ReadOnly | v8::DontDelete); \ - (target)->DefineOwnProperty(context, \ - constant_name, \ - constant_value, \ - constant_attributes).FromJust(); \ - } \ - while (0) +#define NODE_DEFINE_CONSTANT(target, constant) \ + do { \ + v8::Isolate* isolate = target->GetIsolate(); \ + v8::Local context = isolate->GetCurrentContext(); \ + v8::Local constant_name = v8::String::NewFromUtf8(isolate, #constant); \ + v8::Local constant_value = v8::Number::New(isolate, static_cast(constant)); \ + v8::PropertyAttribute constant_attributes = static_cast(v8::ReadOnly | v8::DontDelete); \ + (target)->DefineOwnProperty(context, \ + constant_name, \ + constant_value, \ + constant_attributes) \ + .FromJust(); \ + } while (0) // Used to be a macro, hence the uppercase name. inline void NODE_SET_METHOD(v8::Local recv, - const char* name, - v8::FunctionCallback callback) { - v8::Isolate* isolate = v8::Isolate::GetCurrent(); - v8::HandleScope handle_scope(isolate); - v8::Local t = v8::FunctionTemplate::New(isolate, - callback); - v8::Local fn_name = v8::String::NewFromUtf8(isolate, name); - t->SetClassName(fn_name); - recv->Set(fn_name, t); + const char* name, + v8::FunctionCallback callback) +{ + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + v8::HandleScope handle_scope(isolate); + v8::Local t = v8::FunctionTemplate::New(isolate, + callback); + v8::Local fn_name = v8::String::NewFromUtf8(isolate, name); + t->SetClassName(fn_name); + recv->Set(fn_name, t); } // Used to be a macro, hence the uppercase name. inline void NODE_SET_METHOD(v8::Local recv, - const char* name, - v8::FunctionCallback callback) { - v8::Isolate* isolate = v8::Isolate::GetCurrent(); - v8::HandleScope handle_scope(isolate); - v8::Local t = v8::FunctionTemplate::New(isolate, - callback); - v8::Local fn = t->GetFunction(); - v8::Local fn_name = v8::String::NewFromUtf8(isolate, name); - fn->SetName(fn_name); - recv->Set(fn_name, fn); + const char* name, + v8::FunctionCallback callback) +{ + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + v8::HandleScope handle_scope(isolate); + v8::Local t = v8::FunctionTemplate::New(isolate, + callback); + v8::Local fn = t->GetFunction(); + v8::Local fn_name = v8::String::NewFromUtf8(isolate, name); + fn->SetName(fn_name); + recv->Set(fn_name, fn); } #define NODE_SET_METHOD node::NODE_SET_METHOD // Used to be a macro, hence the uppercase name. // Not a template because it only makes sense for FunctionTemplates. inline void NODE_SET_PROTOTYPE_METHOD(v8::Local recv, - const char* name, - v8::FunctionCallback callback) { - v8::Isolate* isolate = v8::Isolate::GetCurrent(); - v8::HandleScope handle_scope(isolate); - v8::Local s = v8::Signature::New(isolate, recv); - v8::Local t = - v8::FunctionTemplate::New(isolate, callback, v8::Local(), s); - v8::Local fn_name = v8::String::NewFromUtf8(isolate, name); - t->SetClassName(fn_name); - recv->PrototypeTemplate()->Set(v8::String::NewFromUtf8(isolate, name), t); + const char* name, + v8::FunctionCallback callback) +{ + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + v8::HandleScope handle_scope(isolate); + v8::Local s = v8::Signature::New(isolate, recv); + v8::Local t = v8::FunctionTemplate::New(isolate, callback, v8::Local(), s); + v8::Local fn_name = v8::String::NewFromUtf8(isolate, name); + t->SetClassName(fn_name); + recv->PrototypeTemplate()->Set(v8::String::NewFromUtf8(isolate, name), t); } #define NODE_SET_PROTOTYPE_METHOD node::NODE_SET_PROTOTYPE_METHOD // BINARY is a deprecated alias of LATIN1. -enum encoding {ASCII, UTF8, BASE64, UCS2, BINARY, HEX, BUFFER, LATIN1 = BINARY}; +enum encoding { ASCII, + UTF8, + BASE64, + UCS2, + BINARY, + HEX, + BUFFER, + LATIN1 = BINARY }; NODE_EXTERN enum encoding ParseEncoding( v8::Isolate* isolate, v8::Local encoding_v, enum encoding default_encoding = LATIN1); -NODE_DEPRECATED("Use ParseEncoding(isolate, ...)", - inline enum encoding ParseEncoding( - v8::Local encoding_v, - enum encoding default_encoding = LATIN1) { - return ParseEncoding(v8::Isolate::GetCurrent(), encoding_v, default_encoding); -}) +NODE_DEPRECATED( + "Use ParseEncoding(isolate, ...)", + inline enum encoding ParseEncoding( + v8::Local encoding_v, + enum encoding default_encoding = LATIN1) { + return ParseEncoding(v8::Isolate::GetCurrent(), encoding_v, default_encoding); + }) NODE_EXTERN void FatalException(v8::Isolate* isolate, - const v8::TryCatch& try_catch); + const v8::TryCatch& try_catch); -NODE_DEPRECATED("Use FatalException(isolate, ...)", - inline void FatalException(const v8::TryCatch& try_catch) { - return FatalException(v8::Isolate::GetCurrent(), try_catch); -}) +NODE_DEPRECATED( + "Use FatalException(isolate, ...)", + inline void FatalException(const v8::TryCatch& try_catch) { + return FatalException(v8::Isolate::GetCurrent(), try_catch); + }) // Don't call with encoding=UCS2. NODE_EXTERN v8::Local Encode(v8::Isolate* isolate, - const char* buf, - size_t len, - enum encoding encoding = LATIN1); + const char* buf, + size_t len, + enum encoding encoding = LATIN1); // The input buffer should be in host endianness. NODE_EXTERN v8::Local Encode(v8::Isolate* isolate, - const uint16_t* buf, - size_t len); - -NODE_DEPRECATED("Use Encode(isolate, ...)", - inline v8::Local Encode( - const void* buf, - size_t len, - enum encoding encoding = LATIN1) { - v8::Isolate* isolate = v8::Isolate::GetCurrent(); - if (encoding == UCS2) { - assert(reinterpret_cast(buf) % sizeof(uint16_t) == 0 && - "UCS2 buffer must be aligned on two-byte boundary."); - const uint16_t* that = static_cast(buf); - return Encode(isolate, that, len / sizeof(*that)); - } - return Encode(isolate, static_cast(buf), len, encoding); -}) + const uint16_t* buf, + size_t len); + +NODE_DEPRECATED( + "Use Encode(isolate, ...)", + inline v8::Local Encode( + const void* buf, + size_t len, + enum encoding encoding = LATIN1) { + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + if (encoding == UCS2) { + //NODE_ASSERT(reinterpret_cast(buf) % sizeof(uint16_t) == 0 && "UCS2 buffer must be aligned on two-byte boundary."); + const uint16_t* that = static_cast(buf); + return Encode(isolate, that, len / sizeof(*that)); + } + return Encode(isolate, static_cast(buf), len, encoding); + }) // Returns -1 if the handle was not valid for decoding NODE_EXTERN ssize_t DecodeBytes(v8::Isolate* isolate, - v8::Local, - enum encoding encoding = LATIN1); -NODE_DEPRECATED("Use DecodeBytes(isolate, ...)", - inline ssize_t DecodeBytes( - v8::Local val, - enum encoding encoding = LATIN1) { - return DecodeBytes(v8::Isolate::GetCurrent(), val, encoding); -}) + v8::Local, + enum encoding encoding = LATIN1); +NODE_DEPRECATED( + "Use DecodeBytes(isolate, ...)", + inline ssize_t DecodeBytes( + v8::Local val, + enum encoding encoding = LATIN1) { + return DecodeBytes(v8::Isolate::GetCurrent(), val, encoding); + }) // returns bytes written. NODE_EXTERN ssize_t DecodeWrite(v8::Isolate* isolate, - char* buf, - size_t buflen, - v8::Local, - enum encoding encoding = LATIN1); -NODE_DEPRECATED("Use DecodeWrite(isolate, ...)", - inline ssize_t DecodeWrite(char* buf, - size_t buflen, - v8::Local val, - enum encoding encoding = LATIN1) { - return DecodeWrite(v8::Isolate::GetCurrent(), buf, buflen, val, encoding); -}) + char* buf, + size_t buflen, + v8::Local, + enum encoding encoding = LATIN1); +NODE_DEPRECATED( + "Use DecodeWrite(isolate, ...)", + inline ssize_t DecodeWrite(char* buf, + size_t buflen, + v8::Local val, + enum encoding encoding = LATIN1) { + return DecodeWrite(v8::Isolate::GetCurrent(), buf, buflen, val, encoding); + }) #ifdef _WIN32 NODE_EXTERN v8::Local WinapiErrnoException( v8::Isolate* isolate, int errorno, - const char *syscall = NULL, - const char *msg = "", - const char *path = NULL); - -NODE_DEPRECATED("Use WinapiErrnoException(isolate, ...)", - inline v8::Local WinapiErrnoException(int errorno, - const char *syscall = NULL, const char *msg = "", - const char *path = NULL) { - return WinapiErrnoException(v8::Isolate::GetCurrent(), - errorno, - syscall, - msg, - path); -}) + const char* syscall = NULL, + const char* msg = "", + const char* path = NULL); + +NODE_DEPRECATED( + "Use WinapiErrnoException(isolate, ...)", + inline v8::Local WinapiErrnoException(int errorno, + const char* syscall = NULL, const char* msg = "", + const char* path = NULL) { + return WinapiErrnoException(v8::Isolate::GetCurrent(), + errorno, + syscall, + msg, + path); + }) #endif -const char *signo_string(int errorno); - +const char* signo_string(int errorno); typedef void (*addon_register_func)( v8::Local exports, @@ -397,112 +408,122 @@ typedef void (*addon_context_register_func)( void* priv); #define NM_F_BUILTIN 0x01 -#define NM_F_LINKED 0x02 +#define NM_F_LINKED 0x02 struct node_module { - int nm_version; - unsigned int nm_flags; - void* nm_dso_handle; - const char* nm_filename; - node::addon_register_func nm_register_func; - node::addon_context_register_func nm_context_register_func; - const char* nm_modname; - void* nm_priv; - struct node_module* nm_link; + int nm_version; + unsigned int nm_flags; + void* nm_dso_handle; + const char* nm_filename; + node::addon_register_func nm_register_func; + node::addon_context_register_func nm_context_register_func; + const char* nm_modname; + void* nm_priv; + struct node_module* nm_link; }; -node_module* get_builtin_module(const char *name); -node_module* get_linked_module(const char *name); +node_module* get_builtin_module(const char* name); +node_module* get_linked_module(const char* name); extern "C" NODE_EXTERN void node_module_register(void* mod); #ifdef _WIN32 -# define NODE_MODULE_EXPORT __declspec(dllexport) +#define NODE_MODULE_EXPORT __declspec(dllexport) #else -# define NODE_MODULE_EXPORT __attribute__((visibility("default"))) +#define NODE_MODULE_EXPORT __attribute__((visibility("default"))) #endif #ifdef NODE_SHARED_MODE -# define NODE_CTOR_PREFIX +#define NODE_CTOR_PREFIX #else -# define NODE_CTOR_PREFIX static +#define NODE_CTOR_PREFIX static #endif #if defined(_MSC_VER) -#pragma section(".CRT$XCU", read) -#define NODE_C_CTOR(fn) \ - NODE_CTOR_PREFIX void __cdecl fn(void); \ - __declspec(dllexport, allocate(".CRT$XCU")) \ - void (__cdecl*fn ## _)(void) = fn; \ - NODE_CTOR_PREFIX void __cdecl fn(void) +// #pragma section(".CRT$XCU", read) +// #define NODE_C_CTOR(fn) \ +// NODE_CTOR_PREFIX void __cdecl fn(void); \ +// __declspec(dllexport, allocate(".CRT$XCU")) \ +// void (__cdecl*fn ## _)(void) = fn; \ +// NODE_CTOR_PREFIX void __cdecl fn(void) + +#define NODE_C_CTOR(fn) \ + void __cdecl fn(void) + #else -#define NODE_C_CTOR(fn) \ - NODE_CTOR_PREFIX void fn(void) __attribute__((constructor)); \ - NODE_CTOR_PREFIX void fn(void) +#define NODE_C_CTOR(fn) \ + NODE_CTOR_PREFIX void fn(void) __attribute__((constructor)); \ + NODE_CTOR_PREFIX void fn(void) #endif -#define NODE_MODULE_X(modname, regfunc, priv, flags) \ - extern "C" { \ - static node::node_module _module = \ - { \ - NODE_MODULE_VERSION, \ - flags, \ - NULL, \ - __FILE__, \ - (node::addon_register_func) (regfunc), \ - NULL, \ - NODE_STRINGIFY(modname), \ - priv, \ - NULL \ - }; \ - NODE_C_CTOR(_register_ ## modname) { \ - node_module_register(&_module); \ - } \ - } - -#define NODE_MODULE_CONTEXT_AWARE_X(modname, regfunc, priv, flags) \ - extern "C" { \ - static node::node_module _module = \ - { \ - NODE_MODULE_VERSION, \ - flags, \ - NULL, \ - __FILE__, \ - NULL, \ - (node::addon_context_register_func) (regfunc), \ - NODE_STRINGIFY(modname), \ - priv, \ - NULL \ - }; \ - NODE_C_CTOR(_register_ ## modname) { \ - node_module_register(&_module); \ - } \ - } - -#define NODE_MODULE(modname, regfunc) \ - NODE_MODULE_X(modname, regfunc, NULL, 0) - -#define NODE_MODULE_CONTEXT_AWARE(modname, regfunc) \ - NODE_MODULE_CONTEXT_AWARE_X(modname, regfunc, NULL, 0) - -#define NODE_MODULE_CONTEXT_AWARE_BUILTIN(modname, regfunc) \ - NODE_MODULE_CONTEXT_AWARE_X(modname, regfunc, NULL, NM_F_BUILTIN) \ - -#define NODE_MODULE_CONTEXT_AWARE_BUILTIN_SCRIPT(modname, regfunc, priv) \ - NODE_MODULE_CONTEXT_AWARE_X(modname, regfunc, priv, NM_F_BUILTIN) \ -/* - * For backward compatibility in add-on modules. +#define NODE_MODULE_X(modname, regfunc, priv, flags) \ + extern "C" { \ + static node::node_module _module = { \ + NODE_MODULE_VERSION, \ + flags, \ + NULL, \ + __FILE__, \ + (node::addon_register_func)(regfunc), \ + NULL, \ + NODE_STRINGIFY(modname), \ + priv, \ + NULL \ + }; \ + NODE_C_CTOR(_register_##modname) \ + { \ + node_module_register(&_module); \ + } \ + } + +#define NODE_MODULE_CONTEXT_AWARE_X(modname, regfunc, priv, flags) \ + extern "C" { \ + static node::node_module _module = { \ + NODE_MODULE_VERSION, \ + flags, \ + NULL, \ + __FILE__, \ + NULL, \ + (node::addon_context_register_func)(regfunc), \ + NODE_STRINGIFY(modname), \ + priv, \ + NULL \ + }; \ + NODE_C_CTOR(_register_##modname) \ + { \ + node_module_register(&_module); \ + } \ + } + +#define NODE_MODULE(modname, regfunc) \ + NODE_MODULE_X(modname, regfunc, NULL, 0) + +#define NODE_MODULE_CONTEXT_AWARE(modname, regfunc) \ + NODE_MODULE_CONTEXT_AWARE_X(modname, regfunc, NULL, 0) + +#define NODE_MODULE_CONTEXT_AWARE_BUILTIN(modname, regfunc) \ + NODE_MODULE_CONTEXT_AWARE_X(modname, regfunc, NULL, NM_F_BUILTIN) + +#define NODE_MODULE_CONTEXT_AWARE_BUILTIN_SCRIPT(modname, regfunc, priv) \ + NODE_MODULE_CONTEXT_AWARE_X(modname, regfunc, priv, NM_F_BUILTIN) \ +/* \ + * For backward compatibility in add-on modules. \ */ #define NODE_MODULE_DECL /* nothing */ +#define NODE_MODULE_BUILTIN_SCRIPT_DECLARE_IN_MAIN(modname) \ + extern "C" void __cdecl _register_##modname(void); + +#define NODE_MODULE_BUILTIN_SCRIPT_DEFINDE_IN_MAIN(modname) \ + _register_##modname(); + /* Called after the event loop exits but before the VM is disposed. * Callbacks are run in reverse order of registration, i.e. newest first. */ NODE_EXTERN void AtExit(void (*cb)(void* arg), void* arg = 0); -NODE_EXTERN void AddEnvironmentCleanupHook(v8::Isolate* isolate, void(*fun)(void* arg), void* arg); -NODE_EXTERN void RemoveEnvironmentCleanupHook(v8::Isolate* isolate, void(*fun)(void* arg), void* arg); +NODE_EXTERN void AddEnvironmentCleanupHook(v8::Isolate* isolate, void (*fun)(void* arg), void* arg); +NODE_EXTERN void RemoveEnvironmentCleanupHook(v8::Isolate* isolate, void (*fun)(void* arg), void* arg); -} // namespace node +} // namespace node -#endif // SRC_NODE_H_ +#endif // SRC_NODE_H_ diff --git a/node/src/node1.cc b/node/src/node1.cc index daf58c277e..29847b60b9 100644 --- a/node/src/node1.cc +++ b/node/src/node1.cc @@ -18,7 +18,6 @@ #include "node_crypto.h" #endif - #if defined HAVE_DTRACE || defined HAVE_ETW #include "node_dtrace.h" #endif @@ -44,7 +43,7 @@ #include #include -#include // PATH_MAX +#include // PATH_MAX #include #include #include @@ -72,1778 +71,1762 @@ void napi_module_register_by_symbol(v8::Local exports, namespace node { - using v8::Array; - using v8::ArrayBuffer; - using v8::Boolean; - using v8::Context; - using v8::EscapableHandleScope; - using v8::Exception; - using v8::Float64Array; - using v8::Function; - using v8::FunctionCallbackInfo; - using v8::FunctionTemplate; - using v8::HandleScope; - using v8::HeapStatistics; - using v8::Integer; - using v8::Isolate; - using v8::Local; - using v8::Locker; - using v8::MaybeLocal; - using v8::Message; - using v8::Name; - using v8::NamedPropertyHandlerConfiguration; - using v8::Null; - using v8::Number; - using v8::Object; - using v8::ObjectTemplate; - using v8::Promise; - using v8::PromiseRejectMessage; - using v8::PropertyCallbackInfo; - using v8::PropertyHandlerFlags; - using v8::ScriptOrigin; - using v8::SealHandleScope; - using v8::String; - using v8::TryCatch; - using v8::Uint32Array; - using v8::V8; - using v8::Value; - - static bool print_eval = false; - static bool force_repl = false; - static bool syntax_check_only = false; - static bool trace_deprecation = false; - static bool throw_deprecation = false; - static bool trace_sync_io = false; - static bool track_heap_objects = false; - static const char* eval_string = nullptr; - static unsigned int preload_module_count = 0; - static const char** preload_modules = nullptr; - static bool use_debug_agent = false; - static bool debug_wait_connect = false; - static std::string debug_host; // NOLINT(runtime/string) - static int debug_port = 5858; - static const int v8_default_thread_pool_size = 4; - static int v8_thread_pool_size = v8_default_thread_pool_size; - static bool prof_process = false; - static bool v8_is_profiling = false; - static bool node_is_initialized = false; - static node_module* modpending; - static node_module* modlist_builtin; - static node_module* modlist_linked; - static node_module* modlist_addon; - - - // used by C++ modules as well - bool no_deprecation = false; - const char* openssl_config = nullptr; - - // true if process warnings should be suppressed - bool no_process_warnings = false; - bool trace_warnings = false; - - // Set in node.cc by ParseArgs when --preserve-symlinks is used. - // Used in node_config.cc to set a constant on process.binding('config') - // that is used by lib/module.js - bool config_preserve_symlinks = false; - - // process-relative uptime base, initialized at start-up - static double prog_start_time; - static bool debugger_running; - static uv_async_t dispatch_debug_messages_async; - - static Mutex node_isolate_mutex; - static v8::Isolate* node_isolate; - - - static void PrintErrorString(const char* format, ...) { - va_list ap; - va_start(ap, format); +using v8::Array; +using v8::ArrayBuffer; +using v8::Boolean; +using v8::Context; +using v8::EscapableHandleScope; +using v8::Exception; +using v8::Float64Array; +using v8::Function; +using v8::FunctionCallbackInfo; +using v8::FunctionTemplate; +using v8::HandleScope; +using v8::HeapStatistics; +using v8::Integer; +using v8::Isolate; +using v8::Local; +using v8::Locker; +using v8::MaybeLocal; +using v8::Message; +using v8::Name; +using v8::NamedPropertyHandlerConfiguration; +using v8::Null; +using v8::Number; +using v8::Object; +using v8::ObjectTemplate; +using v8::Promise; +using v8::PromiseRejectMessage; +using v8::PropertyCallbackInfo; +using v8::PropertyHandlerFlags; +using v8::ScriptOrigin; +using v8::SealHandleScope; +using v8::String; +using v8::TryCatch; +using v8::Uint32Array; +using v8::V8; +using v8::Value; + +static bool print_eval = false; +static bool force_repl = false; +static bool syntax_check_only = false; +static bool trace_deprecation = false; +static bool throw_deprecation = false; +static bool trace_sync_io = false; +static bool track_heap_objects = false; +static const char* eval_string = nullptr; +static unsigned int preload_module_count = 0; +static const char** preload_modules = nullptr; +static bool use_debug_agent = false; +static bool debug_wait_connect = false; +static std::string debug_host; // NOLINT(runtime/string) +static int debug_port = 5858; +static const int v8_default_thread_pool_size = 4; +static int v8_thread_pool_size = v8_default_thread_pool_size; +static bool prof_process = false; +static bool v8_is_profiling = false; +static bool node_is_initialized = false; +static node_module* modpending; +static node_module* modlist_builtin; +static node_module* modlist_linked; +static node_module* modlist_addon; + +// used by C++ modules as well +bool no_deprecation = false; +const char* openssl_config = nullptr; + +// true if process warnings should be suppressed +bool no_process_warnings = false; +bool trace_warnings = false; + +// Set in node.cc by ParseArgs when --preserve-symlinks is used. +// Used in node_config.cc to set a constant on process.binding('config') +// that is used by lib/module.js +bool config_preserve_symlinks = false; + +// process-relative uptime base, initialized at start-up +static double prog_start_time; +static bool debugger_running; +static uv_async_t dispatch_debug_messages_async; + +static Mutex node_isolate_mutex; +static v8::Isolate* node_isolate; + +static void PrintErrorString(const char* format, ...) +{ + va_list ap; + va_start(ap, format); #ifdef _WIN32 - HANDLE stderr_handle = GetStdHandle(STD_ERROR_HANDLE); - - // Fill in any placeholders - int n = _vscprintf(format, ap); - std::vector out(n + 1); - vsprintf(&out[0], format, ap); - - // Check if stderr is something other than a tty/console - if (stderr_handle == INVALID_HANDLE_VALUE || - stderr_handle == nullptr || - uv_guess_handle(_fileno(stderr)) != UV_TTY) { - vfprintf(stderr, format, ap); - - out.push_back('\n'); - OutputDebugStringA(&out[0]); - - va_end(ap); - return; - } - - // Get required wide buffer size - n = MultiByteToWideChar(CP_UTF8, 0, &out[0], -1, nullptr, 0); - - std::vector wbuf(n); - MultiByteToWideChar(CP_UTF8, 0, &out[0], -1, &wbuf[0], n); - - // Don't include the null character in the output - CHECK_GT(n, 0); - WriteConsoleW(stderr_handle, &wbuf[0], n - 1, nullptr, nullptr); - - wbuf.push_back(L'\n'); - OutputDebugStringW(&wbuf[0]); -#else - vfprintf(stderr, format, ap); -#endif - va_end(ap); - } + HANDLE stderr_handle = GetStdHandle(STD_ERROR_HANDLE); + + // Fill in any placeholders + int n = _vscprintf(format, ap); + std::vector out(n + 1); + vsprintf(&out[0], format, ap); + // Check if stderr is something other than a tty/console + if (stderr_handle == INVALID_HANDLE_VALUE || stderr_handle == nullptr || uv_guess_handle(_fileno(stderr)) != UV_TTY) { + vfprintf(stderr, format, ap); - static void CheckImmediate(uv_check_t* handle) { - Environment* env = Environment::from_immediate_check_handle(handle); - HandleScope scope(env->isolate()); - Context::Scope context_scope(env->context()); - MakeCallback(env, env->process_object(), env->immediate_callback_string()); - } + out.push_back('\n'); + OutputDebugStringA(&out[0]); + + va_end(ap); + return; + } + // Get required wide buffer size + n = MultiByteToWideChar(CP_UTF8, 0, &out[0], -1, nullptr, 0); - static void IdleImmediateDummy(uv_idle_t* handle) { - // Do nothing. Only for maintaining event loop. - // TODO(bnoordhuis) Maybe make libuv accept nullptr idle callbacks. - } + std::vector wbuf(n); + MultiByteToWideChar(CP_UTF8, 0, &out[0], -1, &wbuf[0], n); + // Don't include the null character in the output + CHECK_GT(n, 0); + WriteConsoleW(stderr_handle, &wbuf[0], n - 1, nullptr, nullptr); - static inline const char *errno_string(int errorno) { -#define ERRNO_CASE(e) case e: return #e; - switch (errorno) { + wbuf.push_back(L'\n'); + OutputDebugStringW(&wbuf[0]); +#else + vfprintf(stderr, format, ap); +#endif + va_end(ap); +} + +static void CheckImmediate(uv_check_t* handle) +{ + Environment* env = Environment::from_immediate_check_handle(handle); + HandleScope scope(env->isolate()); + Context::Scope context_scope(env->context()); + MakeCallback(env, env->process_object(), env->immediate_callback_string()); +} + +static void IdleImmediateDummy(uv_idle_t* handle) +{ + // Do nothing. Only for maintaining event loop. + // TODO(bnoordhuis) Maybe make libuv accept nullptr idle callbacks. +} + +static inline const char* errno_string(int errorno) +{ +#define ERRNO_CASE(e) \ + case e: \ + return #e; + switch (errorno) { #ifdef EACCES - ERRNO_CASE(EACCES); + ERRNO_CASE(EACCES); #endif #ifdef EADDRINUSE - ERRNO_CASE(EADDRINUSE); + ERRNO_CASE(EADDRINUSE); #endif #ifdef EADDRNOTAVAIL - ERRNO_CASE(EADDRNOTAVAIL); + ERRNO_CASE(EADDRNOTAVAIL); #endif #ifdef EAFNOSUPPORT - ERRNO_CASE(EAFNOSUPPORT); + ERRNO_CASE(EAFNOSUPPORT); #endif #ifdef EAGAIN - ERRNO_CASE(EAGAIN); + ERRNO_CASE(EAGAIN); #endif #ifdef EWOULDBLOCK -# if EAGAIN != EWOULDBLOCK - ERRNO_CASE(EWOULDBLOCK); -# endif +#if EAGAIN != EWOULDBLOCK + ERRNO_CASE(EWOULDBLOCK); +#endif #endif #ifdef EALREADY - ERRNO_CASE(EALREADY); + ERRNO_CASE(EALREADY); #endif #ifdef EBADF - ERRNO_CASE(EBADF); + ERRNO_CASE(EBADF); #endif #ifdef EBADMSG - ERRNO_CASE(EBADMSG); + ERRNO_CASE(EBADMSG); #endif #ifdef EBUSY - ERRNO_CASE(EBUSY); + ERRNO_CASE(EBUSY); #endif #ifdef ECANCELED - ERRNO_CASE(ECANCELED); + ERRNO_CASE(ECANCELED); #endif #ifdef ECHILD - ERRNO_CASE(ECHILD); + ERRNO_CASE(ECHILD); #endif #ifdef ECONNABORTED - ERRNO_CASE(ECONNABORTED); + ERRNO_CASE(ECONNABORTED); #endif #ifdef ECONNREFUSED - ERRNO_CASE(ECONNREFUSED); + ERRNO_CASE(ECONNREFUSED); #endif #ifdef ECONNRESET - ERRNO_CASE(ECONNRESET); + ERRNO_CASE(ECONNRESET); #endif #ifdef EDEADLK - ERRNO_CASE(EDEADLK); + ERRNO_CASE(EDEADLK); #endif #ifdef EDESTADDRREQ - ERRNO_CASE(EDESTADDRREQ); + ERRNO_CASE(EDESTADDRREQ); #endif #ifdef EDOM - ERRNO_CASE(EDOM); + ERRNO_CASE(EDOM); #endif #ifdef EDQUOT - ERRNO_CASE(EDQUOT); + ERRNO_CASE(EDQUOT); #endif #ifdef EEXIST - ERRNO_CASE(EEXIST); + ERRNO_CASE(EEXIST); #endif #ifdef EFAULT - ERRNO_CASE(EFAULT); + ERRNO_CASE(EFAULT); #endif #ifdef EFBIG - ERRNO_CASE(EFBIG); + ERRNO_CASE(EFBIG); #endif #ifdef EHOSTUNREACH - ERRNO_CASE(EHOSTUNREACH); + ERRNO_CASE(EHOSTUNREACH); #endif #ifdef EIDRM - ERRNO_CASE(EIDRM); + ERRNO_CASE(EIDRM); #endif #ifdef EILSEQ - ERRNO_CASE(EILSEQ); + ERRNO_CASE(EILSEQ); #endif #ifdef EINPROGRESS - ERRNO_CASE(EINPROGRESS); + ERRNO_CASE(EINPROGRESS); #endif #ifdef EINTR - ERRNO_CASE(EINTR); + ERRNO_CASE(EINTR); #endif #ifdef EINVAL - ERRNO_CASE(EINVAL); + ERRNO_CASE(EINVAL); #endif #ifdef EIO - ERRNO_CASE(EIO); + ERRNO_CASE(EIO); #endif #ifdef EISCONN - ERRNO_CASE(EISCONN); + ERRNO_CASE(EISCONN); #endif #ifdef EISDIR - ERRNO_CASE(EISDIR); + ERRNO_CASE(EISDIR); #endif #ifdef ELOOP - ERRNO_CASE(ELOOP); + ERRNO_CASE(ELOOP); #endif #ifdef EMFILE - ERRNO_CASE(EMFILE); + ERRNO_CASE(EMFILE); #endif #ifdef EMLINK - ERRNO_CASE(EMLINK); + ERRNO_CASE(EMLINK); #endif #ifdef EMSGSIZE - ERRNO_CASE(EMSGSIZE); + ERRNO_CASE(EMSGSIZE); #endif #ifdef EMULTIHOP - ERRNO_CASE(EMULTIHOP); + ERRNO_CASE(EMULTIHOP); #endif #ifdef ENAMETOOLONG - ERRNO_CASE(ENAMETOOLONG); + ERRNO_CASE(ENAMETOOLONG); #endif #ifdef ENETDOWN - ERRNO_CASE(ENETDOWN); + ERRNO_CASE(ENETDOWN); #endif #ifdef ENETRESET - ERRNO_CASE(ENETRESET); + ERRNO_CASE(ENETRESET); #endif #ifdef ENETUNREACH - ERRNO_CASE(ENETUNREACH); + ERRNO_CASE(ENETUNREACH); #endif #ifdef ENFILE - ERRNO_CASE(ENFILE); + ERRNO_CASE(ENFILE); #endif #ifdef ENOBUFS - ERRNO_CASE(ENOBUFS); + ERRNO_CASE(ENOBUFS); #endif #ifdef ENODATA - ERRNO_CASE(ENODATA); + ERRNO_CASE(ENODATA); #endif #ifdef ENODEV - ERRNO_CASE(ENODEV); + ERRNO_CASE(ENODEV); #endif #ifdef ENOENT - ERRNO_CASE(ENOENT); + ERRNO_CASE(ENOENT); #endif #ifdef ENOEXEC - ERRNO_CASE(ENOEXEC); + ERRNO_CASE(ENOEXEC); #endif #ifdef ENOLINK - ERRNO_CASE(ENOLINK); + ERRNO_CASE(ENOLINK); #endif #ifdef ENOLCK -# if ENOLINK != ENOLCK - ERRNO_CASE(ENOLCK); -# endif +#if ENOLINK != ENOLCK + ERRNO_CASE(ENOLCK); +#endif #endif #ifdef ENOMEM - ERRNO_CASE(ENOMEM); + ERRNO_CASE(ENOMEM); #endif #ifdef ENOMSG - ERRNO_CASE(ENOMSG); + ERRNO_CASE(ENOMSG); #endif #ifdef ENOPROTOOPT - ERRNO_CASE(ENOPROTOOPT); + ERRNO_CASE(ENOPROTOOPT); #endif #ifdef ENOSPC - ERRNO_CASE(ENOSPC); + ERRNO_CASE(ENOSPC); #endif #ifdef ENOSR - ERRNO_CASE(ENOSR); + ERRNO_CASE(ENOSR); #endif #ifdef ENOSTR - ERRNO_CASE(ENOSTR); + ERRNO_CASE(ENOSTR); #endif #ifdef ENOSYS - ERRNO_CASE(ENOSYS); + ERRNO_CASE(ENOSYS); #endif #ifdef ENOTCONN - ERRNO_CASE(ENOTCONN); + ERRNO_CASE(ENOTCONN); #endif #ifdef ENOTDIR - ERRNO_CASE(ENOTDIR); + ERRNO_CASE(ENOTDIR); #endif #ifdef ENOTEMPTY -# if ENOTEMPTY != EEXIST - ERRNO_CASE(ENOTEMPTY); -# endif +#if ENOTEMPTY != EEXIST + ERRNO_CASE(ENOTEMPTY); +#endif #endif #ifdef ENOTSOCK - ERRNO_CASE(ENOTSOCK); + ERRNO_CASE(ENOTSOCK); #endif #ifdef ENOTSUP - ERRNO_CASE(ENOTSUP); + ERRNO_CASE(ENOTSUP); #else -# ifdef EOPNOTSUPP - ERRNO_CASE(EOPNOTSUPP); -# endif +#ifdef EOPNOTSUPP + ERRNO_CASE(EOPNOTSUPP); +#endif #endif #ifdef ENOTTY - ERRNO_CASE(ENOTTY); + ERRNO_CASE(ENOTTY); #endif #ifdef ENXIO - ERRNO_CASE(ENXIO); + ERRNO_CASE(ENXIO); #endif - #ifdef EOVERFLOW - ERRNO_CASE(EOVERFLOW); + ERRNO_CASE(EOVERFLOW); #endif #ifdef EPERM - ERRNO_CASE(EPERM); + ERRNO_CASE(EPERM); #endif #ifdef EPIPE - ERRNO_CASE(EPIPE); + ERRNO_CASE(EPIPE); #endif #ifdef EPROTO - ERRNO_CASE(EPROTO); + ERRNO_CASE(EPROTO); #endif #ifdef EPROTONOSUPPORT - ERRNO_CASE(EPROTONOSUPPORT); + ERRNO_CASE(EPROTONOSUPPORT); #endif #ifdef EPROTOTYPE - ERRNO_CASE(EPROTOTYPE); + ERRNO_CASE(EPROTOTYPE); #endif #ifdef ERANGE - ERRNO_CASE(ERANGE); + ERRNO_CASE(ERANGE); #endif #ifdef EROFS - ERRNO_CASE(EROFS); + ERRNO_CASE(EROFS); #endif #ifdef ESPIPE - ERRNO_CASE(ESPIPE); + ERRNO_CASE(ESPIPE); #endif #ifdef ESRCH - ERRNO_CASE(ESRCH); + ERRNO_CASE(ESRCH); #endif #ifdef ESTALE - ERRNO_CASE(ESTALE); + ERRNO_CASE(ESTALE); #endif #ifdef ETIME - ERRNO_CASE(ETIME); + ERRNO_CASE(ETIME); #endif #ifdef ETIMEDOUT - ERRNO_CASE(ETIMEDOUT); + ERRNO_CASE(ETIMEDOUT); #endif #ifdef ETXTBSY - ERRNO_CASE(ETXTBSY); + ERRNO_CASE(ETXTBSY); #endif #ifdef EXDEV - ERRNO_CASE(EXDEV); + ERRNO_CASE(EXDEV); #endif - default: return ""; - } - } - - const char *signo_string(int signo) { -#define SIGNO_CASE(e) case e: return #e; - switch (signo) { + default: + return ""; + } +} + +const char* signo_string(int signo) +{ +#define SIGNO_CASE(e) \ + case e: \ + return #e; + switch (signo) { #ifdef SIGHUP - SIGNO_CASE(SIGHUP); + SIGNO_CASE(SIGHUP); #endif #ifdef SIGINT - SIGNO_CASE(SIGINT); + SIGNO_CASE(SIGINT); #endif #ifdef SIGQUIT - SIGNO_CASE(SIGQUIT); + SIGNO_CASE(SIGQUIT); #endif #ifdef SIGILL - SIGNO_CASE(SIGILL); + SIGNO_CASE(SIGILL); #endif #ifdef SIGTRAP - SIGNO_CASE(SIGTRAP); + SIGNO_CASE(SIGTRAP); #endif #ifdef SIGABRT - SIGNO_CASE(SIGABRT); + SIGNO_CASE(SIGABRT); #endif #ifdef SIGIOT -# if SIGABRT != SIGIOT - SIGNO_CASE(SIGIOT); -# endif +#if SIGABRT != SIGIOT + SIGNO_CASE(SIGIOT); +#endif #endif #ifdef SIGBUS - SIGNO_CASE(SIGBUS); + SIGNO_CASE(SIGBUS); #endif #ifdef SIGFPE - SIGNO_CASE(SIGFPE); + SIGNO_CASE(SIGFPE); #endif #ifdef SIGKILL - SIGNO_CASE(SIGKILL); + SIGNO_CASE(SIGKILL); #endif #ifdef SIGUSR1 - SIGNO_CASE(SIGUSR1); + SIGNO_CASE(SIGUSR1); #endif #ifdef SIGSEGV - SIGNO_CASE(SIGSEGV); + SIGNO_CASE(SIGSEGV); #endif #ifdef SIGUSR2 - SIGNO_CASE(SIGUSR2); + SIGNO_CASE(SIGUSR2); #endif #ifdef SIGPIPE - SIGNO_CASE(SIGPIPE); + SIGNO_CASE(SIGPIPE); #endif #ifdef SIGALRM - SIGNO_CASE(SIGALRM); + SIGNO_CASE(SIGALRM); #endif - SIGNO_CASE(SIGTERM); + SIGNO_CASE(SIGTERM); #ifdef SIGCHLD - SIGNO_CASE(SIGCHLD); + SIGNO_CASE(SIGCHLD); #endif #ifdef SIGSTKFLT - SIGNO_CASE(SIGSTKFLT); + SIGNO_CASE(SIGSTKFLT); #endif - #ifdef SIGCONT - SIGNO_CASE(SIGCONT); + SIGNO_CASE(SIGCONT); #endif #ifdef SIGSTOP - SIGNO_CASE(SIGSTOP); + SIGNO_CASE(SIGSTOP); #endif #ifdef SIGTSTP - SIGNO_CASE(SIGTSTP); + SIGNO_CASE(SIGTSTP); #endif #ifdef SIGBREAK - SIGNO_CASE(SIGBREAK); + SIGNO_CASE(SIGBREAK); #endif #ifdef SIGTTIN - SIGNO_CASE(SIGTTIN); + SIGNO_CASE(SIGTTIN); #endif #ifdef SIGTTOU - SIGNO_CASE(SIGTTOU); + SIGNO_CASE(SIGTTOU); #endif #ifdef SIGURG - SIGNO_CASE(SIGURG); + SIGNO_CASE(SIGURG); #endif #ifdef SIGXCPU - SIGNO_CASE(SIGXCPU); + SIGNO_CASE(SIGXCPU); #endif #ifdef SIGXFSZ - SIGNO_CASE(SIGXFSZ); + SIGNO_CASE(SIGXFSZ); #endif #ifdef SIGVTALRM - SIGNO_CASE(SIGVTALRM); + SIGNO_CASE(SIGVTALRM); #endif #ifdef SIGPROF - SIGNO_CASE(SIGPROF); + SIGNO_CASE(SIGPROF); #endif #ifdef SIGWINCH - SIGNO_CASE(SIGWINCH); + SIGNO_CASE(SIGWINCH); #endif #ifdef SIGIO - SIGNO_CASE(SIGIO); + SIGNO_CASE(SIGIO); #endif #ifdef SIGPOLL -# if SIGPOLL != SIGIO - SIGNO_CASE(SIGPOLL); -# endif +#if SIGPOLL != SIGIO + SIGNO_CASE(SIGPOLL); +#endif #endif #ifdef SIGLOST -# if SIGLOST != SIGABRT - SIGNO_CASE(SIGLOST); -# endif +#if SIGLOST != SIGABRT + SIGNO_CASE(SIGLOST); +#endif #endif #ifdef SIGPWR -# if SIGPWR != SIGLOST - SIGNO_CASE(SIGPWR); -# endif +#if SIGPWR != SIGLOST + SIGNO_CASE(SIGPWR); +#endif #endif #ifdef SIGINFO -# if !defined(SIGPWR) || SIGINFO != SIGPWR - SIGNO_CASE(SIGINFO); -# endif +#if !defined(SIGPWR) || SIGINFO != SIGPWR + SIGNO_CASE(SIGINFO); +#endif #endif #ifdef SIGSYS - SIGNO_CASE(SIGSYS); -#endif - - default: return ""; - } - } - - - // Convenience methods - - //异常处理函数 - void ThrowError(v8::Isolate* isolate, const char* errmsg) { - Environment::GetCurrent(isolate)->ThrowError(errmsg); - } - void ThrowTypeError(v8::Isolate* isolate, const char* errmsg) { - Environment::GetCurrent(isolate)->ThrowTypeError(errmsg); - } - void ThrowRangeError(v8::Isolate* isolate, const char* errmsg) { - Environment::GetCurrent(isolate)->ThrowRangeError(errmsg); - } - void ThrowErrnoException(v8::Isolate* isolate, int errorno, const char* syscall, const char* message, const char* path) { - Environment::GetCurrent(isolate)->ThrowErrnoException(errorno, syscall, message, path); - } - void ThrowUVException(v8::Isolate* isolate, int errorno, const char* syscall, const char* message, const char* path, const char* dest) { - Environment::GetCurrent(isolate)->ThrowUVException(errorno, syscall, message, path, dest); - } - Local ErrnoException(Isolate* isolate, int errorno, const char *syscall, const char *msg, const char *path) { - Environment* env = Environment::GetCurrent(isolate); - - Local e; - Local estring = OneByteString(env->isolate(), errno_string(errorno)); - if (msg == nullptr || msg[0] == '\0') { - msg = strerror(errorno); - } - Local message = OneByteString(env->isolate(), msg); - - Local cons = - String::Concat(estring, FIXED_ONE_BYTE_STRING(env->isolate(), ", ")); - cons = String::Concat(cons, message); - - Local path_string; - if (path != nullptr) { - // FIXME(bnoordhuis) It's questionable to interpret the file path as UTF-8. - path_string = String::NewFromUtf8(env->isolate(), path); - } - - if (path_string.IsEmpty() == false) { - cons = String::Concat(cons, FIXED_ONE_BYTE_STRING(env->isolate(), " '")); - cons = String::Concat(cons, path_string); - cons = String::Concat(cons, FIXED_ONE_BYTE_STRING(env->isolate(), "'")); - } - e = Exception::Error(cons); + SIGNO_CASE(SIGSYS); +#endif + + default: + return ""; + } +} + +// Convenience methods + +//异常处理函数 +void ThrowError(v8::Isolate* isolate, const char* errmsg) +{ + Environment::GetCurrent(isolate)->ThrowError(errmsg); +} +void ThrowTypeError(v8::Isolate* isolate, const char* errmsg) +{ + Environment::GetCurrent(isolate)->ThrowTypeError(errmsg); +} +void ThrowRangeError(v8::Isolate* isolate, const char* errmsg) +{ + Environment::GetCurrent(isolate)->ThrowRangeError(errmsg); +} +void ThrowErrnoException(v8::Isolate* isolate, int errorno, const char* syscall, const char* message, const char* path) +{ + Environment::GetCurrent(isolate)->ThrowErrnoException(errorno, syscall, message, path); +} +void ThrowUVException(v8::Isolate* isolate, int errorno, const char* syscall, const char* message, const char* path, const char* dest) +{ + Environment::GetCurrent(isolate)->ThrowUVException(errorno, syscall, message, path, dest); +} +Local ErrnoException(Isolate* isolate, int errorno, const char* syscall, const char* msg, const char* path) +{ + Environment* env = Environment::GetCurrent(isolate); + + Local e; + Local estring = OneByteString(env->isolate(), errno_string(errorno)); + if (msg == nullptr || msg[0] == '\0') { + msg = strerror(errorno); + } + Local message = OneByteString(env->isolate(), msg); + + Local cons = String::Concat(estring, FIXED_ONE_BYTE_STRING(env->isolate(), ", ")); + cons = String::Concat(cons, message); + + Local path_string; + if (path != nullptr) { + // FIXME(bnoordhuis) It's questionable to interpret the file path as UTF-8. + path_string = String::NewFromUtf8(env->isolate(), path); + } + + if (path_string.IsEmpty() == false) { + cons = String::Concat(cons, FIXED_ONE_BYTE_STRING(env->isolate(), " '")); + cons = String::Concat(cons, path_string); + cons = String::Concat(cons, FIXED_ONE_BYTE_STRING(env->isolate(), "'")); + } + e = Exception::Error(cons); if (!env) - return e; - - Local obj = e->ToObject(env->isolate()); - obj->Set(env->errno_string(), Integer::New(env->isolate(), errorno)); - obj->Set(env->code_string(), estring); - - if (path_string.IsEmpty() == false) { - obj->Set(env->path_string(), path_string); - } - - if (syscall != nullptr) { - obj->Set(env->syscall_string(), OneByteString(env->isolate(), syscall)); - } - - return e; - } - static Local StringFromPath(Isolate* isolate, const char* path) { - if (strncmp(path, "\\\\?\\UNC\\", 8) == 0) { - return String::Concat(FIXED_ONE_BYTE_STRING(isolate, "\\\\"), - String::NewFromUtf8(isolate, path + 8)); - } - else if (strncmp(path, "\\\\?\\", 4) == 0) { - return String::NewFromUtf8(isolate, path + 4); - } - return String::NewFromUtf8(isolate, path); - } - Local UVException(Isolate* isolate, int errorno, const char* syscall, const char* msg, const char* path) { - return UVException(isolate, errorno, syscall, msg, path, nullptr); - } - Local UVException(Isolate* isolate, int errorno, const char* syscall, const char* msg, const char* path, const char* dest) { - Environment* env = Environment::GetCurrent(isolate); - - if (!msg || !msg[0]) - msg = uv_strerror(errorno); - - Local js_code = OneByteString(isolate, uv_err_name(errorno)); - Local js_syscall = OneByteString(isolate, syscall); - Local js_path; - Local js_dest; - - Local js_msg = js_code; - js_msg = String::Concat(js_msg, FIXED_ONE_BYTE_STRING(isolate, ": ")); - js_msg = String::Concat(js_msg, OneByteString(isolate, msg)); - js_msg = String::Concat(js_msg, FIXED_ONE_BYTE_STRING(isolate, ", ")); - js_msg = String::Concat(js_msg, js_syscall); - - if (path != nullptr) { - js_path = StringFromPath(isolate, path); - - js_msg = String::Concat(js_msg, FIXED_ONE_BYTE_STRING(isolate, " '")); - js_msg = String::Concat(js_msg, js_path); - js_msg = String::Concat(js_msg, FIXED_ONE_BYTE_STRING(isolate, "'")); - } - - if (dest != nullptr) { - js_dest = StringFromPath(isolate, dest); - - js_msg = String::Concat(js_msg, FIXED_ONE_BYTE_STRING(isolate, " -> '")); - js_msg = String::Concat(js_msg, js_dest); - js_msg = String::Concat(js_msg, FIXED_ONE_BYTE_STRING(isolate, "'")); - } - - Local e = Exception::Error(js_msg)->ToObject(isolate); + return e; + + Local obj = e->ToObject(env->isolate()); + obj->Set(env->errno_string(), Integer::New(env->isolate(), errorno)); + obj->Set(env->code_string(), estring); + + if (path_string.IsEmpty() == false) { + obj->Set(env->path_string(), path_string); + } + + if (syscall != nullptr) { + obj->Set(env->syscall_string(), OneByteString(env->isolate(), syscall)); + } + + return e; +} +static Local StringFromPath(Isolate* isolate, const char* path) +{ + if (strncmp(path, "\\\\?\\UNC\\", 8) == 0) { + return String::Concat(FIXED_ONE_BYTE_STRING(isolate, "\\\\"), + String::NewFromUtf8(isolate, path + 8)); + } else if (strncmp(path, "\\\\?\\", 4) == 0) { + return String::NewFromUtf8(isolate, path + 4); + } + return String::NewFromUtf8(isolate, path); +} +Local UVException(Isolate* isolate, int errorno, const char* syscall, const char* msg, const char* path) +{ + return UVException(isolate, errorno, syscall, msg, path, nullptr); +} +Local UVException(Isolate* isolate, int errorno, const char* syscall, const char* msg, const char* path, const char* dest) +{ + Environment* env = Environment::GetCurrent(isolate); + + if (!msg || !msg[0]) + msg = uv_strerror(errorno); + + Local js_code = OneByteString(isolate, uv_err_name(errorno)); + Local js_syscall = OneByteString(isolate, syscall); + Local js_path; + Local js_dest; + + Local js_msg = js_code; + js_msg = String::Concat(js_msg, FIXED_ONE_BYTE_STRING(isolate, ": ")); + js_msg = String::Concat(js_msg, OneByteString(isolate, msg)); + js_msg = String::Concat(js_msg, FIXED_ONE_BYTE_STRING(isolate, ", ")); + js_msg = String::Concat(js_msg, js_syscall); + + if (path != nullptr) { + js_path = StringFromPath(isolate, path); + + js_msg = String::Concat(js_msg, FIXED_ONE_BYTE_STRING(isolate, " '")); + js_msg = String::Concat(js_msg, js_path); + js_msg = String::Concat(js_msg, FIXED_ONE_BYTE_STRING(isolate, "'")); + } + + if (dest != nullptr) { + js_dest = StringFromPath(isolate, dest); + + js_msg = String::Concat(js_msg, FIXED_ONE_BYTE_STRING(isolate, " -> '")); + js_msg = String::Concat(js_msg, js_dest); + js_msg = String::Concat(js_msg, FIXED_ONE_BYTE_STRING(isolate, "'")); + } + + Local e = Exception::Error(js_msg)->ToObject(isolate); if (!env) return e; - // TODO(piscisaureus) errno should probably go; the user has no way of - // knowing which uv errno value maps to which error. - e->Set(env->errno_string(), Integer::New(isolate, errorno)); - e->Set(env->code_string(), js_code); - e->Set(env->syscall_string(), js_syscall); - if (!js_path.IsEmpty()) - e->Set(env->path_string(), js_path); - if (!js_dest.IsEmpty()) - e->Set(env->dest_string(), js_dest); + // TODO(piscisaureus) errno should probably go; the user has no way of + // knowing which uv errno value maps to which error. + e->Set(env->errno_string(), Integer::New(isolate, errorno)); + e->Set(env->code_string(), js_code); + e->Set(env->syscall_string(), js_syscall); + if (!js_path.IsEmpty()) + e->Set(env->path_string(), js_path); + if (!js_dest.IsEmpty()) + e->Set(env->dest_string(), js_dest); - return e; - } + return e; +} #ifdef _WIN32 - // Does about the same as strerror(), - // but supports all windows error messages - static const char *winapi_strerror(const int errorno, bool* must_free) { - char *errmsg = nullptr; - - FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, errorno, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&errmsg, 0, nullptr); - - if (errmsg) { - *must_free = true; - - // Remove trailing newlines - for (int i = strlen(errmsg) - 1; - i >= 0 && (errmsg[i] == '\n' || errmsg[i] == '\r'); i--) { - errmsg[i] = '\0'; - } - - return errmsg; - } - else { - // FormatMessage failed - *must_free = false; - return "Unknown error"; - } - } - - - Local WinapiErrnoException(Isolate* isolate, int errorno, const char* syscall, const char* msg, const char* path) { - Environment* env = Environment::GetCurrent(isolate); - Local e; - - bool must_free = false; - if (!msg || !msg[0]) { - msg = winapi_strerror(errorno, &must_free); - } - Local message = OneByteString(env->isolate(), msg); - - if (path) { - Local cons1 = - String::Concat(message, FIXED_ONE_BYTE_STRING(isolate, " '")); - Local cons2 = - String::Concat(cons1, String::NewFromUtf8(isolate, path)); - Local cons3 = - String::Concat(cons2, FIXED_ONE_BYTE_STRING(isolate, "'")); - e = Exception::Error(cons3); - } - else { - e = Exception::Error(message); - } +// Does about the same as strerror(), +// but supports all windows error messages +static const char* winapi_strerror(const int errorno, bool* must_free) +{ + char* errmsg = nullptr; + + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, errorno, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&errmsg, 0, nullptr); + + if (errmsg) { + *must_free = true; + + // Remove trailing newlines + for (int i = strlen(errmsg) - 1; + i >= 0 && (errmsg[i] == '\n' || errmsg[i] == '\r'); i--) { + errmsg[i] = '\0'; + } + + return errmsg; + } else { + // FormatMessage failed + *must_free = false; + return "Unknown error"; + } +} + +Local WinapiErrnoException(Isolate* isolate, int errorno, const char* syscall, const char* msg, const char* path) +{ + Environment* env = Environment::GetCurrent(isolate); + Local e; + + bool must_free = false; + if (!msg || !msg[0]) { + msg = winapi_strerror(errorno, &must_free); + } + Local message = OneByteString(env->isolate(), msg); + + if (path) { + Local cons1 = String::Concat(message, FIXED_ONE_BYTE_STRING(isolate, " '")); + Local cons2 = String::Concat(cons1, String::NewFromUtf8(isolate, path)); + Local cons3 = String::Concat(cons2, FIXED_ONE_BYTE_STRING(isolate, "'")); + e = Exception::Error(cons3); + } else { + e = Exception::Error(message); + } if (!env) return e; - Local obj = e->ToObject(env->isolate()); - obj->Set(env->errno_string(), Integer::New(isolate, errorno)); + Local obj = e->ToObject(env->isolate()); + obj->Set(env->errno_string(), Integer::New(isolate, errorno)); - if (path != nullptr) { - obj->Set(env->path_string(), String::NewFromUtf8(isolate, path)); - } + if (path != nullptr) { + obj->Set(env->path_string(), String::NewFromUtf8(isolate, path)); + } - if (syscall != nullptr) { - obj->Set(env->syscall_string(), OneByteString(isolate, syscall)); - } + if (syscall != nullptr) { + obj->Set(env->syscall_string(), OneByteString(isolate, syscall)); + } - if (must_free) - LocalFree((HLOCAL)msg); + if (must_free) + LocalFree((HLOCAL)msg); - return e; - } + return e; +} #endif +void* ArrayBufferAllocator::Allocate(size_t size) +{ + if (env_ == nullptr || !env_->array_buffer_allocator_info()->no_zero_fill() || zero_fill_all_buffers) + return node::Calloc(size, 1); + env_->array_buffer_allocator_info()->reset_fill_flag(); + return node::Malloc(size); +} - void* ArrayBufferAllocator::Allocate(size_t size) { - if (env_ == nullptr || - !env_->array_buffer_allocator_info()->no_zero_fill() || - zero_fill_all_buffers) - return node::Calloc(size, 1); - env_->array_buffer_allocator_info()->reset_fill_flag(); - return node::Malloc(size); - } - - static bool DomainHasErrorHandler(const Environment* env, const Local& domain) { - HandleScope scope(env->isolate()); - - Local domain_event_listeners_v = domain->Get(env->events_string()); - if (!domain_event_listeners_v->IsObject()) - return false; +static bool DomainHasErrorHandler(const Environment* env, const Local& domain) +{ + HandleScope scope(env->isolate()); - Local domain_event_listeners_o = - domain_event_listeners_v.As(); + Local domain_event_listeners_v = domain->Get(env->events_string()); + if (!domain_event_listeners_v->IsObject()) + return false; - Local domain_error_listeners_v = - domain_event_listeners_o->Get(env->error_string()); + Local domain_event_listeners_o = domain_event_listeners_v.As(); - if (domain_error_listeners_v->IsFunction() || - (domain_error_listeners_v->IsArray() && - domain_error_listeners_v.As()->Length() > 0)) - return true; + Local domain_error_listeners_v = domain_event_listeners_o->Get(env->error_string()); - return false; - } + if (domain_error_listeners_v->IsFunction() || (domain_error_listeners_v->IsArray() && domain_error_listeners_v.As()->Length() > 0)) + return true; - static bool DomainsStackHasErrorHandler(const Environment* env) { - HandleScope scope(env->isolate()); + return false; +} - if (!env->using_domains()) - return false; +static bool DomainsStackHasErrorHandler(const Environment* env) +{ + HandleScope scope(env->isolate()); - Local domains_stack_array = env->domains_stack_array().As(); - if (domains_stack_array->Length() == 0) - return false; + if (!env->using_domains()) + return false; - uint32_t domains_stack_length = domains_stack_array->Length(); - for (uint32_t i = domains_stack_length; i > 0; --i) { - Local domain_v = domains_stack_array->Get(i - 1); - if (!domain_v->IsObject()) - return false; + Local domains_stack_array = env->domains_stack_array().As(); + if (domains_stack_array->Length() == 0) + return false; - Local domain = domain_v.As(); - if (DomainHasErrorHandler(env, domain)) - return true; - } + uint32_t domains_stack_length = domains_stack_array->Length(); + for (uint32_t i = domains_stack_length; i > 0; --i) { + Local domain_v = domains_stack_array->Get(i - 1); + if (!domain_v->IsObject()) + return false; - return false; - } + Local domain = domain_v.As(); + if (DomainHasErrorHandler(env, domain)) + return true; + } + return false; +} - static bool ShouldAbortOnUncaughtException(Isolate* isolate) { - HandleScope scope(isolate); +static bool ShouldAbortOnUncaughtException(Isolate* isolate) +{ + HandleScope scope(isolate); - Environment* env = Environment::GetCurrent(isolate); + Environment* env = Environment::GetCurrent(isolate); if (!env) - return false; - Local process_object = env->process_object(); - Local emitting_top_level_domain_error_key = - env->emitting_top_level_domain_error_string(); - bool isEmittingTopLevelDomainError = - process_object->Get(emitting_top_level_domain_error_key)->BooleanValue(); - - return isEmittingTopLevelDomainError || !DomainsStackHasErrorHandler(env); - } + return false; + Local process_object = env->process_object(); + Local emitting_top_level_domain_error_key = env->emitting_top_level_domain_error_string(); + bool isEmittingTopLevelDomainError = process_object->Get(emitting_top_level_domain_error_key)->BooleanValue(); + return isEmittingTopLevelDomainError || !DomainsStackHasErrorHandler(env); +} - void SetupDomainUse(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); +void SetupDomainUse(const FunctionCallbackInfo& args) +{ + Environment* env = Environment::GetCurrent(args); if (!env) return; - if (env->using_domains()) - return; - env->set_using_domains(true); - - HandleScope scope(env->isolate()); - Local process_object = env->process_object(); + if (env->using_domains()) + return; + env->set_using_domains(true); - Local tick_callback_function_key = env->tick_domain_cb_string(); - Local tick_callback_function = - process_object->Get(tick_callback_function_key).As(); + HandleScope scope(env->isolate()); + Local process_object = env->process_object(); - if (!tick_callback_function->IsFunction()) { - fprintf(stderr, "process._tickDomainCallback assigned to non-function\n"); - ABORT(); - } + Local tick_callback_function_key = env->tick_domain_cb_string(); + Local tick_callback_function = process_object->Get(tick_callback_function_key).As(); - process_object->Set(env->tick_callback_string(), tick_callback_function); - env->set_tick_callback_function(tick_callback_function); + if (!tick_callback_function->IsFunction()) { + fprintf(stderr, "process._tickDomainCallback assigned to non-function\n"); + ABORT(); + } - CHECK(args[0]->IsArray()); - env->set_domain_array(args[0].As()); + process_object->Set(env->tick_callback_string(), tick_callback_function); + env->set_tick_callback_function(tick_callback_function); - CHECK(args[1]->IsArray()); - env->set_domains_stack_array(args[1].As()); + CHECK(args[0]->IsArray()); + env->set_domain_array(args[0].As()); - // Do a little housekeeping. - env->process_object()->Delete( - env->context(), - FIXED_ONE_BYTE_STRING(args.GetIsolate(), "_setupDomainUse")).FromJust(); + CHECK(args[1]->IsArray()); + env->set_domains_stack_array(args[1].As()); - uint32_t* const fields = env->domain_flag()->fields(); - uint32_t const fields_count = env->domain_flag()->fields_count(); + // Do a little housekeeping. + env->process_object()->Delete( + env->context(), + FIXED_ONE_BYTE_STRING(args.GetIsolate(), "_setupDomainUse")) + .FromJust(); - Local array_buffer = - ArrayBuffer::New(env->isolate(), fields, sizeof(*fields) * fields_count); + uint32_t* const fields = env->domain_flag()->fields(); + uint32_t const fields_count = env->domain_flag()->fields_count(); - args.GetReturnValue().Set(Uint32Array::New(array_buffer, 0, fields_count)); - } + Local array_buffer = ArrayBuffer::New(env->isolate(), fields, sizeof(*fields) * fields_count); - void RunMicrotasks(const FunctionCallbackInfo& args) { - args.GetIsolate()->RunMicrotasks(); - } + args.GetReturnValue().Set(Uint32Array::New(array_buffer, 0, fields_count)); +} +void RunMicrotasks(const FunctionCallbackInfo& args) +{ + args.GetIsolate()->RunMicrotasks(); +} - void SetupProcessObject(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); +void SetupProcessObject(const FunctionCallbackInfo& args) +{ + Environment* env = Environment::GetCurrent(args); if (!env) return; - CHECK(args[0]->IsFunction()); + CHECK(args[0]->IsFunction()); - env->set_push_values_to_array_function(args[0].As()); - env->process_object()->Delete( - env->context(), - FIXED_ONE_BYTE_STRING(env->isolate(), "_setupProcessObject")).FromJust(); - } + env->set_push_values_to_array_function(args[0].As()); + env->process_object()->Delete( + env->context(), + FIXED_ONE_BYTE_STRING(env->isolate(), "_setupProcessObject")) + .FromJust(); +} - - void SetupNextTick(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); +void SetupNextTick(const FunctionCallbackInfo& args) +{ + Environment* env = Environment::GetCurrent(args); if (!env) return; - CHECK(args[0]->IsFunction()); - CHECK(args[1]->IsObject()); + CHECK(args[0]->IsFunction()); + CHECK(args[1]->IsObject()); - env->set_tick_callback_function(args[0].As()); + env->set_tick_callback_function(args[0].As()); - env->SetMethod(args[1].As(), "runMicrotasks", RunMicrotasks); + env->SetMethod(args[1].As(), "runMicrotasks", RunMicrotasks); - // Do a little housekeeping. - env->process_object()->Delete( - env->context(), - FIXED_ONE_BYTE_STRING(args.GetIsolate(), "_setupNextTick")).FromJust(); + // Do a little housekeeping. + env->process_object()->Delete( + env->context(), + FIXED_ONE_BYTE_STRING(args.GetIsolate(), "_setupNextTick")) + .FromJust(); - // Values use to cross communicate with processNextTick. - uint32_t* const fields = env->tick_info()->fields(); - uint32_t const fields_count = env->tick_info()->fields_count(); + // Values use to cross communicate with processNextTick. + uint32_t* const fields = env->tick_info()->fields(); + uint32_t const fields_count = env->tick_info()->fields_count(); - Local array_buffer = - ArrayBuffer::New(env->isolate(), fields, sizeof(*fields) * fields_count); + Local array_buffer = ArrayBuffer::New(env->isolate(), fields, sizeof(*fields) * fields_count); - args.GetReturnValue().Set(Uint32Array::New(array_buffer, 0, fields_count)); - } + args.GetReturnValue().Set(Uint32Array::New(array_buffer, 0, fields_count)); +} - void PromiseRejectCallback(PromiseRejectMessage message) { - Local promise = message.GetPromise(); - Isolate* isolate = promise->GetIsolate(); - Local value = message.GetValue(); - Local event = Integer::New(isolate, message.GetEvent()); +void PromiseRejectCallback(PromiseRejectMessage message) +{ + Local promise = message.GetPromise(); + Isolate* isolate = promise->GetIsolate(); + Local value = message.GetValue(); + Local event = Integer::New(isolate, message.GetEvent()); - Environment* env = Environment::GetCurrent(isolate); + Environment* env = Environment::GetCurrent(isolate); if (!env) return; - Local callback = env->promise_reject_function(); + Local callback = env->promise_reject_function(); - if (value.IsEmpty()) - value = Undefined(isolate); + if (value.IsEmpty()) + value = Undefined(isolate); - Local args[] = { event, promise, value }; - Local process = env->process_object(); + Local args[] = { event, promise, value }; + Local process = env->process_object(); - callback->Call(process, arraysize(args), args); - } + callback->Call(process, arraysize(args), args); +} - void SetupPromises(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); +void SetupPromises(const FunctionCallbackInfo& args) +{ + Environment* env = Environment::GetCurrent(args); if (!env) return; - Isolate* isolate = env->isolate(); - - CHECK(args[0]->IsFunction()); - - isolate->SetPromiseRejectCallback(PromiseRejectCallback); - env->set_promise_reject_function(args[0].As()); - - env->process_object()->Delete( - env->context(), - FIXED_ONE_BYTE_STRING(args.GetIsolate(), "_setupPromises")).FromJust(); - } - - // Internal only. - Local MakeCallback(Environment* env, Local recv, const Local callback, int argc, Local argv[]) { - // If you hit this assertion, you forgot to enter the v8::Context first. - CHECK_EQ(env->context(), env->isolate()->GetCurrentContext()); - - Local pre_fn = env->async_hooks_pre_function(); - Local post_fn = env->async_hooks_post_function(); - Local object, domain; - bool ran_init_callback = false; - bool has_domain = false; - - Environment::AsyncCallbackScope callback_scope(env); - - // TODO(trevnorris): Adding "_asyncQueue" to the "this" in the init callback - // is a horrible way to detect usage. Rethink how detection should happen. - if (recv->IsObject()) { - object = recv.As(); - Local async_queue_v = object->Get(env->async_queue_string()); - if (async_queue_v->IsObject()) - ran_init_callback = true; - } - - if (env->using_domains()) { - CHECK(recv->IsObject()); - Local domain_v = object->Get(env->domain_string()); - has_domain = domain_v->IsObject(); - if (has_domain) { - domain = domain_v.As(); - if (domain->Get(env->disposed_string())->IsTrue()) - return Undefined(env->isolate()); - } - } - - if (has_domain) { - Local enter_v = domain->Get(env->enter_string()); - if (enter_v->IsFunction()) { - if (enter_v.As()->Call(domain, 0, nullptr).IsEmpty()) { - FatalError("node::MakeCallback", - "domain enter callback threw, please report this"); - } - } - } - - if (ran_init_callback && !pre_fn.IsEmpty()) { - TryCatch try_catch(env->isolate()); - MaybeLocal ar = pre_fn->Call(env->context(), object, 0, nullptr); - if (ar.IsEmpty()) { - ClearFatalExceptionHandlers(env); - FatalException(env->isolate(), try_catch); - return Local(); - } - } - - Local ret = callback->Call(recv, argc, argv); - - if (ran_init_callback && !post_fn.IsEmpty()) { - Local did_throw = Boolean::New(env->isolate(), ret.IsEmpty()); - // Currently there's no way to retrieve an uid from node::MakeCallback(). - // This needs to be fixed. - Local vals[] = - { Undefined(env->isolate()).As(), did_throw }; - TryCatch try_catch(env->isolate()); - MaybeLocal ar = - post_fn->Call(env->context(), object, arraysize(vals), vals); - if (ar.IsEmpty()) { - ClearFatalExceptionHandlers(env); - FatalException(env->isolate(), try_catch); - return Local(); - } - } - - if (ret.IsEmpty()) { - // NOTE: For backwards compatibility with public API we return Undefined() - // if the top level call threw. - return callback_scope.in_makecallback() ? - ret : Undefined(env->isolate()).As(); - } - - if (has_domain) { - Local exit_v = domain->Get(env->exit_string()); - if (exit_v->IsFunction()) { - if (exit_v.As()->Call(domain, 0, nullptr).IsEmpty()) { - FatalError("node::MakeCallback", - "domain exit callback threw, please report this"); - } - } - } - - if (callback_scope.in_makecallback()) { - return ret; - } - - Environment::TickInfo* tick_info = env->tick_info(); - - if (tick_info->length() == 0) { - env->isolate()->RunMicrotasks(); - } - - Local process = env->process_object(); - - if (tick_info->length() == 0) { - tick_info->set_index(0); - } - - if (env->tick_callback_function()->Call(process, 0, nullptr).IsEmpty()) { - return Undefined(env->isolate()); - } - - return ret; - } - Local MakeCallback(Environment* env, Local recv, uint32_t index, int argc, Local argv[]) { - Local cb_v = recv->Get(index); - CHECK(cb_v->IsFunction()); - return MakeCallback(env, recv.As(), cb_v.As(), argc, argv); - } - Local MakeCallback(Environment* env, Local recv, Local symbol, int argc, Local argv[]) { - Local cb_v = recv->Get(symbol); - CHECK(cb_v->IsFunction()); - return MakeCallback(env, recv.As(), cb_v.As(), argc, argv); - } - Local MakeCallback(Environment* env, Local recv, const char* method, int argc, Local argv[]) { - Local method_string = OneByteString(env->isolate(), method); - return MakeCallback(env, recv, method_string, argc, argv); - } - Local MakeCallback(Isolate* isolate, Local recv, const char* method, int argc, Local argv[]) { - EscapableHandleScope handle_scope(isolate); - Local context = recv->CreationContext(); - Environment* env = Environment::GetCurrent(context); - if (!env) - return Local(); - - Context::Scope context_scope(context); - return handle_scope.Escape( - Local::New(isolate, MakeCallback(env, recv, method, argc, argv))); - } - Local MakeCallback(Isolate* isolate, Local recv, Local symbol, int argc, Local argv[]) { - EscapableHandleScope handle_scope(isolate); - Local context = recv->CreationContext(); - Environment* env = Environment::GetCurrent(context); + Isolate* isolate = env->isolate(); + + CHECK(args[0]->IsFunction()); + + isolate->SetPromiseRejectCallback(PromiseRejectCallback); + env->set_promise_reject_function(args[0].As()); + + env->process_object()->Delete( + env->context(), + FIXED_ONE_BYTE_STRING(args.GetIsolate(), "_setupPromises")) + .FromJust(); +} + +// Internal only. +Local MakeCallback(Environment* env, Local recv, const Local callback, int argc, Local argv[]) +{ + // If you hit this assertion, you forgot to enter the v8::Context first. + CHECK_EQ(env->context(), env->isolate()->GetCurrentContext()); + + Local pre_fn = env->async_hooks_pre_function(); + Local post_fn = env->async_hooks_post_function(); + Local object, domain; + bool ran_init_callback = false; + bool has_domain = false; + + Environment::AsyncCallbackScope callback_scope(env); + + // TODO(trevnorris): Adding "_asyncQueue" to the "this" in the init callback + // is a horrible way to detect usage. Rethink how detection should happen. + if (recv->IsObject()) { + object = recv.As(); + Local async_queue_v = object->Get(env->async_queue_string()); + if (async_queue_v->IsObject()) + ran_init_callback = true; + } + + if (env->using_domains()) { + CHECK(recv->IsObject()); + Local domain_v = object->Get(env->domain_string()); + has_domain = domain_v->IsObject(); + if (has_domain) { + domain = domain_v.As(); + if (domain->Get(env->disposed_string())->IsTrue()) + return Undefined(env->isolate()); + } + } + + if (has_domain) { + Local enter_v = domain->Get(env->enter_string()); + if (enter_v->IsFunction()) { + if (enter_v.As()->Call(domain, 0, nullptr).IsEmpty()) { + FatalError("node::MakeCallback", + "domain enter callback threw, please report this"); + } + } + } + + if (ran_init_callback && !pre_fn.IsEmpty()) { + TryCatch try_catch(env->isolate()); + MaybeLocal ar = pre_fn->Call(env->context(), object, 0, nullptr); + if (ar.IsEmpty()) { + ClearFatalExceptionHandlers(env); + FatalException(env->isolate(), try_catch); + return Local(); + } + } + + Local ret = callback->Call(recv, argc, argv); + + if (ran_init_callback && !post_fn.IsEmpty()) { + Local did_throw = Boolean::New(env->isolate(), ret.IsEmpty()); + // Currently there's no way to retrieve an uid from node::MakeCallback(). + // This needs to be fixed. + Local vals[] = { Undefined(env->isolate()).As(), did_throw }; + TryCatch try_catch(env->isolate()); + MaybeLocal ar = post_fn->Call(env->context(), object, arraysize(vals), vals); + if (ar.IsEmpty()) { + ClearFatalExceptionHandlers(env); + FatalException(env->isolate(), try_catch); + return Local(); + } + } + + if (ret.IsEmpty()) { + // NOTE: For backwards compatibility with public API we return Undefined() + // if the top level call threw. + return callback_scope.in_makecallback() ? ret : Undefined(env->isolate()).As(); + } + + if (has_domain) { + Local exit_v = domain->Get(env->exit_string()); + if (exit_v->IsFunction()) { + if (exit_v.As()->Call(domain, 0, nullptr).IsEmpty()) { + FatalError("node::MakeCallback", + "domain exit callback threw, please report this"); + } + } + } + + if (callback_scope.in_makecallback()) { + return ret; + } + + Environment::TickInfo* tick_info = env->tick_info(); + + if (tick_info->length() == 0) { + env->isolate()->RunMicrotasks(); + } + + Local process = env->process_object(); + + if (tick_info->length() == 0) { + tick_info->set_index(0); + } + + if (env->tick_callback_function()->Call(process, 0, nullptr).IsEmpty()) { + return Undefined(env->isolate()); + } + + return ret; +} +Local MakeCallback(Environment* env, Local recv, uint32_t index, int argc, Local argv[]) +{ + Local cb_v = recv->Get(index); + CHECK(cb_v->IsFunction()); + return MakeCallback(env, recv.As(), cb_v.As(), argc, argv); +} +Local MakeCallback(Environment* env, Local recv, Local symbol, int argc, Local argv[]) +{ + Local cb_v = recv->Get(symbol); + CHECK(cb_v->IsFunction()); + return MakeCallback(env, recv.As(), cb_v.As(), argc, argv); +} +Local MakeCallback(Environment* env, Local recv, const char* method, int argc, Local argv[]) +{ + Local method_string = OneByteString(env->isolate(), method); + return MakeCallback(env, recv, method_string, argc, argv); +} +Local MakeCallback(Isolate* isolate, Local recv, const char* method, int argc, Local argv[]) +{ + EscapableHandleScope handle_scope(isolate); + Local context = recv->CreationContext(); + Environment* env = Environment::GetCurrent(context); if (!env) - return Local(); - Context::Scope context_scope(context); - return handle_scope.Escape( - Local::New(isolate, MakeCallback(env, recv, symbol, argc, argv))); - } - Local MakeCallback(Isolate* isolate, Local recv, Local callback, int argc, Local argv[]) { - EscapableHandleScope handle_scope(isolate); - Local context = recv->CreationContext(); - Environment* env = Environment::GetCurrent(context); + return Local(); + + Context::Scope context_scope(context); + return handle_scope.Escape( + Local::New(isolate, MakeCallback(env, recv, method, argc, argv))); +} +Local MakeCallback(Isolate* isolate, Local recv, Local symbol, int argc, Local argv[]) +{ + EscapableHandleScope handle_scope(isolate); + Local context = recv->CreationContext(); + Environment* env = Environment::GetCurrent(context); if (!env) - return Local(); - Context::Scope context_scope(context); - return handle_scope.Escape(Local::New( - isolate, - MakeCallback(env, recv.As(), callback, argc, argv))); - } - - - enum encoding ParseEncoding(const char* encoding, enum encoding default_encoding) { - switch (encoding[0]) { - case 'u': - // utf8, utf16le - if (encoding[1] == 't' && encoding[2] == 'f') { - // Skip `-` - encoding += encoding[3] == '-' ? 4 : 3; - if (encoding[0] == '8' && encoding[1] == '\0') - return UTF8; - if (strncmp(encoding, "16le", 4) == 0) - return UCS2; - - // ucs2 - } - else if (encoding[1] == 'c' && encoding[2] == 's') { - encoding += encoding[3] == '-' ? 4 : 3; - if (encoding[0] == '2' && encoding[1] == '\0') - return UCS2; - } - break; - case 'l': - // latin1 - if (encoding[1] == 'a') { - if (strncmp(encoding + 2, "tin1", 4) == 0) - return LATIN1; - } - break; - case 'b': - // binary - if (encoding[1] == 'i') { - if (strncmp(encoding + 2, "nary", 4) == 0) - return LATIN1; - - // buffer - } - else if (encoding[1] == 'u') { - if (strncmp(encoding + 2, "ffer", 4) == 0) - return BUFFER; - } - break; - case '\0': - return default_encoding; - default: - break; - } - - if (StringEqualNoCase(encoding, "utf8")) { - return UTF8; - } - else if (StringEqualNoCase(encoding, "utf-8")) { - return UTF8; - } - else if (StringEqualNoCase(encoding, "ascii")) { - return ASCII; - } - else if (StringEqualNoCase(encoding, "base64")) { - return BASE64; - } - else if (StringEqualNoCase(encoding, "ucs2")) { - return UCS2; - } - else if (StringEqualNoCase(encoding, "ucs-2")) { - return UCS2; - } - else if (StringEqualNoCase(encoding, "utf16le")) { - return UCS2; - } - else if (StringEqualNoCase(encoding, "utf-16le")) { - return UCS2; - } - else if (StringEqualNoCase(encoding, "latin1")) { - return LATIN1; - } - else if (StringEqualNoCase(encoding, "binary")) { - return LATIN1; // BINARY is a deprecated alias of LATIN1. - } - else if (StringEqualNoCase(encoding, "buffer")) { - return BUFFER; - } - else if (StringEqualNoCase(encoding, "hex")) { - return HEX; - } - else { - return default_encoding; - } - } - - - enum encoding ParseEncoding(Isolate* isolate, Local encoding_v, enum encoding default_encoding) { - if (!encoding_v->IsString()) - return default_encoding; - - node::Utf8Value encoding(isolate, encoding_v); - - return ParseEncoding(*encoding, default_encoding); - } - - Local Encode(Isolate* isolate, const char* buf, size_t len, enum encoding encoding) { - CHECK_NE(encoding, UCS2); - return StringBytes::Encode(isolate, buf, len, encoding); - } - - Local Encode(Isolate* isolate, const uint16_t* buf, size_t len) { - return StringBytes::Encode(isolate, buf, len); - } - - // Returns -1 if the handle was not valid for decoding - ssize_t DecodeBytes(Isolate* isolate, Local val, enum encoding encoding) { - HandleScope scope(isolate); - - return StringBytes::Size(isolate, val, encoding); - } - - // Returns number of bytes written. - ssize_t DecodeWrite(Isolate* isolate, char* buf, size_t buflen, Local val, enum encoding encoding) { - return StringBytes::Write(isolate, buf, buflen, val, encoding, nullptr); - } - - bool IsExceptionDecorated(Environment* env, Local er) { - if (!er.IsEmpty() && er->IsObject()) { - Local err_obj = er.As(); - auto maybe_value = - err_obj->GetPrivate(env->context(), env->decorated_private_symbol()); - Local decorated; - return maybe_value.ToLocal(&decorated) && decorated->IsTrue(); - } - return false; - } - - void AppendExceptionLine(Environment* env, Local er, Local message, enum ErrorHandlingMode mode) { - if (message.IsEmpty()) - return; - - HandleScope scope(env->isolate()); - Local err_obj; - if (!er.IsEmpty() && er->IsObject()) { - err_obj = er.As(); - - auto context = env->context(); - auto processed_private_symbol = env->processed_private_symbol(); - // Do it only once per message - if (err_obj->HasPrivate(context, processed_private_symbol).FromJust()) - return; - err_obj->SetPrivate( - context, - processed_private_symbol, - True(env->isolate())); - } - - // Print (filename):(line number): (message). - node::Utf8Value filename(env->isolate(), message->GetScriptResourceName()); - const char* filename_string = *filename; - int linenum = message->GetLineNumber(); - // Print line of source code. - node::Utf8Value sourceline(env->isolate(), message->GetSourceLine()); - const char* sourceline_string = *sourceline; - - // Because of how node modules work, all scripts are wrapped with a - // "function (module, exports, __filename, ...) {" - // to provide script local variables. - // - // When reporting errors on the first line of a script, this wrapper - // function is leaked to the user. There used to be a hack here to - // truncate off the first 62 characters, but it caused numerous other - // problems when vm.runIn*Context() methods were used for non-module - // code. - // - // If we ever decide to re-instate such a hack, the following steps - // must be taken: - // - // 1. Pass a flag around to say "this code was wrapped" - // 2. Update the stack frame output so that it is also correct. - // - // It would probably be simpler to add a line rather than add some - // number of characters to the first line, since V8 truncates the - // sourceline to 78 characters, and we end up not providing very much - // useful debugging info to the user if we remove 62 characters. - - int start = message->GetStartColumn(env->context()).FromMaybe(0); - int end = message->GetEndColumn(env->context()).FromMaybe(0); - - char arrow[1024]; - int max_off = sizeof(arrow) - 2; - - int off = snprintf(arrow, - sizeof(arrow), - "%s:%i\n%s\n", - filename_string, - linenum, - sourceline_string); - CHECK_GE(off, 0); - if (off > max_off) { - off = max_off; - } - - // Print wavy underline (GetUnderline is deprecated). - for (int i = 0; i < start; i++) { - if (sourceline_string[i] == '\0' || off >= max_off) { - break; - } - CHECK_LT(off, max_off); - arrow[off++] = (sourceline_string[i] == '\t') ? '\t' : ' '; - } - for (int i = start; i < end; i++) { - if (sourceline_string[i] == '\0' || off >= max_off) { - break; - } - CHECK_LT(off, max_off); - arrow[off++] = '^'; - } - CHECK_LE(off, max_off); - arrow[off] = '\n'; - arrow[off + 1] = '\0'; - - Local arrow_str = String::NewFromUtf8(env->isolate(), arrow); - - const bool can_set_arrow = !arrow_str.IsEmpty() && !err_obj.IsEmpty(); - // If allocating arrow_str failed, print it out. There's not much else to do. - // If it's not an error, but something needs to be printed out because - // it's a fatal exception, also print it out from here. - // Otherwise, the arrow property will be attached to the object and handled - // by the caller. - if (!can_set_arrow || (mode == FATAL_ERROR && !err_obj->IsNativeError())) { - if (env->printed_error()) - return; - env->set_printed_error(true); - //zero - //uv_tty_reset_mode(); - PrintErrorString("\n%s", arrow); - return; - } - - CHECK(err_obj->SetPrivate( - env->context(), - env->arrow_message_private_symbol(), - arrow_str).FromMaybe(false)); - } - - - static void ReportException(Environment* env, Local er, Local message) { - HandleScope scope(env->isolate()); - - AppendExceptionLine(env, er, message, FATAL_ERROR); - - Local trace_value; - Local arrow; - const bool decorated = IsExceptionDecorated(env, er); - - if (er->IsUndefined() || er->IsNull()) { - trace_value = Undefined(env->isolate()); - } - else { - Local err_obj = er->ToObject(env->isolate()); - - trace_value = err_obj->Get(env->stack_string()); - arrow = - err_obj->GetPrivate( - env->context(), - env->arrow_message_private_symbol()).ToLocalChecked(); - } - - node::Utf8Value trace(env->isolate(), trace_value); - - // range errors have a trace member set to undefined - if (trace.length() > 0 && !trace_value->IsUndefined()) { - if (arrow.IsEmpty() || !arrow->IsString() || decorated) { - PrintErrorString("%s\n", *trace); - } - else { - node::Utf8Value arrow_string(env->isolate(), arrow); - PrintErrorString("%s\n%s\n", *arrow_string, *trace); - } - } - else { - // this really only happens for RangeErrors, since they're the only - // kind that won't have all this info in the trace, or when non-Error - // objects are thrown manually. - Local message; - Local name; - - if (er->IsObject()) { - Local err_obj = er.As(); - message = err_obj->Get(env->message_string()); - name = err_obj->Get(FIXED_ONE_BYTE_STRING(env->isolate(), "name")); - } - - if (message.IsEmpty() || - message->IsUndefined() || - name.IsEmpty() || - name->IsUndefined()) { - // Not an error object. Just print as-is. - String::Utf8Value message(er); - - PrintErrorString("%s\n", *message ? *message : - ""); - } - else { - node::Utf8Value name_string(env->isolate(), name); - node::Utf8Value message_string(env->isolate(), message); - - if (arrow.IsEmpty() || !arrow->IsString() || decorated) { - PrintErrorString("%s: %s\n", *name_string, *message_string); - } - else { - node::Utf8Value arrow_string(env->isolate(), arrow); - PrintErrorString("%s\n%s: %s\n", - *arrow_string, - *name_string, - *message_string); - } - } - } - - fflush(stderr); - } - static void ReportException(Environment* env, const TryCatch& try_catch) { - ReportException(env, try_catch.Exception(), try_catch.Message()); - } - - - // Executes a str within the current v8 context. - // 执行js - Local ExecuteString(Environment* env, Local source, Local filename) { - EscapableHandleScope scope(env->isolate()); - TryCatch try_catch(env->isolate()); - - // try_catch must be nonverbose to disable FatalException() handler, - // we will handle exceptions ourself. - try_catch.SetVerbose(false); - - ScriptOrigin origin(filename); - MaybeLocal script = - v8::Script::Compile(env->context(), source, &origin); - if (script.IsEmpty()) { - ReportException(env, try_catch); - //exit(3); - } - - Local result = script.ToLocalChecked()->Run(); - if (result.IsEmpty()) { - ReportException(env, try_catch); - //exit(4); - } - - return scope.Escape(result); - } - - static void GetActiveRequests(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); + return Local(); + Context::Scope context_scope(context); + return handle_scope.Escape( + Local::New(isolate, MakeCallback(env, recv, symbol, argc, argv))); +} +Local MakeCallback(Isolate* isolate, Local recv, Local callback, int argc, Local argv[]) +{ + EscapableHandleScope handle_scope(isolate); + Local context = recv->CreationContext(); + Environment* env = Environment::GetCurrent(context); if (!env) + return Local(); + Context::Scope context_scope(context); + return handle_scope.Escape(Local::New( + isolate, + MakeCallback(env, recv.As(), callback, argc, argv))); +} + +enum encoding ParseEncoding(const char* encoding, enum encoding default_encoding) +{ + switch (encoding[0]) { + case 'u': + // utf8, utf16le + if (encoding[1] == 't' && encoding[2] == 'f') { + // Skip `-` + encoding += encoding[3] == '-' ? 4 : 3; + if (encoding[0] == '8' && encoding[1] == '\0') + return UTF8; + if (strncmp(encoding, "16le", 4) == 0) + return UCS2; + + // ucs2 + } else if (encoding[1] == 'c' && encoding[2] == 's') { + encoding += encoding[3] == '-' ? 4 : 3; + if (encoding[0] == '2' && encoding[1] == '\0') + return UCS2; + } + break; + case 'l': + // latin1 + if (encoding[1] == 'a') { + if (strncmp(encoding + 2, "tin1", 4) == 0) + return LATIN1; + } + break; + case 'b': + // binary + if (encoding[1] == 'i') { + if (strncmp(encoding + 2, "nary", 4) == 0) + return LATIN1; + + // buffer + } else if (encoding[1] == 'u') { + if (strncmp(encoding + 2, "ffer", 4) == 0) + return BUFFER; + } + break; + case '\0': + return default_encoding; + default: + break; + } + + if (StringEqualNoCase(encoding, "utf8")) { + return UTF8; + } else if (StringEqualNoCase(encoding, "utf-8")) { + return UTF8; + } else if (StringEqualNoCase(encoding, "ascii")) { + return ASCII; + } else if (StringEqualNoCase(encoding, "base64")) { + return BASE64; + } else if (StringEqualNoCase(encoding, "ucs2")) { + return UCS2; + } else if (StringEqualNoCase(encoding, "ucs-2")) { + return UCS2; + } else if (StringEqualNoCase(encoding, "utf16le")) { + return UCS2; + } else if (StringEqualNoCase(encoding, "utf-16le")) { + return UCS2; + } else if (StringEqualNoCase(encoding, "latin1")) { + return LATIN1; + } else if (StringEqualNoCase(encoding, "binary")) { + return LATIN1; // BINARY is a deprecated alias of LATIN1. + } else if (StringEqualNoCase(encoding, "buffer")) { + return BUFFER; + } else if (StringEqualNoCase(encoding, "hex")) { + return HEX; + } else { + return default_encoding; + } +} + +enum encoding ParseEncoding(Isolate* isolate, Local encoding_v, enum encoding default_encoding) +{ + if (!encoding_v->IsString()) + return default_encoding; + + node::Utf8Value encoding(isolate, encoding_v); + + return ParseEncoding(*encoding, default_encoding); +} + +Local Encode(Isolate* isolate, const char* buf, size_t len, enum encoding encoding) +{ + CHECK_NE(encoding, UCS2); + return StringBytes::Encode(isolate, buf, len, encoding); +} + +Local Encode(Isolate* isolate, const uint16_t* buf, size_t len) +{ + return StringBytes::Encode(isolate, buf, len); +} + +// Returns -1 if the handle was not valid for decoding +ssize_t DecodeBytes(Isolate* isolate, Local val, enum encoding encoding) +{ + HandleScope scope(isolate); + + return StringBytes::Size(isolate, val, encoding); +} + +// Returns number of bytes written. +ssize_t DecodeWrite(Isolate* isolate, char* buf, size_t buflen, Local val, enum encoding encoding) +{ + return StringBytes::Write(isolate, buf, buflen, val, encoding, nullptr); +} + +bool IsExceptionDecorated(Environment* env, Local er) +{ + if (!er.IsEmpty() && er->IsObject()) { + Local err_obj = er.As(); + auto maybe_value = err_obj->GetPrivate(env->context(), env->decorated_private_symbol()); + Local decorated; + return maybe_value.ToLocal(&decorated) && decorated->IsTrue(); + } + return false; +} + +void AppendExceptionLine(Environment* env, Local er, Local message, enum ErrorHandlingMode mode) +{ + if (message.IsEmpty()) return; - Local ary = Array::New(args.GetIsolate()); - Local ctx = env->context(); - Local fn = env->push_values_to_array_function(); - Local argv[NODE_PUSH_VAL_TO_ARRAY_MAX]; - size_t idx = 0; - - for (auto w : *env->req_wrap_queue()) { - if (w->persistent().IsEmpty()) - continue; - argv[idx] = w->object(); - if (++idx >= arraysize(argv)) { - fn->Call(ctx, ary, idx, argv).ToLocalChecked(); - idx = 0; - } - } - - if (idx > 0) { - fn->Call(ctx, ary, idx, argv).ToLocalChecked(); - } - - args.GetReturnValue().Set(ary); - } - - - // Non-static, friend of HandleWrap. Could have been a HandleWrap method but - // implemented here for consistency with GetActiveRequests(). - void GetActiveHandles(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - if (!env) + HandleScope scope(env->isolate()); + Local err_obj; + if (!er.IsEmpty() && er->IsObject()) { + err_obj = er.As(); + + auto context = env->context(); + auto processed_private_symbol = env->processed_private_symbol(); + // Do it only once per message + if (err_obj->HasPrivate(context, processed_private_symbol).FromJust()) + return; + err_obj->SetPrivate( + context, + processed_private_symbol, + True(env->isolate())); + } + + // Print (filename):(line number): (message). + node::Utf8Value filename(env->isolate(), message->GetScriptResourceName()); + const char* filename_string = *filename; + int linenum = message->GetLineNumber(); + // Print line of source code. + node::Utf8Value sourceline(env->isolate(), message->GetSourceLine()); + const char* sourceline_string = *sourceline; + + // Because of how node modules work, all scripts are wrapped with a + // "function (module, exports, __filename, ...) {" + // to provide script local variables. + // + // When reporting errors on the first line of a script, this wrapper + // function is leaked to the user. There used to be a hack here to + // truncate off the first 62 characters, but it caused numerous other + // problems when vm.runIn*Context() methods were used for non-module + // code. + // + // If we ever decide to re-instate such a hack, the following steps + // must be taken: + // + // 1. Pass a flag around to say "this code was wrapped" + // 2. Update the stack frame output so that it is also correct. + // + // It would probably be simpler to add a line rather than add some + // number of characters to the first line, since V8 truncates the + // sourceline to 78 characters, and we end up not providing very much + // useful debugging info to the user if we remove 62 characters. + + int start = message->GetStartColumn(env->context()).FromMaybe(0); + int end = message->GetEndColumn(env->context()).FromMaybe(0); + + char arrow[1024]; + int max_off = sizeof(arrow) - 2; + + int off = snprintf(arrow, + sizeof(arrow), + "%s:%i\n%s\n", + filename_string, + linenum, + sourceline_string); + CHECK_GE(off, 0); + if (off > max_off) { + off = max_off; + } + + // Print wavy underline (GetUnderline is deprecated). + for (int i = 0; i < start; i++) { + if (sourceline_string[i] == '\0' || off >= max_off) { + break; + } + CHECK_LT(off, max_off); + arrow[off++] = (sourceline_string[i] == '\t') ? '\t' : ' '; + } + for (int i = start; i < end; i++) { + if (sourceline_string[i] == '\0' || off >= max_off) { + break; + } + CHECK_LT(off, max_off); + arrow[off++] = '^'; + } + CHECK_LE(off, max_off); + arrow[off] = '\n'; + arrow[off + 1] = '\0'; + + Local arrow_str = String::NewFromUtf8(env->isolate(), arrow); + + const bool can_set_arrow = !arrow_str.IsEmpty() && !err_obj.IsEmpty(); + // If allocating arrow_str failed, print it out. There's not much else to do. + // If it's not an error, but something needs to be printed out because + // it's a fatal exception, also print it out from here. + // Otherwise, the arrow property will be attached to the object and handled + // by the caller. + if (!can_set_arrow || (mode == FATAL_ERROR && !err_obj->IsNativeError())) { + if (env->printed_error()) + return; + env->set_printed_error(true); + //zero + //uv_tty_reset_mode(); + PrintErrorString("\n%s", arrow); return; + } - Local ary = Array::New(env->isolate()); - Local ctx = env->context(); - Local fn = env->push_values_to_array_function(); - Local argv[NODE_PUSH_VAL_TO_ARRAY_MAX]; - size_t idx = 0; - - Local owner_sym = env->owner_string(); - - for (auto w : *env->handle_wrap_queue()) { - if (w->persistent().IsEmpty() || !HandleWrap::HasRef(w)) - continue; - Local object = w->object(); - Local owner = object->Get(owner_sym); - if (owner->IsUndefined()) - owner = object; - argv[idx] = owner; - if (++idx >= arraysize(argv)) { - fn->Call(ctx, ary, idx, argv).ToLocalChecked(); - idx = 0; - } - } - if (idx > 0) { - fn->Call(ctx, ary, idx, argv).ToLocalChecked(); - } - - args.GetReturnValue().Set(ary); - } - - - NO_RETURN void Abort() { - DumpBacktrace(stderr); - fflush(stderr); - //ABORT_NO_BACKTRACE(); - } - - - NO_RETURN void Assert(const char* const (*args)[4]) { - auto filename = (*args)[0]; - auto linenum = (*args)[1]; - auto message = (*args)[2]; - auto function = (*args)[3]; - - char exepath[256]; - size_t exepath_size = sizeof(exepath); - if (uv_exepath(exepath, &exepath_size)) - snprintf(exepath, sizeof(exepath), "node"); - - char pid[12] = { 0 }; -#ifndef _WIN32 - snprintf(pid, sizeof(pid), "[%u]", getpid()); -#endif + CHECK(err_obj->SetPrivate( + env->context(), + env->arrow_message_private_symbol(), + arrow_str) + .FromMaybe(false)); +} + +static void ReportException(Environment* env, Local er, Local message) +{ + HandleScope scope(env->isolate()); + + AppendExceptionLine(env, er, message, FATAL_ERROR); + + Local trace_value; + Local arrow; + const bool decorated = IsExceptionDecorated(env, er); + + if (er->IsUndefined() || er->IsNull()) { + trace_value = Undefined(env->isolate()); + } else { + Local err_obj = er->ToObject(env->isolate()); + + trace_value = err_obj->Get(env->stack_string()); + arrow = err_obj->GetPrivate( + env->context(), + env->arrow_message_private_symbol()) + .ToLocalChecked(); + } + + node::Utf8Value trace(env->isolate(), trace_value); - fprintf(stderr, "%s%s: %s:%s:%s%s Assertion `%s' failed.\n", - exepath, pid, filename, linenum, - function, *function ? ":" : "", message); - fflush(stderr); + // range errors have a trace member set to undefined + if (trace.length() > 0 && !trace_value->IsUndefined()) { + if (arrow.IsEmpty() || !arrow->IsString() || decorated) { + PrintErrorString("%s\n", *trace); + } else { + node::Utf8Value arrow_string(env->isolate(), arrow); + PrintErrorString("%s\n%s\n", *arrow_string, *trace); + } + } else { + // this really only happens for RangeErrors, since they're the only + // kind that won't have all this info in the trace, or when non-Error + // objects are thrown manually. + Local message; + Local name; + + if (er->IsObject()) { + Local err_obj = er.As(); + message = err_obj->Get(env->message_string()); + name = err_obj->Get(FIXED_ONE_BYTE_STRING(env->isolate(), "name")); + } - Abort(); - } + if (message.IsEmpty() || message->IsUndefined() || name.IsEmpty() || name->IsUndefined()) { + // Not an error object. Just print as-is. + String::Utf8Value message(er); + + PrintErrorString("%s\n", *message ? *message : ""); + } else { + node::Utf8Value name_string(env->isolate(), name); + node::Utf8Value message_string(env->isolate(), message); + + if (arrow.IsEmpty() || !arrow->IsString() || decorated) { + PrintErrorString("%s: %s\n", *name_string, *message_string); + } else { + node::Utf8Value arrow_string(env->isolate(), arrow); + PrintErrorString("%s\n%s: %s\n", + *arrow_string, + *name_string, + *message_string); + } + } + } + fflush(stderr); +} +static void ReportException(Environment* env, const TryCatch& try_catch) +{ + ReportException(env, try_catch.Exception(), try_catch.Message()); +} + +// Executes a str within the current v8 context. +// 执行js +Local ExecuteString(Environment* env, Local source, Local filename) +{ + EscapableHandleScope scope(env->isolate()); + TryCatch try_catch(env->isolate()); + + // try_catch must be nonverbose to disable FatalException() handler, + // we will handle exceptions ourself. + try_catch.SetVerbose(false); + + ScriptOrigin origin(filename); + MaybeLocal script = v8::Script::Compile(env->context(), source, &origin); + if (script.IsEmpty()) { + ReportException(env, try_catch); + //exit(3); + } - static void Abort(const FunctionCallbackInfo& args) { - Abort(); - } + Local result = script.ToLocalChecked()->Run(); + if (result.IsEmpty()) { + ReportException(env, try_catch); + //exit(4); + } + return scope.Escape(result); +} - static void Chdir(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); +static void GetActiveRequests(const FunctionCallbackInfo& args) +{ + Environment* env = Environment::GetCurrent(args); if (!env) return; - if (args.Length() != 1 || !args[0]->IsString()) { - return env->ThrowTypeError("Bad argument."); - } + Local ary = Array::New(args.GetIsolate()); + Local ctx = env->context(); + Local fn = env->push_values_to_array_function(); + Local argv[NODE_PUSH_VAL_TO_ARRAY_MAX]; + size_t idx = 0; + + for (auto w : *env->req_wrap_queue()) { + if (w->persistent().IsEmpty()) + continue; + argv[idx] = w->object(); + if (++idx >= arraysize(argv)) { + fn->Call(ctx, ary, idx, argv).ToLocalChecked(); + idx = 0; + } + } - node::Utf8Value path(args.GetIsolate(), args[0]); - int err = uv_chdir(*path); - if (err) { - return env->ThrowUVException(err, "uv_chdir"); - } - } + if (idx > 0) { + fn->Call(ctx, ary, idx, argv).ToLocalChecked(); + } + args.GetReturnValue().Set(ary); +} - static void Cwd(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); +// Non-static, friend of HandleWrap. Could have been a HandleWrap method but +// implemented here for consistency with GetActiveRequests(). +void GetActiveHandles(const FunctionCallbackInfo& args) +{ + Environment* env = Environment::GetCurrent(args); if (!env) return; -#ifdef _WIN32 - /* MAX_PATH is in characters, not bytes. Make sure we have enough headroom. */ - char buf[MAX_PATH * 4]; -#else - char buf[PATH_MAX]; + + Local ary = Array::New(env->isolate()); + Local ctx = env->context(); + Local fn = env->push_values_to_array_function(); + Local argv[NODE_PUSH_VAL_TO_ARRAY_MAX]; + size_t idx = 0; + + Local owner_sym = env->owner_string(); + + for (auto w : *env->handle_wrap_queue()) { + if (w->persistent().IsEmpty() || !HandleWrap::HasRef(w)) + continue; + Local object = w->object(); + Local owner = object->Get(owner_sym); + if (owner->IsUndefined()) + owner = object; + argv[idx] = owner; + if (++idx >= arraysize(argv)) { + fn->Call(ctx, ary, idx, argv).ToLocalChecked(); + idx = 0; + } + } + if (idx > 0) { + fn->Call(ctx, ary, idx, argv).ToLocalChecked(); + } + + args.GetReturnValue().Set(ary); +} + +NO_RETURN void Abort() +{ + DumpBacktrace(stderr); + fflush(stderr); + //ABORT_NO_BACKTRACE(); +} + +NO_RETURN void Assert(const char* const (*args)[4]) +{ + auto filename = (*args)[0]; + auto linenum = (*args)[1]; + auto message = (*args)[2]; + auto function = (*args)[3]; + + char exepath[256]; + size_t exepath_size = sizeof(exepath); + if (uv_exepath(exepath, &exepath_size)) + snprintf(exepath, sizeof(exepath), "node"); + + char pid[12] = { 0 }; +#ifndef _WIN32 + snprintf(pid, sizeof(pid), "[%u]", getpid()); #endif - size_t cwd_len = sizeof(buf); - int err = uv_cwd(buf, &cwd_len); - if (err) { - return env->ThrowUVException(err, "uv_cwd"); - } + fprintf(stderr, "%s%s: %s:%s:%s%s Assertion `%s' failed.\n", + exepath, pid, filename, linenum, + function, *function ? ":" : "", message); + fflush(stderr); - Local cwd = String::NewFromUtf8(env->isolate(), - buf, - String::kNormalString, - cwd_len); - args.GetReturnValue().Set(cwd); - } + Abort(); +} +static void Abort(const FunctionCallbackInfo& args) +{ + Abort(); +} - static void Umask(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); +static void Chdir(const FunctionCallbackInfo& args) +{ + Environment* env = Environment::GetCurrent(args); if (!env) return; - uint32_t old; - - if (args.Length() < 1 || args[0]->IsUndefined()) { - old = umask(0); - umask(static_cast(old)); - } - else if (!args[0]->IsInt32() && !args[0]->IsString()) { - return env->ThrowTypeError("argument must be an integer or octal string."); - } - else { - int oct; - if (args[0]->IsInt32()) { - oct = args[0]->Uint32Value(); - } - else { - oct = 0; - node::Utf8Value str(env->isolate(), args[0]); - - // Parse the octal string. - for (size_t i = 0; i < str.length(); i++) { - char c = (*str)[i]; - if (c > '7' || c < '0') { - return env->ThrowTypeError("invalid octal string"); - } - oct *= 8; - oct += c - '0'; - } - } - old = umask(static_cast(oct)); - } - - args.GetReturnValue().Set(old); - } - - - void Exit(const FunctionCallbackInfo& args) { - exit(args[0]->Int32Value()); - } - - - static void Uptime(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); + + if (args.Length() != 1 || !args[0]->IsString()) { + return env->ThrowTypeError("Bad argument."); + } + + node::Utf8Value path(args.GetIsolate(), args[0]); + int err = uv_chdir(*path); + if (err) { + return env->ThrowUVException(err, "uv_chdir"); + } +} + +static void Cwd(const FunctionCallbackInfo& args) +{ + Environment* env = Environment::GetCurrent(args); if (!env) return; - double uptime; +#ifdef _WIN32 + /* MAX_PATH is in characters, not bytes. Make sure we have enough headroom. */ + char buf[MAX_PATH * 4]; +#else + char buf[PATH_MAX]; +#endif - uv_update_time(env->event_loop()); - uptime = uv_now(env->event_loop()) - prog_start_time; + size_t cwd_len = sizeof(buf); + int err = uv_cwd(buf, &cwd_len); + if (err) { + return env->ThrowUVException(err, "uv_cwd"); + } - args.GetReturnValue().Set(Number::New(env->isolate(), uptime / 1000)); - } + Local cwd = String::NewFromUtf8(env->isolate(), + buf, + String::kNormalString, + cwd_len); + args.GetReturnValue().Set(cwd); +} +static void Umask(const FunctionCallbackInfo& args) +{ + Environment* env = Environment::GetCurrent(args); + if (!env) + return; + uint32_t old; + + if (args.Length() < 1 || args[0]->IsUndefined()) { + old = umask(0); + umask(static_cast(old)); + } else if (!args[0]->IsInt32() && !args[0]->IsString()) { + return env->ThrowTypeError("argument must be an integer or octal string."); + } else { + int oct; + if (args[0]->IsInt32()) { + oct = args[0]->Uint32Value(); + } else { + oct = 0; + node::Utf8Value str(env->isolate(), args[0]); + + // Parse the octal string. + for (size_t i = 0; i < str.length(); i++) { + char c = (*str)[i]; + if (c > '7' || c < '0') { + return env->ThrowTypeError("invalid octal string"); + } + oct *= 8; + oct += c - '0'; + } + } + old = umask(static_cast(oct)); + } + + args.GetReturnValue().Set(old); +} + +void Exit(const FunctionCallbackInfo& args) +{ + exit(args[0]->Int32Value()); +} - void MemoryUsage(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); +static void Uptime(const FunctionCallbackInfo& args) +{ + Environment* env = Environment::GetCurrent(args); if (!env) return; + double uptime; - size_t rss; - int err = uv_resident_set_memory(&rss); - if (err) { - return env->ThrowUVException(err, "uv_resident_set_memory"); - } + uv_update_time(env->event_loop()); + uptime = uv_now(env->event_loop()) - prog_start_time; - // V8 memory usage - HeapStatistics v8_heap_stats; - env->isolate()->GetHeapStatistics(&v8_heap_stats); + args.GetReturnValue().Set(Number::New(env->isolate(), uptime / 1000)); +} - Local heap_total = - Number::New(env->isolate(), v8_heap_stats.total_heap_size()); - Local heap_used = - Number::New(env->isolate(), v8_heap_stats.used_heap_size()); +void MemoryUsage(const FunctionCallbackInfo& args) +{ + Environment* env = Environment::GetCurrent(args); + if (!env) + return; - Local info = Object::New(env->isolate()); - info->Set(env->rss_string(), Number::New(env->isolate(), rss)); - info->Set(env->heap_total_string(), heap_total); - info->Set(env->heap_used_string(), heap_used); + size_t rss; + int err = uv_resident_set_memory(&rss); + if (err) { + return env->ThrowUVException(err, "uv_resident_set_memory"); + } - args.GetReturnValue().Set(info); - } + // V8 memory usage + HeapStatistics v8_heap_stats; + env->isolate()->GetHeapStatistics(&v8_heap_stats); + Local heap_total = Number::New(env->isolate(), v8_heap_stats.total_heap_size()); + Local heap_used = Number::New(env->isolate(), v8_heap_stats.used_heap_size()); - void Kill(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); + Local info = Object::New(env->isolate()); + info->Set(env->rss_string(), Number::New(env->isolate(), rss)); + info->Set(env->heap_total_string(), heap_total); + info->Set(env->heap_used_string(), heap_used); + + args.GetReturnValue().Set(info); +} + +void Kill(const FunctionCallbackInfo& args) +{ + Environment* env = Environment::GetCurrent(args); if (!env) return; - if (args.Length() != 2) { - return env->ThrowError("Bad argument."); - } + if (args.Length() != 2) { + return env->ThrowError("Bad argument."); + } - int pid = args[0]->Int32Value(); - int sig = args[1]->Int32Value(); - int err = uv_kill(pid, sig); - args.GetReturnValue().Set(err); - } + int pid = args[0]->Int32Value(); + int sig = args[1]->Int32Value(); + int err = uv_kill(pid, sig); + args.GetReturnValue().Set(err); +} - // used in Hrtime() below +// used in Hrtime() below #define NANOS_PER_SEC 1000000000 // Hrtime exposes libuv's uv_hrtime() high-resolution timer. @@ -1851,21 +1834,22 @@ namespace node { // so this function instead returns an Array with 2 entries representing seconds // and nanoseconds, to avoid any integer overflow possibility. // Pass in an Array from a previous hrtime() call to instead get a time diff. - void Hrtime(const FunctionCallbackInfo& args) { - uint64_t t = uv_hrtime(); - - Local ab = args[0].As()->Buffer(); - uint32_t* fields = static_cast(ab->GetContents().Data()); - - // These three indices will contain the values for the hrtime tuple. The - // seconds value is broken into the upper/lower 32 bits and stored in two - // uint32 fields to be converted back in JS. - fields[0] = (t / NANOS_PER_SEC) >> 32; - fields[1] = (t / NANOS_PER_SEC) & 0xffffffff; - fields[2] = t % NANOS_PER_SEC; - } - - // Microseconds in a second, as a float, used in CPUUsage() below +void Hrtime(const FunctionCallbackInfo& args) +{ + uint64_t t = uv_hrtime(); + + Local ab = args[0].As()->Buffer(); + uint32_t* fields = static_cast(ab->GetContents().Data()); + + // These three indices will contain the values for the hrtime tuple. The + // seconds value is broken into the upper/lower 32 bits and stored in two + // uint32 fields to be converted back in JS. + fields[0] = (t / NANOS_PER_SEC) >> 32; + fields[1] = (t / NANOS_PER_SEC) & 0xffffffff; + fields[2] = t % NANOS_PER_SEC; +} + +// Microseconds in a second, as a float, used in CPUUsage() below #define MICROS_PER_SEC 1e6 // CPUUsage use libuv's uv_getrusage() this-process resource usage accessor, @@ -1873,150 +1857,155 @@ namespace node { // which are uv_timeval_t structs (long tv_sec, long tv_usec). // Returns those values as Float64 microseconds in the elements of the array // passed to the function. - void CPUUsage(const FunctionCallbackInfo& args) { - uv_rusage_t rusage; - - // Call libuv to get the values we'll return. - int err = uv_getrusage(&rusage); - if (err) { - // On error, return the strerror version of the error code. - Local errmsg = OneByteString(args.GetIsolate(), uv_strerror(err)); - args.GetReturnValue().Set(errmsg); - return; - } - - // Get the double array pointer from the Float64Array argument. - CHECK(args[0]->IsFloat64Array()); - Local array = args[0].As(); - CHECK_EQ(array->Length(), 2); - Local ab = array->Buffer(); - double* fields = static_cast(ab->GetContents().Data()); - - // Set the Float64Array elements to be user / system values in microseconds. - fields[0] = MICROS_PER_SEC * rusage.ru_utime.tv_sec + rusage.ru_utime.tv_usec; - fields[1] = MICROS_PER_SEC * rusage.ru_stime.tv_sec + rusage.ru_stime.tv_usec; - } - - extern "C" void node_module_register(void* m) { - struct node_module* mp = reinterpret_cast(m); - - if (mp->nm_flags & NM_F_BUILTIN) { - mp->nm_link = modlist_builtin; - modlist_builtin = mp; - } - else if (!node_is_initialized) { - // "Linked" modules are included as part of the node project. - // Like builtins they are registered *before* node::Init runs. - mp->nm_flags = NM_F_LINKED; - mp->nm_link = modlist_linked; - modlist_linked = mp; - } - else { - modpending = mp; - } - } - - struct node_module* get_builtin_module(const char* name) { - struct node_module* mp; - - for (mp = modlist_builtin; mp != nullptr; mp = mp->nm_link) { - if (strcmp(mp->nm_modname, name) == 0) - break; - } - - CHECK(mp == nullptr || (mp->nm_flags & NM_F_BUILTIN) != 0); - return (mp); - } - - struct node_module* get_linked_module(const char* name) { - struct node_module* mp; - - for (mp = modlist_linked; mp != nullptr; mp = mp->nm_link) { - if (strcmp(mp->nm_modname, name) == 0) - break; - } - - CHECK(mp == nullptr || (mp->nm_flags & NM_F_LINKED) != 0); - return mp; - } - - typedef void (UV_DYNAMIC* extInit)(Local exports); - - void PrintCallStack(v8::Isolate* isolate) { - const v8::StackTrace::StackTraceOptions options = static_cast( - v8::StackTrace::kLineNumber - | v8::StackTrace::kColumnOffset - | v8::StackTrace::kScriptId - | v8::StackTrace::kScriptNameOrSourceURL - | v8::StackTrace::kFunctionName); - - int stackNum = 50; - v8::HandleScope handleScope(isolate); - v8::Local stackTrace(v8::StackTrace::CurrentStackTrace(isolate, stackNum, options)); - int count = stackTrace->GetFrameCount(); - - for (int i = 0; i < count; ++i) { - v8::Local stackFrame = stackTrace->GetFrame(i); - int frameCount = stackTrace->GetFrameCount(); - int line = stackFrame->GetLineNumber(); - v8::Local scriptName = stackFrame->GetScriptNameOrSourceURL(); - v8::Local funcName = stackFrame->GetFunctionName(); - - std::string scriptNameWTF; - std::string funcNameWTF; - - if (!scriptName.IsEmpty()) { - v8::String::Utf8Value scriptNameUtf8(scriptName); - scriptNameWTF = *scriptNameUtf8; - } - - if (!funcName.IsEmpty()) { - v8::String::Utf8Value funcNameUtf8(funcName); - funcNameWTF = *funcNameUtf8; - } - std::vector output; - output.resize(1000); - sprintf(&output[0], "line:%d, [", line); - OutputDebugStringA(&output[0]); - - if (!scriptNameWTF.empty()) { - OutputDebugStringA(scriptNameWTF.c_str()); - } - OutputDebugStringA("] , ["); - - if (!funcNameWTF.empty()) { - OutputDebugStringA(funcNameWTF.c_str()); - } - OutputDebugStringA("]\n"); - } +void CPUUsage(const FunctionCallbackInfo& args) +{ + uv_rusage_t rusage; + + // Call libuv to get the values we'll return. + int err = uv_getrusage(&rusage); + if (err) { + // On error, return the strerror version of the error code. + Local errmsg = OneByteString(args.GetIsolate(), uv_strerror(err)); + args.GetReturnValue().Set(errmsg); + return; + } + + // Get the double array pointer from the Float64Array argument. + CHECK(args[0]->IsFloat64Array()); + Local array = args[0].As(); + CHECK_EQ(array->Length(), 2); + Local ab = array->Buffer(); + double* fields = static_cast(ab->GetContents().Data()); + + // Set the Float64Array elements to be user / system values in microseconds. + fields[0] = MICROS_PER_SEC * rusage.ru_utime.tv_sec + rusage.ru_utime.tv_usec; + fields[1] = MICROS_PER_SEC * rusage.ru_stime.tv_sec + rusage.ru_stime.tv_usec; +} + +extern "C" void node_module_register(void* m) +{ + struct node_module* mp = reinterpret_cast(m); + + if (mp->nm_flags & NM_F_BUILTIN) { + mp->nm_link = modlist_builtin; + modlist_builtin = mp; + } else if (!node_is_initialized) { + // "Linked" modules are included as part of the node project. + // Like builtins they are registered *before* node::Init runs. + mp->nm_flags = NM_F_LINKED; + mp->nm_link = modlist_linked; + modlist_linked = mp; + } else { + modpending = mp; + } +} + +struct node_module* get_builtin_module(const char* name) +{ + struct node_module* mp; + + for (mp = modlist_builtin; mp != nullptr; mp = mp->nm_link) { + if (strcmp(mp->nm_modname, name) == 0) + break; + } + + CHECK(mp == nullptr || (mp->nm_flags & NM_F_BUILTIN) != 0); + return (mp); +} + +struct node_module* get_linked_module(const char* name) +{ + struct node_module* mp; + + for (mp = modlist_linked; mp != nullptr; mp = mp->nm_link) { + if (strcmp(mp->nm_modname, name) == 0) + break; + } + + CHECK(mp == nullptr || (mp->nm_flags & NM_F_LINKED) != 0); + return mp; +} + +typedef void(UV_DYNAMIC* extInit)(Local exports); + +void PrintCallStack(v8::Isolate* isolate) +{ + const v8::StackTrace::StackTraceOptions options = static_cast( + v8::StackTrace::kLineNumber + | v8::StackTrace::kColumnOffset + | v8::StackTrace::kScriptId + | v8::StackTrace::kScriptNameOrSourceURL + | v8::StackTrace::kFunctionName); + + int stackNum = 50; + v8::HandleScope handleScope(isolate); + v8::Local stackTrace(v8::StackTrace::CurrentStackTrace(isolate, stackNum, options)); + int count = stackTrace->GetFrameCount(); + + for (int i = 0; i < count; ++i) { + v8::Local stackFrame = stackTrace->GetFrame(i); + int frameCount = stackTrace->GetFrameCount(); + int line = stackFrame->GetLineNumber(); + v8::Local scriptName = stackFrame->GetScriptNameOrSourceURL(); + v8::Local funcName = stackFrame->GetFunctionName(); + + std::string scriptNameWTF; + std::string funcNameWTF; + + if (!scriptName.IsEmpty()) { + v8::String::Utf8Value scriptNameUtf8(scriptName); + scriptNameWTF = *scriptNameUtf8; + } + + if (!funcName.IsEmpty()) { + v8::String::Utf8Value funcNameUtf8(funcName); + funcNameWTF = *funcNameUtf8; + } + std::vector output; + output.resize(1000); + sprintf(&output[0], "line:%d, [", line); + OutputDebugStringA(&output[0]); + + if (!scriptNameWTF.empty()) { + OutputDebugStringA(scriptNameWTF.c_str()); + } + OutputDebugStringA("] , ["); + + if (!funcNameWTF.empty()) { + OutputDebugStringA(funcNameWTF.c_str()); + } + OutputDebugStringA("]\n"); + } OutputDebugStringA("\n"); - } - - bool CopyFile(const wchar_t* to, const wchar_t* from) { - if (::PathFileExistsW(to)) - return true; - - size_t size = wcslen(to) + 3; - std::vector to_buf; - to_buf.resize(size); - memset(&to_buf[0], 0, size * sizeof(WCHAR)); - wcscpy(&to_buf[0], to); - - size = wcslen(from) + 3; - std::vector from_buf; - from_buf.resize(size); - memset(&from_buf[0], 0, size * sizeof(WCHAR)); - wcscpy(&from_buf[0], from); - - SHFILEOPSTRUCTW FileOp = { 0 }; - FileOp.fFlags = FOF_NOCONFIRMATION | FOF_SILENT | FOF_NOERRORUI; - FileOp.pFrom = &from_buf[0]; - FileOp.pTo = &to_buf[0]; - FileOp.wFunc = FO_COPY; - return SHFileOperationW(&FileOp) == 0; - } - - bool CopyAndLoad(const char* filename, uv_lib_t* lib) { +} + +bool CopyFile(const wchar_t* to, const wchar_t* from) +{ + if (::PathFileExistsW(to)) + return true; + + size_t size = wcslen(to) + 3; + std::vector to_buf; + to_buf.resize(size); + memset(&to_buf[0], 0, size * sizeof(WCHAR)); + wcscpy(&to_buf[0], to); + + size = wcslen(from) + 3; + std::vector from_buf; + from_buf.resize(size); + memset(&from_buf[0], 0, size * sizeof(WCHAR)); + wcscpy(&from_buf[0], from); + + SHFILEOPSTRUCTW FileOp = { 0 }; + FileOp.fFlags = FOF_NOCONFIRMATION | FOF_SILENT | FOF_NOERRORUI; + FileOp.pFrom = &from_buf[0]; + FileOp.pTo = &to_buf[0]; + FileOp.wFunc = FO_COPY; + return SHFileOperationW(&FileOp) == 0; +} + +bool CopyAndLoad(const char* filename, uv_lib_t* lib) +{ std::vector filename_buffer; filename_buffer.resize(32768); @@ -2024,77 +2013,79 @@ namespace node { const wchar_t* postfix = L"_dummy"; - if (!MultiByteToWideChar(CP_UTF8, 0, filename, -1, &filename_buffer[0], /*ARRAY_SIZE(filename_w)*/32768)) - return false; + if (!MultiByteToWideChar(CP_UTF8, 0, filename, -1, &filename_buffer[0], /*ARRAY_SIZE(filename_w)*/ 32768)) + return false; if (nullptr != wcsstr(&filename_buffer[0], postfix)) - return false; + return false; std::wstring filename_w(&filename_buffer[0]); filename_dummy = filename_w; filename_dummy += postfix; - std::wstring prefix(L"\\\\?\\"); + std::wstring prefix(L"\\\\?\\"); if (0 == filename_dummy.compare(0, prefix.size(), prefix)) - filename_dummy = filename_dummy.substr(prefix.size()); + filename_dummy = filename_dummy.substr(prefix.size()); if (0 == filename_w.compare(0, prefix.size(), prefix)) - filename_w = filename_w.substr(prefix.size()); + filename_w = filename_w.substr(prefix.size()); if (!CopyFile(filename_dummy.c_str(), filename_w.c_str())) - return false; + return false; lib->handle = LoadLibraryExW(filename_dummy.c_str(), NULL, LOAD_WITH_ALTERED_SEARCH_PATH); return lib->handle != nullptr; - } +} - inline napi_addon_register_func GetNapiInitializerCallback(uv_lib_t* lib) { +inline napi_addon_register_func GetNapiInitializerCallback(uv_lib_t* lib) +{ const char* name = STRINGIFY(NAPI_MODULE_INITIALIZER_BASE) STRINGIFY(NAPI_MODULE_VERSION); void* address; uv_dlsym(lib, name, &address); return reinterpret_cast(address); - } - - // DLOpen is process.dlopen(module, filename). - // Used to load 'module.node' dynamically shared objects. - // - // FIXME(bnoordhuis) Not multi-context ready. TBD how to resolve the conflict - // when two contexts try to load the same shared object. Maybe have a shadow - // cache that's a plain C list or hash table that's shared across contexts? - void DLOpen(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); +} + +// DLOpen is process.dlopen(module, filename). +// Used to load 'module.node' dynamically shared objects. +// +// FIXME(bnoordhuis) Not multi-context ready. TBD how to resolve the conflict +// when two contexts try to load the same shared object. Maybe have a shadow +// cache that's a plain C list or hash table that's shared across contexts? +void DLOpen(const FunctionCallbackInfo& args) +{ + Environment* env = Environment::GetCurrent(args); if (!env) return; - uv_lib_t lib; - - CHECK_EQ(modpending, nullptr); - - if (args.Length() != 2) { - env->ThrowError("process.dlopen takes exactly 2 arguments."); - return; - } - - Local module = args[0]->ToObject(env->isolate()); // Cast - node::Utf8Value filename(env->isolate(), args[1]); // Cast - const bool is_dlopen_error = uv_dlopen(*filename, &lib); - - // Objects containing v14 or later modules will have registered themselves - // on the pending list. Activate all of them now. At present, only one - // module per object is supported. - node_module* /*const*/ mp = modpending; - modpending = nullptr; - - if (is_dlopen_error) { - Local errmsg = OneByteString(env->isolate(), uv_dlerror(&lib)); - uv_dlclose(&lib); + uv_lib_t lib; + + CHECK_EQ(modpending, nullptr); + + if (args.Length() != 2) { + env->ThrowError("process.dlopen takes exactly 2 arguments."); + return; + } + + Local module = args[0]->ToObject(env->isolate()); // Cast + node::Utf8Value filename(env->isolate(), args[1]); // Cast + const bool is_dlopen_error = uv_dlopen(*filename, &lib); + + // Objects containing v14 or later modules will have registered themselves + // on the pending list. Activate all of them now. At present, only one + // module per object is supported. + node_module* /*const*/ mp = modpending; + modpending = nullptr; + + if (is_dlopen_error) { + Local errmsg = OneByteString(env->isolate(), uv_dlerror(&lib)); + uv_dlclose(&lib); #ifdef _WIN32 - // Windows needs to add the filename into the error message - errmsg = String::Concat(errmsg, args[1]->ToString(env->isolate())); -#endif // _WIN32 - env->isolate()->ThrowException(Exception::Error(errmsg)); - return; - } + // Windows needs to add the filename into the error message + errmsg = String::Concat(errmsg, args[1]->ToString(env->isolate())); +#endif // _WIN32 + env->isolate()->ThrowException(Exception::Error(errmsg)); + return; + } #if 0 if (lib.handle && nullptr == mp) { @@ -2106,110 +2097,107 @@ namespace node { #endif if (mp == nullptr) { - if (napi_addon_register_func napi_callback = GetNapiInitializerCallback(&lib)) { - v8::Local module; - v8::Local exports; - v8::Local exports_v; - v8::Local context = env->context(); - if (!args[0]->ToObject(context).ToLocal(&module) || - !module->Get(context, env->exports_string()).ToLocal(&exports_v) || - !exports_v->ToObject(context).ToLocal(&exports)) { - return; // Exception pending. + if (napi_addon_register_func napi_callback = GetNapiInitializerCallback(&lib)) { + v8::Local module; + v8::Local exports; + v8::Local exports_v; + v8::Local context = env->context(); + if (!args[0]->ToObject(context).ToLocal(&module) || !module->Get(context, env->exports_string()).ToLocal(&exports_v) || !exports_v->ToObject(context).ToLocal(&exports)) { + return; // Exception pending. + } + + napi_module_register_by_symbol(exports, module, context, napi_callback); + return; } + } + + if (mp == nullptr) { + PrintCallStack(args.GetIsolate()); - napi_module_register_by_symbol(exports, module, context, napi_callback); + uv_dlclose(&lib); + env->ThrowError("Module did not self-register."); return; - } - } - - if (mp == nullptr) { - PrintCallStack(args.GetIsolate()); - - uv_dlclose(&lib); - env->ThrowError("Module did not self-register."); - return; - } - if (false && mp->nm_version != NODE_MODULE_VERSION && mp->nm_version != 50 && mp->nm_version != 54) { // 特别支持下50\54版 - char errmsg[1024]; - snprintf(errmsg, - sizeof(errmsg), - "Module version mismatch. Expected %d, got %d.", - NODE_MODULE_VERSION, mp->nm_version); - - ::DebugBreak(); - // NOTE: `mp` is allocated inside of the shared library's memory, calling - // `uv_dlclose` will deallocate it - uv_dlclose(&lib); - env->ThrowError(errmsg); - return; - } - if (mp->nm_flags & NM_F_BUILTIN) { - uv_dlclose(&lib); - env->ThrowError("Built-in module self-registered."); - return; - } - - mp->nm_dso_handle = lib.handle; - mp->nm_link = modlist_addon; - modlist_addon = mp; - - Local exports_string = env->exports_string(); - Local exports = module->Get(exports_string)->ToObject(env->isolate()); - - if (mp->nm_context_register_func != nullptr) { - mp->nm_context_register_func(exports, module, env->context(), mp->nm_priv); - } - else if (mp->nm_register_func != nullptr) { - mp->nm_register_func(exports, module, mp->nm_priv); - } - else { - uv_dlclose(&lib); - env->ThrowError("Module has no declared entry point."); - return; - } - - // Tell coverity that 'handle' should not be freed when we return. - // coverity[leaked_storage] - } - - - static void OnFatalError(const char* location, const char* message) { - if (location) { - PrintErrorString("FATAL ERROR: %s %s\n", location, message); - } - else { - PrintErrorString("FATAL ERROR: %s\n", message); - } - fflush(stderr); - ABORT(); - } - - - NO_RETURN void FatalError(const char* location, const char* message) { - OnFatalError(location, message); - // to suppress compiler warning - ABORT(); - } - - FatalTryCatch::FatalTryCatch(Environment* env) - : v8::TryCatch(env->isolate()) - , env_(env) { - } + } + if (false && mp->nm_version != NODE_MODULE_VERSION && mp->nm_version != 50 && mp->nm_version != 54) { // 特别支持下50\54版 + char errmsg[1024]; + snprintf(errmsg, + sizeof(errmsg), + "Module version mismatch. Expected %d, got %d.", + NODE_MODULE_VERSION, mp->nm_version); + + ::DebugBreak(); + // NOTE: `mp` is allocated inside of the shared library's memory, calling + // `uv_dlclose` will deallocate it + uv_dlclose(&lib); + env->ThrowError(errmsg); + return; + } + if (mp->nm_flags & NM_F_BUILTIN) { + uv_dlclose(&lib); + env->ThrowError("Built-in module self-registered."); + return; + } + + mp->nm_dso_handle = lib.handle; + mp->nm_link = modlist_addon; + modlist_addon = mp; + + Local exports_string = env->exports_string(); + Local exports = module->Get(exports_string)->ToObject(env->isolate()); + + if (mp->nm_context_register_func != nullptr) { + mp->nm_context_register_func(exports, module, env->context(), mp->nm_priv); + } else if (mp->nm_register_func != nullptr) { + mp->nm_register_func(exports, module, mp->nm_priv); + } else { + uv_dlclose(&lib); + env->ThrowError("Module has no declared entry point."); + return; + } - FatalTryCatch::~FatalTryCatch() { - if (HasCaught()) { - HandleScope scope(env_->isolate()); - ReportException(env_, *this); - exit(7); - } - } + // Tell coverity that 'handle' should not be freed when we return. + // coverity[leaked_storage] +} +static void OnFatalError(const char* location, const char* message) +{ + if (location) { + PrintErrorString("FATAL ERROR: %s %s\n", location, message); + } else { + PrintErrorString("FATAL ERROR: %s\n", message); + } + fflush(stderr); + ABORT(); +} + +NO_RETURN void FatalError(const char* location, const char* message) +{ + OnFatalError(location, message); + // to suppress compiler warning + ABORT(); +} + +FatalTryCatch::FatalTryCatch(Environment* env) + : v8::TryCatch(env->isolate()) + , env_(env) +{ +} + +FatalTryCatch::~FatalTryCatch() +{ + if (HasCaught()) { + HandleScope scope(env_->isolate()); + ReportException(env_, *this); + exit(7); + } +} - void FatalException(Isolate* isolate, Local error, Local message) { - HandleScope scope(isolate); +void FatalException(Isolate* isolate, Local error, Local message) +{ + HandleScope scope(isolate); Environment* env = Environment::GetCurrent(isolate); if (!env || env->is_blink_core()) - return; + return; Local error_mesage = message->Get(); v8::String::Utf8Value error_mesage_utf8(error_mesage); @@ -2217,16 +2205,16 @@ namespace node { char* error_mesage_buf = new char[1000]; v8::Local stackTrace = message->GetStackTrace(); if (!stackTrace.IsEmpty()) { - int frameCount = stackTrace->GetFrameCount(); - for (int i = 0; i < frameCount; ++i) { - v8::Local stackFrame = stackTrace->GetFrame(i); - Local scriptName = stackFrame->GetScriptName(); - v8::String::Utf8Value scriptNameUtf8(scriptName); - snprintf(error_mesage_buf, 999, "StackTrace:%d, %s\n", stackFrame->GetLineNumber(), *scriptNameUtf8); - OutputDebugStringA(error_mesage_buf); - } + int frameCount = stackTrace->GetFrameCount(); + for (int i = 0; i < frameCount; ++i) { + v8::Local stackFrame = stackTrace->GetFrame(i); + Local scriptName = stackFrame->GetScriptName(); + v8::String::Utf8Value scriptNameUtf8(scriptName); + snprintf(error_mesage_buf, 999, "StackTrace:%d, %s\n", stackFrame->GetLineNumber(), *scriptNameUtf8); + OutputDebugStringA(error_mesage_buf); + } } - + Local scriptResourceName = message->GetScriptResourceName(); v8::String::Utf8Value scriptResourceNameUtf8(scriptResourceName); @@ -2240,573 +2228,568 @@ namespace node { //::TerminateProcess((HANDLE)-1, 0); return; - Local process_object = env->process_object(); - Local fatal_exception_string = env->fatal_exception_string(); - Local fatal_exception_function = - process_object->Get(fatal_exception_string).As(); - - int exit_code = 0; - if (!fatal_exception_function->IsFunction()) { - // failed before the process._fatalException function was added! - // this is probably pretty bad. Nothing to do but report and exit. - ReportException(env, error, message); - //exit_code = 6; - } - - if (exit_code == 0) { - TryCatch fatal_try_catch(isolate); - - // Do not call FatalException when _fatalException handler throws - fatal_try_catch.SetVerbose(false); - - // this will return true if the JS layer handled it, false otherwise - Local caught = - fatal_exception_function->Call(process_object, 1, &error); - - if (fatal_try_catch.HasCaught()) { - // the fatal exception function threw, so we must exit - ReportException(env, fatal_try_catch); - //exit_code = 7; - } - - if (exit_code == 0 && false == caught->BooleanValue()) { - ReportException(env, error, message); - //exit_code = 1; - } - } - - if (exit_code) { + Local process_object = env->process_object(); + Local fatal_exception_string = env->fatal_exception_string(); + Local fatal_exception_function = process_object->Get(fatal_exception_string).As(); + + int exit_code = 0; + if (!fatal_exception_function->IsFunction()) { + // failed before the process._fatalException function was added! + // this is probably pretty bad. Nothing to do but report and exit. + ReportException(env, error, message); + //exit_code = 6; + } + + if (exit_code == 0) { + TryCatch fatal_try_catch(isolate); + + // Do not call FatalException when _fatalException handler throws + fatal_try_catch.SetVerbose(false); + + // this will return true if the JS layer handled it, false otherwise + Local caught = fatal_exception_function->Call(process_object, 1, &error); + + if (fatal_try_catch.HasCaught()) { + // the fatal exception function threw, so we must exit + ReportException(env, fatal_try_catch); + //exit_code = 7; + } + + if (exit_code == 0 && false == caught->BooleanValue()) { + ReportException(env, error, message); + //exit_code = 1; + } + } + + if (exit_code) { #if HAVE_INSPECTOR - if (use_inspector) { - env->inspector_agent()->FatalException(error, message); - } -#endif - exit(exit_code); - } - } - void FatalException(Isolate* isolate, const TryCatch& try_catch) { - HandleScope scope(isolate); - // TODO(bajtos) do not call FatalException if try_catch is verbose - // (requires V8 API to expose getter for try_catch.is_verbose_) - FatalException(isolate, try_catch.Exception(), try_catch.Message()); - } - - - void OnMessage(Local message, Local error) { - // The current version of V8 sends messages for errors only - // (thus `error` is always set). - FatalException(Isolate::GetCurrent(), error, message); - } - - - void ClearFatalExceptionHandlers(Environment* env) { - Local process = env->process_object(); - Local events = - process->Get(env->context(), env->events_string()).ToLocalChecked(); - - if (events->IsObject()) { - events.As()->Set( - env->context(), - OneByteString(env->isolate(), "uncaughtException"), - Undefined(env->isolate())).FromJust(); - } - - process->Set( - env->context(), - env->domain_string(), - Undefined(env->isolate())).FromJust(); - } - //加载模块js - void ModuleJavaScript(Environment* env, Local target) { - HandleScope scope(env->isolate()); - - struct node_module* mp; - - for (mp = modlist_builtin; mp != nullptr; mp = mp->nm_link) { - if (mp->nm_priv) { - struct _native* n = reinterpret_cast(mp->nm_priv); - Local name = String::NewFromUtf8(env->isolate(), n->name); - Local source = - String::NewFromUtf8( - env->isolate(), reinterpret_cast(n->source), - v8::NewStringType::kNormal, n->source_len).ToLocalChecked(); - target->Set(name, source); - - } - } - } - static void Binding(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); + if (use_inspector) { + env->inspector_agent()->FatalException(error, message); + } +#endif + exit(exit_code); + } +} +void FatalException(Isolate* isolate, const TryCatch& try_catch) +{ + HandleScope scope(isolate); + // TODO(bajtos) do not call FatalException if try_catch is verbose + // (requires V8 API to expose getter for try_catch.is_verbose_) + FatalException(isolate, try_catch.Exception(), try_catch.Message()); +} + +void OnMessage(Local message, Local error) +{ + // The current version of V8 sends messages for errors only + // (thus `error` is always set). + FatalException(Isolate::GetCurrent(), error, message); +} + +void ClearFatalExceptionHandlers(Environment* env) +{ + Local process = env->process_object(); + Local events = process->Get(env->context(), env->events_string()).ToLocalChecked(); + + if (events->IsObject()) { + events.As()->Set( + env->context(), + OneByteString(env->isolate(), "uncaughtException"), + Undefined(env->isolate())) + .FromJust(); + } + + process->Set( + env->context(), + env->domain_string(), + Undefined(env->isolate())) + .FromJust(); +} +//加载模块js +void ModuleJavaScript(Environment* env, Local target) +{ + HandleScope scope(env->isolate()); + + struct node_module* mp; + + for (mp = modlist_builtin; mp != nullptr; mp = mp->nm_link) { + if (mp->nm_priv) { + struct _native* n = reinterpret_cast(mp->nm_priv); + Local name = String::NewFromUtf8(env->isolate(), n->name); + Local source = String::NewFromUtf8( + env->isolate(), reinterpret_cast(n->source), + v8::NewStringType::kNormal, n->source_len) + .ToLocalChecked(); + target->Set(name, source); + } + } +} +static void Binding(const FunctionCallbackInfo& args) +{ + Environment* env = Environment::GetCurrent(args); if (!env) return; - Local module = args[0]->ToString(env->isolate()); - node::Utf8Value module_v(env->isolate(), module); - - Local cache = env->binding_cache_object(); - Local exports; - - if (cache->Has(env->context(), module).FromJust()) { - exports = cache->Get(module)->ToObject(env->isolate()); - args.GetReturnValue().Set(exports); - return; - } - - // Append a string to process.moduleLoadList - char buf[1024]; - snprintf(buf, sizeof(buf), "Binding %s", *module_v); - - Local modules = env->module_load_list_array(); - uint32_t l = modules->Length(); - modules->Set(l, OneByteString(env->isolate(), buf)); - - node_module* mod = get_builtin_module(*module_v); - if (mod != nullptr) { - exports = Object::New(env->isolate()); - // Internal bindings don't have a "module" object, only exports. - CHECK_EQ(mod->nm_register_func, nullptr); - CHECK_NE(mod->nm_context_register_func, nullptr); - Local unused = Undefined(env->isolate()); - mod->nm_context_register_func(exports, unused, - env->context(), mod->nm_priv); - cache->Set(module, exports); - } - else if (!strcmp(*module_v, "constants")) { - exports = Object::New(env->isolate()); - DefineConstants(env->isolate(), exports); - cache->Set(module, exports); - } - else if (!strcmp(*module_v, "natives")) { - exports = Object::New(env->isolate()); - DefineJavaScript(env, exports); - //加载脚本 - ModuleJavaScript(env, exports); - - cache->Set(module, exports); - } - else { - char errmsg[1024]; - snprintf(errmsg, - sizeof(errmsg), - "No such module: %s", - *module_v); - return env->ThrowError(errmsg); - } - - args.GetReturnValue().Set(exports); - } - - static void LinkedBinding(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args.GetIsolate()); + Local module = args[0]->ToString(env->isolate()); + node::Utf8Value module_v(env->isolate(), module); + + Local cache = env->binding_cache_object(); + Local exports; + + if (cache->Has(env->context(), module).FromJust()) { + exports = cache->Get(module)->ToObject(env->isolate()); + args.GetReturnValue().Set(exports); + return; + } + + // Append a string to process.moduleLoadList + char buf[1024]; + snprintf(buf, sizeof(buf), "Binding %s", *module_v); + + Local modules = env->module_load_list_array(); + uint32_t l = modules->Length(); + modules->Set(l, OneByteString(env->isolate(), buf)); + + node_module* mod = get_builtin_module(*module_v); + if (mod != nullptr) { + exports = Object::New(env->isolate()); + // Internal bindings don't have a "module" object, only exports. + CHECK_EQ(mod->nm_register_func, nullptr); + CHECK_NE(mod->nm_context_register_func, nullptr); + Local unused = Undefined(env->isolate()); + mod->nm_context_register_func(exports, unused, + env->context(), mod->nm_priv); + cache->Set(module, exports); + } else if (!strcmp(*module_v, "constants")) { + exports = Object::New(env->isolate()); + DefineConstants(env->isolate(), exports); + cache->Set(module, exports); + } else if (!strcmp(*module_v, "natives")) { + exports = Object::New(env->isolate()); + DefineJavaScript(env, exports); + //加载脚本 + ModuleJavaScript(env, exports); + + cache->Set(module, exports); + } else { + char errmsg[1024]; + snprintf(errmsg, + sizeof(errmsg), + "No such module: %s", + *module_v); + return env->ThrowError(errmsg); + } + + args.GetReturnValue().Set(exports); +} + +static void LinkedBinding(const FunctionCallbackInfo& args) +{ + Environment* env = Environment::GetCurrent(args.GetIsolate()); if (!env) return; - Local module_name = args[0]->ToString(env->isolate()); - - Local cache = env->binding_cache_object(); - Local exports_v = cache->Get(module_name); - - if (exports_v->IsObject()) - return args.GetReturnValue().Set(exports_v.As()); - - node::Utf8Value module_name_v(env->isolate(), module_name); - node_module* mod = get_linked_module(*module_name_v); - - if (mod == nullptr) { - char errmsg[1024]; - snprintf(errmsg, - sizeof(errmsg), - "No such module was linked: %s", - *module_name_v); - return env->ThrowError(errmsg); - } - - Local module = Object::New(env->isolate()); - Local exports = Object::New(env->isolate()); - Local exports_prop = String::NewFromUtf8(env->isolate(), "exports"); - module->Set(exports_prop, exports); - - if (mod->nm_context_register_func != nullptr) { - mod->nm_context_register_func(exports, - module, - env->context(), - mod->nm_priv); - } - else if (mod->nm_register_func != nullptr) { - mod->nm_register_func(exports, module, mod->nm_priv); - } - else { - return env->ThrowError("Linked module has no declared entry point."); - } - - auto effective_exports = module->Get(exports_prop); - cache->Set(module_name, effective_exports); - - args.GetReturnValue().Set(effective_exports); - } - - static void ProcessTitleGetter(Local property, const PropertyCallbackInfo& info) { - char buffer[512]; - uv_get_process_title(buffer, sizeof(buffer)); - info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), buffer)); - } - static void ProcessTitleSetter(Local property, Local value, const PropertyCallbackInfo& info) { - node::Utf8Value title(info.GetIsolate(), value); - // TODO(piscisaureus): protect with a lock - uv_set_process_title(*title); - } - - - static void EnvGetter(Local property, const PropertyCallbackInfo& info) { - Isolate* isolate = info.GetIsolate(); - String::Value key(property); - WCHAR buffer[32767]; // The maximum size allowed for environment variables. - DWORD result = GetEnvironmentVariableW(reinterpret_cast(*key), - buffer, - arraysize(buffer)); - // If result >= sizeof buffer the buffer was too small. That should never - // happen. If result == 0 and result != ERROR_SUCCESS the variable was not - // not found. - if ((result > 0 || GetLastError() == ERROR_SUCCESS) && - result < arraysize(buffer)) { - const uint16_t* two_byte_buffer = reinterpret_cast(buffer); - Local rc = String::NewFromTwoByte(isolate, two_byte_buffer); - return info.GetReturnValue().Set(rc); - } - } - static void EnvSetter(Local property, Local value, const PropertyCallbackInfo& info) { - String::Value key(property); - String::Value val(value); - WCHAR* key_ptr = reinterpret_cast(*key); - // Environment variables that start with '=' are read-only. - if (key_ptr[0] != L'=') { - SetEnvironmentVariableW(key_ptr, reinterpret_cast(*val)); - } - // Whether it worked or not, always return value. - info.GetReturnValue().Set(value); - } - - - static void EnvQuery(Local property, const PropertyCallbackInfo& info) { - int32_t rc = -1; // Not found unless proven otherwise. - String::Value key(property); - WCHAR* key_ptr = reinterpret_cast(*key); - if (GetEnvironmentVariableW(key_ptr, nullptr, 0) > 0 || - GetLastError() == ERROR_SUCCESS) { - rc = 0; - if (key_ptr[0] == L'=') { - // Environment variables that start with '=' are hidden and read-only. - rc = static_cast(v8::ReadOnly) | - static_cast(v8::DontDelete) | - static_cast(v8::DontEnum); - } - } - if (rc != -1) - info.GetReturnValue().Set(rc); - } - - - static void EnvDeleter(Local property, const PropertyCallbackInfo& info) { - String::Value key(property); - WCHAR* key_ptr = reinterpret_cast(*key); - SetEnvironmentVariableW(key_ptr, nullptr); - - // process.env never has non-configurable properties, so always - // return true like the tc39 delete operator. - info.GetReturnValue().Set(true); - } - - - static void EnvEnumerator(const PropertyCallbackInfo& info) { - Environment* env = Environment::GetCurrent(info); + Local module_name = args[0]->ToString(env->isolate()); + + Local cache = env->binding_cache_object(); + Local exports_v = cache->Get(module_name); + + if (exports_v->IsObject()) + return args.GetReturnValue().Set(exports_v.As()); + + node::Utf8Value module_name_v(env->isolate(), module_name); + node_module* mod = get_linked_module(*module_name_v); + + if (mod == nullptr) { + char errmsg[1024]; + snprintf(errmsg, + sizeof(errmsg), + "No such module was linked: %s", + *module_name_v); + return env->ThrowError(errmsg); + } + + Local module = Object::New(env->isolate()); + Local exports = Object::New(env->isolate()); + Local exports_prop = String::NewFromUtf8(env->isolate(), "exports"); + module->Set(exports_prop, exports); + + if (mod->nm_context_register_func != nullptr) { + mod->nm_context_register_func(exports, + module, + env->context(), + mod->nm_priv); + } else if (mod->nm_register_func != nullptr) { + mod->nm_register_func(exports, module, mod->nm_priv); + } else { + return env->ThrowError("Linked module has no declared entry point."); + } + + auto effective_exports = module->Get(exports_prop); + cache->Set(module_name, effective_exports); + + args.GetReturnValue().Set(effective_exports); +} + +static void ProcessTitleGetter(Local property, const PropertyCallbackInfo& info) +{ + char buffer[512]; + uv_get_process_title(buffer, sizeof(buffer)); + info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), buffer)); +} +static void ProcessTitleSetter(Local property, Local value, const PropertyCallbackInfo& info) +{ + node::Utf8Value title(info.GetIsolate(), value); + // TODO(piscisaureus): protect with a lock + uv_set_process_title(*title); +} + +static void EnvGetter(Local property, const PropertyCallbackInfo& info) +{ + Isolate* isolate = info.GetIsolate(); + String::Value key(property); + WCHAR buffer[32767]; // The maximum size allowed for environment variables. + DWORD result = GetEnvironmentVariableW(reinterpret_cast(*key), + buffer, + arraysize(buffer)); + // If result >= sizeof buffer the buffer was too small. That should never + // happen. If result == 0 and result != ERROR_SUCCESS the variable was not + // not found. + if ((result > 0 || GetLastError() == ERROR_SUCCESS) && result < arraysize(buffer)) { + const uint16_t* two_byte_buffer = reinterpret_cast(buffer); + Local rc = String::NewFromTwoByte(isolate, two_byte_buffer); + return info.GetReturnValue().Set(rc); + } +} +static void EnvSetter(Local property, Local value, const PropertyCallbackInfo& info) +{ + String::Value key(property); + String::Value val(value); + WCHAR* key_ptr = reinterpret_cast(*key); + // Environment variables that start with '=' are read-only. + if (key_ptr[0] != L'=') { + SetEnvironmentVariableW(key_ptr, reinterpret_cast(*val)); + } + // Whether it worked or not, always return value. + info.GetReturnValue().Set(value); +} + +static void EnvQuery(Local property, const PropertyCallbackInfo& info) +{ + int32_t rc = -1; // Not found unless proven otherwise. + String::Value key(property); + WCHAR* key_ptr = reinterpret_cast(*key); + if (GetEnvironmentVariableW(key_ptr, nullptr, 0) > 0 || GetLastError() == ERROR_SUCCESS) { + rc = 0; + if (key_ptr[0] == L'=') { + // Environment variables that start with '=' are hidden and read-only. + rc = static_cast(v8::ReadOnly) | static_cast(v8::DontDelete) | static_cast(v8::DontEnum); + } + } + if (rc != -1) + info.GetReturnValue().Set(rc); +} + +static void EnvDeleter(Local property, const PropertyCallbackInfo& info) +{ + String::Value key(property); + WCHAR* key_ptr = reinterpret_cast(*key); + SetEnvironmentVariableW(key_ptr, nullptr); + + // process.env never has non-configurable properties, so always + // return true like the tc39 delete operator. + info.GetReturnValue().Set(true); +} + +static void EnvEnumerator(const PropertyCallbackInfo& info) +{ + Environment* env = Environment::GetCurrent(info); if (!env) return; - Isolate* isolate = env->isolate(); - Local ctx = env->context(); - Local fn = env->push_values_to_array_function(); - Local argv[NODE_PUSH_VAL_TO_ARRAY_MAX]; - size_t idx = 0; - - WCHAR* environment = GetEnvironmentStringsW(); - if (environment == nullptr) - return; // This should not happen. - Local envarr = Array::New(isolate); - WCHAR* p = environment; - while (*p) { - WCHAR *s; - if (*p == L'=') { - // If the key starts with '=' it is a hidden environment variable. - p += wcslen(p) + 1; - continue; - } - else { - s = wcschr(p, L'='); - } - if (!s) { - s = p + wcslen(p); - } - const uint16_t* two_byte_buffer = reinterpret_cast(p); - const size_t two_byte_buffer_len = s - p; - argv[idx] = String::NewFromTwoByte(isolate, - two_byte_buffer, - String::kNormalString, - two_byte_buffer_len); - if (++idx >= arraysize(argv)) { - fn->Call(ctx, envarr, idx, argv).ToLocalChecked(); - idx = 0; - } - p = s + wcslen(s) + 1; - } - if (idx > 0) { - fn->Call(ctx, envarr, idx, argv).ToLocalChecked(); - } - FreeEnvironmentStringsW(environment); - - info.GetReturnValue().Set(envarr); - } - - - static Local GetFeatures(Environment* env) { - EscapableHandleScope scope(env->isolate()); - - Local obj = Object::New(env->isolate()); + Isolate* isolate = env->isolate(); + Local ctx = env->context(); + Local fn = env->push_values_to_array_function(); + Local argv[NODE_PUSH_VAL_TO_ARRAY_MAX]; + size_t idx = 0; + + WCHAR* environment = GetEnvironmentStringsW(); + if (environment == nullptr) + return; // This should not happen. + Local envarr = Array::New(isolate); + WCHAR* p = environment; + while (*p) { + WCHAR* s; + if (*p == L'=') { + // If the key starts with '=' it is a hidden environment variable. + p += wcslen(p) + 1; + continue; + } else { + s = wcschr(p, L'='); + } + if (!s) { + s = p + wcslen(p); + } + const uint16_t* two_byte_buffer = reinterpret_cast(p); + const size_t two_byte_buffer_len = s - p; + argv[idx] = String::NewFromTwoByte(isolate, + two_byte_buffer, + String::kNormalString, + two_byte_buffer_len); + if (++idx >= arraysize(argv)) { + fn->Call(ctx, envarr, idx, argv).ToLocalChecked(); + idx = 0; + } + p = s + wcslen(s) + 1; + } + if (idx > 0) { + fn->Call(ctx, envarr, idx, argv).ToLocalChecked(); + } + FreeEnvironmentStringsW(environment); + + info.GetReturnValue().Set(envarr); +} + +static Local GetFeatures(Environment* env) +{ + EscapableHandleScope scope(env->isolate()); + + Local obj = Object::New(env->isolate()); #if defined(DEBUG) && DEBUG - Local debug = True(env->isolate()); + Local debug = True(env->isolate()); #else - Local debug = False(env->isolate()); -#endif // defined(DEBUG) && DEBUG + Local debug = False(env->isolate()); +#endif // defined(DEBUG) && DEBUG - obj->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "debug"), debug); - obj->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "uv"), True(env->isolate())); - // TODO(bnoordhuis) ping libuv - obj->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "ipv6"), True(env->isolate())); + obj->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "debug"), debug); + obj->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "uv"), True(env->isolate())); + // TODO(bnoordhuis) ping libuv + obj->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "ipv6"), True(env->isolate())); #ifdef OPENSSL_NPN_NEGOTIATED - Local tls_npn = True(env->isolate()); + Local tls_npn = True(env->isolate()); #else - Local tls_npn = False(env->isolate()); + Local tls_npn = False(env->isolate()); #endif - obj->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "tls_npn"), tls_npn); + obj->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "tls_npn"), tls_npn); #ifdef TLSEXT_TYPE_application_layer_protocol_negotiation - Local tls_alpn = True(env->isolate()); + Local tls_alpn = True(env->isolate()); #else - Local tls_alpn = False(env->isolate()); + Local tls_alpn = False(env->isolate()); #endif - obj->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "tls_alpn"), tls_alpn); + obj->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "tls_alpn"), tls_alpn); #ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB - Local tls_sni = True(env->isolate()); + Local tls_sni = True(env->isolate()); #else - Local tls_sni = False(env->isolate()); + Local tls_sni = False(env->isolate()); #endif - obj->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "tls_sni"), tls_sni); + obj->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "tls_sni"), tls_sni); #if !defined(OPENSSL_NO_TLSEXT) && defined(SSL_CTX_set_tlsext_status_cb) - Local tls_ocsp = True(env->isolate()); + Local tls_ocsp = True(env->isolate()); #else - Local tls_ocsp = False(env->isolate()); -#endif // !defined(OPENSSL_NO_TLSEXT) && defined(SSL_CTX_set_tlsext_status_cb) - obj->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "tls_ocsp"), tls_ocsp); - - obj->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "tls"), - Boolean::New(env->isolate(), - get_builtin_module("crypto") != nullptr)); - - return scope.Escape(obj); - } - - - static void DebugPortGetter(Local property, const PropertyCallbackInfo& info) { - info.GetReturnValue().Set(debug_port); - } - static void DebugPortSetter(Local property, Local value, const PropertyCallbackInfo& info) { - debug_port = value->Int32Value(); - } - - - static void DebugProcess(const FunctionCallbackInfo& args); - static void DebugPause(const FunctionCallbackInfo& args); - static void DebugEnd(const FunctionCallbackInfo& args); - - - static void NeedImmediateCallbackGetter(Local property, const PropertyCallbackInfo& info) { - Environment* env = Environment::GetCurrent(info); + Local tls_ocsp = False(env->isolate()); +#endif // !defined(OPENSSL_NO_TLSEXT) && defined(SSL_CTX_set_tlsext_status_cb) + obj->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "tls_ocsp"), tls_ocsp); + + obj->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "tls"), + Boolean::New(env->isolate(), + get_builtin_module("crypto") != nullptr)); + + return scope.Escape(obj); +} + +static void DebugPortGetter(Local property, const PropertyCallbackInfo& info) +{ + info.GetReturnValue().Set(debug_port); +} +static void DebugPortSetter(Local property, Local value, const PropertyCallbackInfo& info) +{ + debug_port = value->Int32Value(); +} + +static void DebugProcess(const FunctionCallbackInfo& args); +static void DebugPause(const FunctionCallbackInfo& args); +static void DebugEnd(const FunctionCallbackInfo& args); + +static void NeedImmediateCallbackGetter(Local property, const PropertyCallbackInfo& info) +{ + Environment* env = Environment::GetCurrent(info); if (!env) return; - const uv_check_t* immediate_check_handle = env->immediate_check_handle(); - bool active = uv_is_active( - reinterpret_cast(immediate_check_handle)); - info.GetReturnValue().Set(active); - } - static void NeedImmediateCallbackSetter(Local property, Local value, const PropertyCallbackInfo& info) { - Environment* env = Environment::GetCurrent(info); + const uv_check_t* immediate_check_handle = env->immediate_check_handle(); + bool active = uv_is_active( + reinterpret_cast(immediate_check_handle)); + info.GetReturnValue().Set(active); +} +static void NeedImmediateCallbackSetter(Local property, Local value, const PropertyCallbackInfo& info) +{ + Environment* env = Environment::GetCurrent(info); if (!env) return; - uv_check_t* immediate_check_handle = env->immediate_check_handle(); - bool active = uv_is_active( - reinterpret_cast(immediate_check_handle)); - - if (active == value->BooleanValue()) - return; - - uv_idle_t* immediate_idle_handle = env->immediate_idle_handle(); - - if (active) { - uv_check_stop(immediate_check_handle); - uv_idle_stop(immediate_idle_handle); - } - else { - uv_check_start(immediate_check_handle, CheckImmediate); - // Idle handle is needed only to stop the event loop from blocking in poll. - uv_idle_start(immediate_idle_handle, IdleImmediateDummy); - } - } - - - void SetIdle(uv_prepare_t* handle) { - Environment* env = Environment::from_idle_prepare_handle(handle); - env->isolate()->GetCpuProfiler()->SetIdle(true); - } - void ClearIdle(uv_check_t* handle) { - Environment* env = Environment::from_idle_check_handle(handle); - env->isolate()->GetCpuProfiler()->SetIdle(false); - } - - - void StartProfilerIdleNotifier(Environment* env) { - uv_prepare_start(env->idle_prepare_handle(), SetIdle); - uv_check_start(env->idle_check_handle(), ClearIdle); - } - void StopProfilerIdleNotifier(Environment* env) { - uv_prepare_stop(env->idle_prepare_handle()); - uv_check_stop(env->idle_check_handle()); - } - - - void StartProfilerIdleNotifier(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); + uv_check_t* immediate_check_handle = env->immediate_check_handle(); + bool active = uv_is_active( + reinterpret_cast(immediate_check_handle)); + + if (active == value->BooleanValue()) + return; + + uv_idle_t* immediate_idle_handle = env->immediate_idle_handle(); + + if (active) { + uv_check_stop(immediate_check_handle); + uv_idle_stop(immediate_idle_handle); + } else { + uv_check_start(immediate_check_handle, CheckImmediate); + // Idle handle is needed only to stop the event loop from blocking in poll. + uv_idle_start(immediate_idle_handle, IdleImmediateDummy); + } +} + +void SetIdle(uv_prepare_t* handle) +{ + Environment* env = Environment::from_idle_prepare_handle(handle); + env->isolate()->GetCpuProfiler()->SetIdle(true); +} +void ClearIdle(uv_check_t* handle) +{ + Environment* env = Environment::from_idle_check_handle(handle); + env->isolate()->GetCpuProfiler()->SetIdle(false); +} + +void StartProfilerIdleNotifier(Environment* env) +{ + uv_prepare_start(env->idle_prepare_handle(), SetIdle); + uv_check_start(env->idle_check_handle(), ClearIdle); +} +void StopProfilerIdleNotifier(Environment* env) +{ + uv_prepare_stop(env->idle_prepare_handle()); + uv_check_stop(env->idle_check_handle()); +} + +void StartProfilerIdleNotifier(const FunctionCallbackInfo& args) +{ + Environment* env = Environment::GetCurrent(args); if (!env) return; - StartProfilerIdleNotifier(env); - } - void StopProfilerIdleNotifier(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); + StartProfilerIdleNotifier(env); +} +void StopProfilerIdleNotifier(const FunctionCallbackInfo& args) +{ + Environment* env = Environment::GetCurrent(args); if (!env) return; - StopProfilerIdleNotifier(env); - } - - -#define READONLY_PROPERTY(obj, str, var) \ - do { \ - obj->DefineOwnProperty(env->context(), \ - OneByteString(env->isolate(), str), \ - var, \ - v8::ReadOnly).FromJust(); \ - } while (0) - -#define READONLY_DONT_ENUM_PROPERTY(obj, str, var) \ - do { \ - obj->DefineOwnProperty(env->context(), \ - OneByteString(env->isolate(), str), \ - var, \ - static_cast(v8::ReadOnly | \ - v8::DontEnum)) \ - .FromJust(); \ - } while (0) - - //安装进程数据 - void SetupProcessObject(Environment* env, int argc, const char* const* argv, int exec_argc, const char* const* exec_argv) { - HandleScope scope(env->isolate()); - - Local process = env->process_object(); - - auto title_string = FIXED_ONE_BYTE_STRING(env->isolate(), "title"); - CHECK(process->SetAccessor(env->context(), - title_string, - ProcessTitleGetter, - ProcessTitleSetter, - env->as_external()).FromJust()); - - // process.version - READONLY_PROPERTY(process, - "version", - FIXED_ONE_BYTE_STRING(env->isolate(), NODE_VERSION)); - - // process.moduleLoadList - READONLY_PROPERTY(process, - "moduleLoadList", - env->module_load_list_array()); - - // process.versions - Local versions = Object::New(env->isolate()); - READONLY_PROPERTY(process, "versions", versions); - - const char http_parser_version[] = NODE_STRINGIFY(HTTP_PARSER_VERSION_MAJOR) - "." - NODE_STRINGIFY(HTTP_PARSER_VERSION_MINOR) - "." - NODE_STRINGIFY(HTTP_PARSER_VERSION_PATCH); - READONLY_PROPERTY(versions, - "http_parser", - FIXED_ONE_BYTE_STRING(env->isolate(), http_parser_version)); - - // +1 to get rid of the leading 'v' - READONLY_PROPERTY(versions, - "node", - OneByteString(env->isolate(), NODE_VERSION + 1)); - READONLY_PROPERTY(versions, - "uv", - OneByteString(env->isolate(), uv_version_string())); - READONLY_PROPERTY(versions, - "zlib", - FIXED_ONE_BYTE_STRING(env->isolate(), ZLIB_VERSION)); - READONLY_PROPERTY(versions, - "ares", - FIXED_ONE_BYTE_STRING(env->isolate(), ARES_VERSION_STR)); - - const char node_modules_version[] = NODE_STRINGIFY(NODE_MODULE_VERSION); - READONLY_PROPERTY( - versions, - "modules", - FIXED_ONE_BYTE_STRING(env->isolate(), node_modules_version)); - - // process._promiseRejectEvent - Local promiseRejectEvent = Object::New(env->isolate()); - READONLY_DONT_ENUM_PROPERTY(process, - "_promiseRejectEvent", - promiseRejectEvent); - READONLY_PROPERTY(promiseRejectEvent, - "unhandled", - Integer::New(env->isolate(), - v8::kPromiseRejectWithNoHandler)); - READONLY_PROPERTY(promiseRejectEvent, - "handled", - Integer::New(env->isolate(), - v8::kPromiseHandlerAddedAfterReject)); + StopProfilerIdleNotifier(env); +} + +#define READONLY_PROPERTY(obj, str, var) \ + do { \ + obj->DefineOwnProperty(env->context(), \ + OneByteString(env->isolate(), str), \ + var, \ + v8::ReadOnly) \ + .FromJust(); \ + } while (0) + +#define READONLY_DONT_ENUM_PROPERTY(obj, str, var) \ + do { \ + obj->DefineOwnProperty(env->context(), \ + OneByteString(env->isolate(), str), \ + var, \ + static_cast(v8::ReadOnly | v8::DontEnum)) \ + .FromJust(); \ + } while (0) + +//安装进程数据 +void SetupProcessObject(Environment* env, int argc, const char* const* argv, int exec_argc, const char* const* exec_argv) +{ + HandleScope scope(env->isolate()); + + Local process = env->process_object(); + + auto title_string = FIXED_ONE_BYTE_STRING(env->isolate(), "title"); + CHECK(process->SetAccessor(env->context(), + title_string, + ProcessTitleGetter, + ProcessTitleSetter, + env->as_external()) + .FromJust()); + + // process.version + READONLY_PROPERTY(process, + "version", + FIXED_ONE_BYTE_STRING(env->isolate(), NODE_VERSION)); + + // process.moduleLoadList + READONLY_PROPERTY(process, + "moduleLoadList", + env->module_load_list_array()); + + // process.versions + Local versions = Object::New(env->isolate()); + READONLY_PROPERTY(process, "versions", versions); + + const char http_parser_version[] = NODE_STRINGIFY(HTTP_PARSER_VERSION_MAJOR) "." NODE_STRINGIFY(HTTP_PARSER_VERSION_MINOR) "." NODE_STRINGIFY(HTTP_PARSER_VERSION_PATCH); + READONLY_PROPERTY(versions, + "http_parser", + FIXED_ONE_BYTE_STRING(env->isolate(), http_parser_version)); + + // +1 to get rid of the leading 'v' + READONLY_PROPERTY(versions, + "node", + OneByteString(env->isolate(), NODE_VERSION + 1)); + READONLY_PROPERTY(versions, + "uv", + OneByteString(env->isolate(), uv_version_string())); + READONLY_PROPERTY(versions, + "zlib", + FIXED_ONE_BYTE_STRING(env->isolate(), ZLIB_VERSION)); + READONLY_PROPERTY(versions, + "ares", + FIXED_ONE_BYTE_STRING(env->isolate(), ARES_VERSION_STR)); + + const char node_modules_version[] = NODE_STRINGIFY(NODE_MODULE_VERSION); + READONLY_PROPERTY( + versions, + "modules", + FIXED_ONE_BYTE_STRING(env->isolate(), node_modules_version)); + + // process._promiseRejectEvent + Local promiseRejectEvent = Object::New(env->isolate()); + READONLY_DONT_ENUM_PROPERTY(process, + "_promiseRejectEvent", + promiseRejectEvent); + READONLY_PROPERTY(promiseRejectEvent, + "unhandled", + Integer::New(env->isolate(), + v8::kPromiseRejectWithNoHandler)); + READONLY_PROPERTY(promiseRejectEvent, + "handled", + Integer::New(env->isolate(), + v8::kPromiseHandlerAddedAfterReject)); #if HAVE_OPENSSL - // Stupid code to slice out the version string. - { // NOLINT(whitespace/braces) - size_t i, j, k; - int c; - for (i = j = 0, k = sizeof(OPENSSL_VERSION_TEXT) - 1; i < k; ++i) { - c = OPENSSL_VERSION_TEXT[i]; - if ('0' <= c && c <= '9') { - for (j = i + 1; j < k; ++j) { - c = OPENSSL_VERSION_TEXT[j]; - if (c == ' ') - break; - } - break; - } - } - READONLY_PROPERTY( - versions, - "openssl", - OneByteString(env->isolate(), &OPENSSL_VERSION_TEXT[i], j - i)); - } + // Stupid code to slice out the version string. + { // NOLINT(whitespace/braces) + size_t i, j, k; + int c; + for (i = j = 0, k = sizeof(OPENSSL_VERSION_TEXT) - 1; i < k; ++i) { + c = OPENSSL_VERSION_TEXT[i]; + if ('0' <= c && c <= '9') { + for (j = i + 1; j < k; ++j) { + c = OPENSSL_VERSION_TEXT[j]; + if (c == ' ') + break; + } + break; + } + } + READONLY_PROPERTY( + versions, + "openssl", + OneByteString(env->isolate(), &OPENSSL_VERSION_TEXT[i], j - i)); + } #else READONLY_PROPERTY( versions, @@ -2814,1081 +2797,1045 @@ namespace node { OneByteString(env->isolate(), "0", 1)); #endif - // process.arch - READONLY_PROPERTY(process, "arch", OneByteString(env->isolate(), NODE_ARCH)); + // process.arch + READONLY_PROPERTY(process, "arch", OneByteString(env->isolate(), NODE_ARCH)); - // process.platform - READONLY_PROPERTY(process, - "platform", - OneByteString(env->isolate(), NODE_PLATFORM)); + // process.platform + READONLY_PROPERTY(process, + "platform", + OneByteString(env->isolate(), NODE_PLATFORM)); - // process.release - Local release = Object::New(env->isolate()); - READONLY_PROPERTY(process, "release", release); - READONLY_PROPERTY(release, "name", OneByteString(env->isolate(), "node")); + // process.release + Local release = Object::New(env->isolate()); + READONLY_PROPERTY(process, "release", release); + READONLY_PROPERTY(release, "name", OneByteString(env->isolate(), "node")); #if NODE_VERSION_IS_LTS - READONLY_PROPERTY(release, "lts", - OneByteString(env->isolate(), NODE_VERSION_LTS_CODENAME)); + READONLY_PROPERTY(release, "lts", + OneByteString(env->isolate(), NODE_VERSION_LTS_CODENAME)); #endif - // if this is a release build and no explicit base has been set - // substitute the standard release download URL + // if this is a release build and no explicit base has been set + // substitute the standard release download URL #ifndef NODE_RELEASE_URLBASE -# if NODE_VERSION_IS_RELEASE -# define NODE_RELEASE_URLBASE "https://nodejs.org/download/release/" -# endif +#if NODE_VERSION_IS_RELEASE +#define NODE_RELEASE_URLBASE "https://nodejs.org/download/release/" +#endif #endif #if defined(NODE_RELEASE_URLBASE) -# define NODE_RELEASE_URLPFX NODE_RELEASE_URLBASE "v" NODE_VERSION_STRING "/" -# define NODE_RELEASE_URLFPFX NODE_RELEASE_URLPFX "node-v" NODE_VERSION_STRING - - READONLY_PROPERTY(release, - "sourceUrl", - OneByteString(env->isolate(), - NODE_RELEASE_URLFPFX ".tar.gz")); - READONLY_PROPERTY(release, - "headersUrl", - OneByteString(env->isolate(), - NODE_RELEASE_URLFPFX "-headers.tar.gz")); -# ifdef _WIN32 - READONLY_PROPERTY(release, - "libUrl", - OneByteString(env->isolate(), - strcmp(NODE_ARCH, "ia32") ? NODE_RELEASE_URLPFX "win-" - NODE_ARCH "/node.lib" - : NODE_RELEASE_URLPFX - "win-x86/node.lib")); -# endif -#endif - - // process.argv - Local arguments = Array::New(env->isolate(), argc); - for (int i = 0; i < argc; ++i) { - arguments->Set(i, String::NewFromUtf8(env->isolate(), argv[i])); - } - process->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "argv"), arguments); - - // process.execArgv - Local exec_arguments = Array::New(env->isolate(), exec_argc); - for (int i = 0; i < exec_argc; ++i) { - exec_arguments->Set(i, String::NewFromUtf8(env->isolate(), exec_argv[i])); - } - process->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "execArgv"), - exec_arguments); - - // create process.env - Local process_env_template = - ObjectTemplate::New(env->isolate()); - process_env_template->SetHandler(NamedPropertyHandlerConfiguration( - EnvGetter, - EnvSetter, - EnvQuery, - EnvDeleter, - EnvEnumerator, - env->as_external(), - PropertyHandlerFlags::kOnlyInterceptStrings)); - - Local process_env = - process_env_template->NewInstance(env->context()).ToLocalChecked(); - process->Set(env->env_string(), process_env); - - READONLY_PROPERTY(process, "pid", Integer::New(env->isolate(), getpid())); - READONLY_PROPERTY(process, "features", GetFeatures(env)); - - auto need_immediate_callback_string = - FIXED_ONE_BYTE_STRING(env->isolate(), "_needImmediateCallback"); - CHECK(process->SetAccessor(env->context(), need_immediate_callback_string, - NeedImmediateCallbackGetter, - NeedImmediateCallbackSetter, - env->as_external()).FromJust()); - - // -e, --eval - if (eval_string) { - READONLY_PROPERTY(process, - "_eval", - String::NewFromUtf8(env->isolate(), eval_string)); - } - - // -p, --print - if (print_eval) { - READONLY_PROPERTY(process, "_print_eval", True(env->isolate())); - } - - // -c, --check - if (syntax_check_only) { - READONLY_PROPERTY(process, "_syntax_check_only", True(env->isolate())); - } - - // -i, --interactive - if (force_repl) { - READONLY_PROPERTY(process, "_forceRepl", True(env->isolate())); - } - - if (preload_module_count) { - CHECK(preload_modules); - Local array = Array::New(env->isolate()); - for (unsigned int i = 0; i < preload_module_count; ++i) { - Local module = String::NewFromUtf8(env->isolate(), - preload_modules[i]); - array->Set(i, module); - } - READONLY_PROPERTY(process, - "_preload_modules", - array); - - delete[] preload_modules; - preload_modules = nullptr; - preload_module_count = 0; - } - - // --no-deprecation - if (no_deprecation) { - READONLY_PROPERTY(process, "noDeprecation", True(env->isolate())); - } - - if (no_process_warnings) { - READONLY_PROPERTY(process, "noProcessWarnings", True(env->isolate())); - } - - if (trace_warnings) { - READONLY_PROPERTY(process, "traceProcessWarnings", True(env->isolate())); - } - - // --throw-deprecation - if (throw_deprecation) { - READONLY_PROPERTY(process, "throwDeprecation", True(env->isolate())); - } - - - // --prof-process - if (prof_process) { - READONLY_PROPERTY(process, "profProcess", True(env->isolate())); - } - - // --trace-deprecation - if (trace_deprecation) { - READONLY_PROPERTY(process, "traceDeprecation", True(env->isolate())); - } - - // --debug-brk - if (debug_wait_connect) { - READONLY_PROPERTY(process, "_debugWaitConnect", True(env->isolate())); - } - - // --security-revert flags -#define V(code, _, __) \ - do { \ - if (IsReverted(REVERT_ ## code)) { \ - READONLY_PROPERTY(process, "REVERT_" #code, True(env->isolate())); \ - } \ - } while (0); - REVERSIONS(V) -#undef V +#define NODE_RELEASE_URLPFX NODE_RELEASE_URLBASE "v" NODE_VERSION_STRING "/" +#define NODE_RELEASE_URLFPFX NODE_RELEASE_URLPFX "node-v" NODE_VERSION_STRING + + READONLY_PROPERTY(release, + "sourceUrl", + OneByteString(env->isolate(), + NODE_RELEASE_URLFPFX ".tar.gz")); + READONLY_PROPERTY(release, + "headersUrl", + OneByteString(env->isolate(), + NODE_RELEASE_URLFPFX "-headers.tar.gz")); +#ifdef _WIN32 + READONLY_PROPERTY(release, + "libUrl", + OneByteString(env->isolate(), + strcmp(NODE_ARCH, "ia32") ? NODE_RELEASE_URLPFX "win-" NODE_ARCH "/node.lib" + : NODE_RELEASE_URLPFX + "win-x86/node.lib")); +#endif +#endif + + // process.argv + Local arguments = Array::New(env->isolate(), argc); + for (int i = 0; i < argc; ++i) { + arguments->Set(i, String::NewFromUtf8(env->isolate(), argv[i])); + } + process->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "argv"), arguments); - size_t exec_path_len = 2 * PATH_MAX; - char* exec_path = new char[exec_path_len]; - Local exec_path_value; - if (uv_exepath(exec_path, &exec_path_len) == 0) { - exec_path_value = String::NewFromUtf8(env->isolate(), - exec_path, - String::kNormalString, - exec_path_len); - } - else { - exec_path_value = String::NewFromUtf8(env->isolate(), argv[0]); - } - process->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "execPath"), - exec_path_value); - delete[] exec_path; - - auto debug_port_string = FIXED_ONE_BYTE_STRING(env->isolate(), "debugPort"); - CHECK(process->SetAccessor(env->context(), - debug_port_string, - DebugPortGetter, - DebugPortSetter, - env->as_external()).FromJust()); - - // define various internal methods - env->SetMethod(process, - "_startProfilerIdleNotifier", - StartProfilerIdleNotifier); - env->SetMethod(process, - "_stopProfilerIdleNotifier", - StopProfilerIdleNotifier); - env->SetMethod(process, "_getActiveRequests", GetActiveRequests); - env->SetMethod(process, "_getActiveHandles", GetActiveHandles); - env->SetMethod(process, "reallyExit", Exit); - env->SetMethod(process, "abort", Abort); - env->SetMethod(process, "chdir", Chdir); - env->SetMethod(process, "cwd", Cwd); - - env->SetMethod(process, "umask", Umask); - - env->SetMethod(process, "_kill", Kill); - - env->SetMethod(process, "_debugProcess", DebugProcess); - env->SetMethod(process, "_debugPause", DebugPause); - env->SetMethod(process, "_debugEnd", DebugEnd); - - env->SetMethod(process, "hrtime", Hrtime); - - env->SetMethod(process, "cpuUsage", CPUUsage); - - env->SetMethod(process, "dlopen", DLOpen); - - env->SetMethod(process, "uptime", Uptime); - env->SetMethod(process, "memoryUsage", MemoryUsage); - - env->SetMethod(process, "binding", Binding); - env->SetMethod(process, "_linkedBinding", LinkedBinding); - - env->SetMethod(process, "_setupProcessObject", SetupProcessObject); - env->SetMethod(process, "_setupNextTick", SetupNextTick); - env->SetMethod(process, "_setupPromises", SetupPromises); - env->SetMethod(process, "_setupDomainUse", SetupDomainUse); - - // pre-set _events object for faster emit checks - Local events_obj = Object::New(env->isolate()); - CHECK(events_obj->SetPrototype(env->context(), - Null(env->isolate())).FromJust()); - process->Set(env->events_string(), events_obj); - } + // process.execArgv + Local exec_arguments = Array::New(env->isolate(), exec_argc); + for (int i = 0; i < exec_argc; ++i) { + exec_arguments->Set(i, String::NewFromUtf8(env->isolate(), exec_argv[i])); + } + process->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "execArgv"), + exec_arguments); + + // create process.env + Local process_env_template = ObjectTemplate::New(env->isolate()); + process_env_template->SetHandler(NamedPropertyHandlerConfiguration( + EnvGetter, + EnvSetter, + EnvQuery, + EnvDeleter, + EnvEnumerator, + env->as_external(), + PropertyHandlerFlags::kOnlyInterceptStrings)); + + Local process_env = process_env_template->NewInstance(env->context()).ToLocalChecked(); + process->Set(env->env_string(), process_env); + + READONLY_PROPERTY(process, "pid", Integer::New(env->isolate(), getpid())); + READONLY_PROPERTY(process, "features", GetFeatures(env)); + + auto need_immediate_callback_string = FIXED_ONE_BYTE_STRING(env->isolate(), "_needImmediateCallback"); + CHECK(process->SetAccessor(env->context(), need_immediate_callback_string, + NeedImmediateCallbackGetter, + NeedImmediateCallbackSetter, + env->as_external()) + .FromJust()); + + // -e, --eval + if (eval_string) { + READONLY_PROPERTY(process, + "_eval", + String::NewFromUtf8(env->isolate(), eval_string)); + } + // -p, --print + if (print_eval) { + READONLY_PROPERTY(process, "_print_eval", True(env->isolate())); + } -#undef READONLY_PROPERTY + // -c, --check + if (syntax_check_only) { + READONLY_PROPERTY(process, "_syntax_check_only", True(env->isolate())); + } + // -i, --interactive + if (force_repl) { + READONLY_PROPERTY(process, "_forceRepl", True(env->isolate())); + } - static void AtProcessExit() { - //zero - //uv_tty_reset_mode(); - } + if (preload_module_count) { + CHECK(preload_modules); + Local array = Array::New(env->isolate()); + for (unsigned int i = 0; i < preload_module_count; ++i) { + Local module = String::NewFromUtf8(env->isolate(), + preload_modules[i]); + array->Set(i, module); + } + READONLY_PROPERTY(process, + "_preload_modules", + array); + delete[] preload_modules; + preload_modules = nullptr; + preload_module_count = 0; + } - void SignalExit(int signo) { - //zero - //uv_tty_reset_mode(); - raise(signo); - } + // --no-deprecation + if (no_deprecation) { + READONLY_PROPERTY(process, "noDeprecation", True(env->isolate())); + } + if (no_process_warnings) { + READONLY_PROPERTY(process, "noProcessWarnings", True(env->isolate())); + } - // Most of the time, it's best to use `console.error` to write - // to the process.stderr stream. However, in some cases, such as - // when debugging the stream.Writable class or the process.nextTick - // function, it is useful to bypass JavaScript entirely. - static void RawDebug(const FunctionCallbackInfo& args) { - CHECK(args.Length() == 1 && args[0]->IsString() && - "must be called with a single string"); - node::Utf8Value message(args.GetIsolate(), args[0]); - PrintErrorString("%s\n", *message); - fflush(stderr); - } + if (trace_warnings) { + READONLY_PROPERTY(process, "traceProcessWarnings", True(env->isolate())); + } + // --throw-deprecation + if (throw_deprecation) { + READONLY_PROPERTY(process, "throwDeprecation", True(env->isolate())); + } - void LoadEnvironment(Environment* env) { - HandleScope handle_scope(env->isolate()); + // --prof-process + if (prof_process) { + READONLY_PROPERTY(process, "profProcess", True(env->isolate())); + } - env->isolate()->SetFatalErrorHandler(node::OnFatalError); - env->isolate()->AddMessageListener(OnMessage); + // --trace-deprecation + if (trace_deprecation) { + READONLY_PROPERTY(process, "traceDeprecation", True(env->isolate())); + } + + // --debug-brk + if (debug_wait_connect) { + READONLY_PROPERTY(process, "_debugWaitConnect", True(env->isolate())); + } + + // --security-revert flags +#define V(code, _, __) \ + do { \ + if (IsReverted(REVERT_##code)) { \ + READONLY_PROPERTY(process, "REVERT_" #code, True(env->isolate())); \ + } \ + } while (0); + REVERSIONS(V) +#undef V + + size_t exec_path_len = 2 * PATH_MAX; + char* exec_path = new char[exec_path_len]; + Local exec_path_value; + if (uv_exepath(exec_path, &exec_path_len) == 0) { + exec_path_value = String::NewFromUtf8(env->isolate(), + exec_path, + String::kNormalString, + exec_path_len); + } else { + exec_path_value = String::NewFromUtf8(env->isolate(), argv[0]); + } + process->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "execPath"), + exec_path_value); + delete[] exec_path; + + auto debug_port_string = FIXED_ONE_BYTE_STRING(env->isolate(), "debugPort"); + CHECK(process->SetAccessor(env->context(), + debug_port_string, + DebugPortGetter, + DebugPortSetter, + env->as_external()) + .FromJust()); + + // define various internal methods + env->SetMethod(process, + "_startProfilerIdleNotifier", + StartProfilerIdleNotifier); + env->SetMethod(process, + "_stopProfilerIdleNotifier", + StopProfilerIdleNotifier); + env->SetMethod(process, "_getActiveRequests", GetActiveRequests); + env->SetMethod(process, "_getActiveHandles", GetActiveHandles); + env->SetMethod(process, "reallyExit", Exit); + env->SetMethod(process, "abort", Abort); + env->SetMethod(process, "chdir", Chdir); + env->SetMethod(process, "cwd", Cwd); + + env->SetMethod(process, "umask", Umask); + + env->SetMethod(process, "_kill", Kill); + + env->SetMethod(process, "_debugProcess", DebugProcess); + env->SetMethod(process, "_debugPause", DebugPause); + env->SetMethod(process, "_debugEnd", DebugEnd); + + env->SetMethod(process, "hrtime", Hrtime); + + env->SetMethod(process, "cpuUsage", CPUUsage); + + env->SetMethod(process, "dlopen", DLOpen); + + env->SetMethod(process, "uptime", Uptime); + env->SetMethod(process, "memoryUsage", MemoryUsage); + + env->SetMethod(process, "binding", Binding); + env->SetMethod(process, "_linkedBinding", LinkedBinding); + + env->SetMethod(process, "_setupProcessObject", SetupProcessObject); + env->SetMethod(process, "_setupNextTick", SetupNextTick); + env->SetMethod(process, "_setupPromises", SetupPromises); + env->SetMethod(process, "_setupDomainUse", SetupDomainUse); + + // pre-set _events object for faster emit checks + Local events_obj = Object::New(env->isolate()); + CHECK(events_obj->SetPrototype(env->context(), + Null(env->isolate())) + .FromJust()); + process->Set(env->events_string(), events_obj); +} + +#undef READONLY_PROPERTY + +static void AtProcessExit() +{ + //zero + //uv_tty_reset_mode(); +} + +void SignalExit(int signo) +{ + //zero + //uv_tty_reset_mode(); + raise(signo); +} + +// Most of the time, it's best to use `console.error` to write +// to the process.stderr stream. However, in some cases, such as +// when debugging the stream.Writable class or the process.nextTick +// function, it is useful to bypass JavaScript entirely. +static void RawDebug(const FunctionCallbackInfo& args) +{ + CHECK(args.Length() == 1 && args[0]->IsString() && "must be called with a single string"); + node::Utf8Value message(args.GetIsolate(), args[0]); + PrintErrorString("%s\n", *message); + fflush(stderr); +} + +void LoadEnvironment(Environment* env) +{ + HandleScope handle_scope(env->isolate()); + + env->isolate()->SetFatalErrorHandler(node::OnFatalError); + env->isolate()->AddMessageListener(OnMessage); env->isolate()->SetCaptureStackTraceForUncaughtExceptions(true, 50, v8::StackTrace::kDetailed); - atexit(AtProcessExit);//注册退出函数 + atexit(AtProcessExit); //注册退出函数 - TryCatch try_catch(env->isolate()); + TryCatch try_catch(env->isolate()); - // Disable verbose mode to stop FatalException() handler from trying - // to handle the exception. Errors this early in the start-up phase - // are not safe to ignore. - try_catch.SetVerbose(false); + // Disable verbose mode to stop FatalException() handler from trying + // to handle the exception. Errors this early in the start-up phase + // are not safe to ignore. + try_catch.SetVerbose(false); - // Execute the lib/internal/bootstrap_node.js file which was included as a - // static C string in node_natives.h by node_js2c. - // 'internal_bootstrap_node_native' is the string containing that source code. - Local script_name = FIXED_ONE_BYTE_STRING(env->isolate(), "bootstrap_node.js"); - Local f_value = ExecuteString(env, MainSource(env), script_name); - if (try_catch.HasCaught()) { - ReportException(env, try_catch); - //exit(10); - } - // The bootstrap_node.js file returns a function 'f' - CHECK(f_value->IsFunction()); - Local f = Local::Cast(f_value); + // Execute the lib/internal/bootstrap_node.js file which was included as a + // static C string in node_natives.h by node_js2c. + // 'internal_bootstrap_node_native' is the string containing that source code. + Local script_name = FIXED_ONE_BYTE_STRING(env->isolate(), "bootstrap_node.js"); + Local f_value = ExecuteString(env, MainSource(env), script_name); + if (try_catch.HasCaught()) { + ReportException(env, try_catch); + //exit(10); + } + // The bootstrap_node.js file returns a function 'f' + CHECK(f_value->IsFunction()); + Local f = Local::Cast(f_value); - // Add a reference to the global object - Local global = env->context()->Global(); + // Add a reference to the global object + Local global = env->context()->Global(); #if defined HAVE_DTRACE || defined HAVE_ETW - InitDTrace(env, global); + InitDTrace(env, global); #endif - #if defined HAVE_PERFCTR - InitPerfCounters(env, global); -#endif - - // Enable handling of uncaught exceptions - // (FatalException(), break on uncaught exception in debugger) - // - // This is not strictly necessary since it's almost impossible - // to attach the debugger fast enought to break on exception - // thrown during process startup. - try_catch.SetVerbose(true); - - env->SetMethod(env->process_object(), "_rawDebug", RawDebug); - - // Expose the global object as a property on itself - // (Allows you to set stuff on `global` from anywhere in JavaScript.) - global->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "global"), global); - - // Now we call 'f' with the 'process' variable that we've built up with - // all our bindings. Inside bootstrap_node.js and internal/process we'll - // take care of assigning things to their places. - - // We start the process this way in order to be more modular. Developers - // who do not like how bootstrap_node.js sets up the module system but do - // like Node's I/O bindings may want to replace 'f' with their own function. - Local arg = env->process_object(); - f->Call(Null(env->isolate()), 1, &arg); - } - void FreeEnvironment(Environment* env) { - CHECK_NE(env, nullptr); - env->Dispose(); - } - - //解析调试参数 - static bool ParseDebugOpt(const char* arg) { - const char* port = nullptr; - - if (!strcmp(arg, "--debug")) { - use_debug_agent = true; - } - else if (!strncmp(arg, "--debug=", sizeof("--debug=") - 1)) { - use_debug_agent = true; - port = arg + sizeof("--debug=") - 1; - } - else if (!strcmp(arg, "--debug-brk")) { - use_debug_agent = true; - debug_wait_connect = true; - } - else if (!strncmp(arg, "--debug-brk=", sizeof("--debug-brk=") - 1)) { - use_debug_agent = true; - debug_wait_connect = true; - port = arg + sizeof("--debug-brk=") - 1; - } - else if (!strncmp(arg, "--debug-port=", sizeof("--debug-port=") - 1)) { - // XXX(bnoordhuis) Misnomer, configures port and listen address. - port = arg + sizeof("--debug-port=") - 1; - } - else { - return false; - } - - if (port == nullptr) { - return true; - } - - std::string* const the_host = &debug_host; - int* const the_port = &debug_port; - - // FIXME(bnoordhuis) Move IPv6 address parsing logic to lib/net.js. - // It seems reasonable to support [address]:port notation - // in net.Server#listen() and net.Socket#connect(). - const size_t port_len = strlen(port); - if (port[0] == '[' && port[port_len - 1] == ']') { - the_host->assign(port + 1, port_len - 2); - return true; - } - - const char* const colon = strrchr(port, ':'); - if (colon == nullptr) { - // Either a port number or a host name. Assume that - // if it's not all decimal digits, it's a host name. - for (size_t n = 0; port[n] != '\0'; n += 1) { - if (port[n] < '0' || port[n] > '9') { - *the_host = port; - return true; - } - } - } - else { - const bool skip = (colon > port && port[0] == '[' && colon[-1] == ']'); - the_host->assign(port + skip, colon - skip); - } - - char* endptr; - errno = 0; - const char* const digits = colon != nullptr ? colon + 1 : port; - const long result = strtol(digits, &endptr, 10); // NOLINT(runtime/int) - if (errno != 0 || *endptr != '\0' || result < 1024 || result > 65535) { - fprintf(stderr, "Debug port must be in range 1024 to 65535.\n"); - exit(12); - } - - *the_port = static_cast(result); - - return true; - } - - static void ParseArgs(int* argc, const char** argv, int* exec_argc, const char*** exec_argv, int* v8_argc, const char*** v8_argv) { - const unsigned int nargs = static_cast(*argc); - const char** new_exec_argv = new const char*[nargs]; - const char** new_v8_argv = new const char*[nargs]; - const char** new_argv = new const char*[nargs]; - const char** local_preload_modules = new const char*[nargs]; - - for (unsigned int i = 0; i < nargs; ++i) { - new_exec_argv[i] = nullptr; - new_v8_argv[i] = nullptr; - new_argv[i] = nullptr; - local_preload_modules[i] = nullptr; - } - - // exec_argv starts with the first option, the other two start with argv[0]. - unsigned int new_exec_argc = 0; - unsigned int new_v8_argc = 1; - unsigned int new_argc = 1; - new_v8_argv[0] = argv[0]; - new_argv[0] = argv[0]; - - unsigned int index = 1; - bool short_circuit = false; - while (index < nargs && argv[index][0] == '-' && !short_circuit) { - const char* const arg = argv[index]; - unsigned int args_consumed = 1; - - if (ParseDebugOpt(arg)) { - // Done, consumed by ParseDebugOpt(). - } - else if (strcmp(arg, "--version") == 0 || strcmp(arg, "-v") == 0) { - printf("%s\n", NODE_VERSION); - exit(0); - } - else if (strcmp(arg, "--eval") == 0 || - strcmp(arg, "-e") == 0 || - strcmp(arg, "--print") == 0 || - strcmp(arg, "-pe") == 0 || - strcmp(arg, "-p") == 0) { - bool is_eval = strchr(arg, 'e') != nullptr; - bool is_print = strchr(arg, 'p') != nullptr; - print_eval = print_eval || is_print; - // --eval, -e and -pe always require an argument. - if (is_eval == true) { - args_consumed += 1; - eval_string = argv[index + 1]; - if (eval_string == nullptr) { - fprintf(stderr, "%s: %s requires an argument\n", argv[0], arg); - exit(9); - } - } - else if ((index + 1 < nargs) && - argv[index + 1] != nullptr && - argv[index + 1][0] != '-') { - args_consumed += 1; - eval_string = argv[index + 1]; - if (strncmp(eval_string, "\\-", 2) == 0) { - // Starts with "\\-": escaped expression, drop the backslash. - eval_string += 1; - } - } - } - else if (strcmp(arg, "--require") == 0 || - strcmp(arg, "-r") == 0) { - const char* module = argv[index + 1]; - if (module == nullptr) { - fprintf(stderr, "%s: %s requires an argument\n", argv[0], arg); - exit(9); - } - args_consumed += 1; - local_preload_modules[preload_module_count++] = module; - } - else if (strcmp(arg, "--check") == 0 || strcmp(arg, "-c") == 0) { - syntax_check_only = true; - } - else if (strcmp(arg, "--interactive") == 0 || strcmp(arg, "-i") == 0) { - force_repl = true; - } - else if (strcmp(arg, "--no-deprecation") == 0) { - no_deprecation = true; - } - else if (strcmp(arg, "--no-warnings") == 0) { - no_process_warnings = true; - } - else if (strcmp(arg, "--trace-warnings") == 0) { - trace_warnings = true; - } - else if (strcmp(arg, "--trace-deprecation") == 0) { - trace_deprecation = true; - } - else if (strcmp(arg, "--trace-sync-io") == 0) { - trace_sync_io = true; - } - else if (strcmp(arg, "--track-heap-objects") == 0) { - track_heap_objects = true; - } - else if (strcmp(arg, "--throw-deprecation") == 0) { - throw_deprecation = true; - } - else if (strncmp(arg, "--security-revert=", 18) == 0) { - const char* cve = arg + 18; - Revert(cve); - } - else if (strcmp(arg, "--preserve-symlinks") == 0) { - config_preserve_symlinks = true; - } - else if (strcmp(arg, "--prof-process") == 0) { - prof_process = true; - short_circuit = true; - } - else if (strcmp(arg, "--zero-fill-buffers") == 0) { - zero_fill_all_buffers = true; - } - else if (strcmp(arg, "--v8-options") == 0) { - new_v8_argv[new_v8_argc] = "--help"; - new_v8_argc += 1; - } - else if (strncmp(arg, "--v8-pool-size=", 15) == 0) { - v8_thread_pool_size = atoi(arg + 15); + InitPerfCounters(env, global); +#endif + + // Enable handling of uncaught exceptions + // (FatalException(), break on uncaught exception in debugger) + // + // This is not strictly necessary since it's almost impossible + // to attach the debugger fast enought to break on exception + // thrown during process startup. + try_catch.SetVerbose(true); + + env->SetMethod(env->process_object(), "_rawDebug", RawDebug); + + // Expose the global object as a property on itself + // (Allows you to set stuff on `global` from anywhere in JavaScript.) + global->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "global"), global); + + // Now we call 'f' with the 'process' variable that we've built up with + // all our bindings. Inside bootstrap_node.js and internal/process we'll + // take care of assigning things to their places. + + // We start the process this way in order to be more modular. Developers + // who do not like how bootstrap_node.js sets up the module system but do + // like Node's I/O bindings may want to replace 'f' with their own function. + Local arg = env->process_object(); + f->Call(Null(env->isolate()), 1, &arg); +} +void FreeEnvironment(Environment* env) +{ + CHECK_NE(env, nullptr); + env->Dispose(); +} + +//解析调试参数 +static bool ParseDebugOpt(const char* arg) +{ + const char* port = nullptr; + + if (!strcmp(arg, "--debug")) { + use_debug_agent = true; + } else if (!strncmp(arg, "--debug=", sizeof("--debug=") - 1)) { + use_debug_agent = true; + port = arg + sizeof("--debug=") - 1; + } else if (!strcmp(arg, "--debug-brk")) { + use_debug_agent = true; + debug_wait_connect = true; + } else if (!strncmp(arg, "--debug-brk=", sizeof("--debug-brk=") - 1)) { + use_debug_agent = true; + debug_wait_connect = true; + port = arg + sizeof("--debug-brk=") - 1; + } else if (!strncmp(arg, "--debug-port=", sizeof("--debug-port=") - 1)) { + // XXX(bnoordhuis) Misnomer, configures port and listen address. + port = arg + sizeof("--debug-port=") - 1; + } else { + return false; + } + + if (port == nullptr) { + return true; + } + + std::string* const the_host = &debug_host; + int* const the_port = &debug_port; + + // FIXME(bnoordhuis) Move IPv6 address parsing logic to lib/net.js. + // It seems reasonable to support [address]:port notation + // in net.Server#listen() and net.Socket#connect(). + const size_t port_len = strlen(port); + if (port[0] == '[' && port[port_len - 1] == ']') { + the_host->assign(port + 1, port_len - 2); + return true; + } + + const char* const colon = strrchr(port, ':'); + if (colon == nullptr) { + // Either a port number or a host name. Assume that + // if it's not all decimal digits, it's a host name. + for (size_t n = 0; port[n] != '\0'; n += 1) { + if (port[n] < '0' || port[n] > '9') { + *the_host = port; + return true; + } + } + } else { + const bool skip = (colon > port && port[0] == '[' && colon[-1] == ']'); + the_host->assign(port + skip, colon - skip); + } + + char* endptr; + errno = 0; + const char* const digits = colon != nullptr ? colon + 1 : port; + const long result = strtol(digits, &endptr, 10); // NOLINT(runtime/int) + if (errno != 0 || *endptr != '\0' || result < 1024 || result > 65535) { + fprintf(stderr, "Debug port must be in range 1024 to 65535.\n"); + exit(12); + } + + *the_port = static_cast(result); + + return true; +} + +static void ParseArgs(int* argc, const char** argv, int* exec_argc, const char*** exec_argv, int* v8_argc, const char*** v8_argv) +{ + const unsigned int nargs = static_cast(*argc); + const char** new_exec_argv = new const char*[nargs]; + const char** new_v8_argv = new const char*[nargs]; + const char** new_argv = new const char*[nargs]; + const char** local_preload_modules = new const char*[nargs]; + + for (unsigned int i = 0; i < nargs; ++i) { + new_exec_argv[i] = nullptr; + new_v8_argv[i] = nullptr; + new_argv[i] = nullptr; + local_preload_modules[i] = nullptr; + } + + // exec_argv starts with the first option, the other two start with argv[0]. + unsigned int new_exec_argc = 0; + unsigned int new_v8_argc = 1; + unsigned int new_argc = 1; + new_v8_argv[0] = argv[0]; + new_argv[0] = argv[0]; + + unsigned int index = 1; + bool short_circuit = false; + while (index < nargs && argv[index][0] == '-' && !short_circuit) { + const char* const arg = argv[index]; + unsigned int args_consumed = 1; + + if (ParseDebugOpt(arg)) { + // Done, consumed by ParseDebugOpt(). + } else if (strcmp(arg, "--version") == 0 || strcmp(arg, "-v") == 0) { + printf("%s\n", NODE_VERSION); + exit(0); + } else if (strcmp(arg, "--eval") == 0 || strcmp(arg, "-e") == 0 || strcmp(arg, "--print") == 0 || strcmp(arg, "-pe") == 0 || strcmp(arg, "-p") == 0) { + bool is_eval = strchr(arg, 'e') != nullptr; + bool is_print = strchr(arg, 'p') != nullptr; + print_eval = print_eval || is_print; + // --eval, -e and -pe always require an argument. + if (is_eval == true) { + args_consumed += 1; + eval_string = argv[index + 1]; + if (eval_string == nullptr) { + fprintf(stderr, "%s: %s requires an argument\n", argv[0], arg); + exit(9); + } + } else if ((index + 1 < nargs) && argv[index + 1] != nullptr && argv[index + 1][0] != '-') { + args_consumed += 1; + eval_string = argv[index + 1]; + if (strncmp(eval_string, "\\-", 2) == 0) { + // Starts with "\\-": escaped expression, drop the backslash. + eval_string += 1; + } + } + } else if (strcmp(arg, "--require") == 0 || strcmp(arg, "-r") == 0) { + const char* module = argv[index + 1]; + if (module == nullptr) { + fprintf(stderr, "%s: %s requires an argument\n", argv[0], arg); + exit(9); + } + args_consumed += 1; + local_preload_modules[preload_module_count++] = module; + } else if (strcmp(arg, "--check") == 0 || strcmp(arg, "-c") == 0) { + syntax_check_only = true; + } else if (strcmp(arg, "--interactive") == 0 || strcmp(arg, "-i") == 0) { + force_repl = true; + } else if (strcmp(arg, "--no-deprecation") == 0) { + no_deprecation = true; + } else if (strcmp(arg, "--no-warnings") == 0) { + no_process_warnings = true; + } else if (strcmp(arg, "--trace-warnings") == 0) { + trace_warnings = true; + } else if (strcmp(arg, "--trace-deprecation") == 0) { + trace_deprecation = true; + } else if (strcmp(arg, "--trace-sync-io") == 0) { + trace_sync_io = true; + } else if (strcmp(arg, "--track-heap-objects") == 0) { + track_heap_objects = true; + } else if (strcmp(arg, "--throw-deprecation") == 0) { + throw_deprecation = true; + } else if (strncmp(arg, "--security-revert=", 18) == 0) { + const char* cve = arg + 18; + Revert(cve); + } else if (strcmp(arg, "--preserve-symlinks") == 0) { + config_preserve_symlinks = true; + } else if (strcmp(arg, "--prof-process") == 0) { + prof_process = true; + short_circuit = true; + } else if (strcmp(arg, "--zero-fill-buffers") == 0) { + zero_fill_all_buffers = true; + } else if (strcmp(arg, "--v8-options") == 0) { + new_v8_argv[new_v8_argc] = "--help"; + new_v8_argc += 1; + } else if (strncmp(arg, "--v8-pool-size=", 15) == 0) { + v8_thread_pool_size = atoi(arg + 15); #if HAVE_OPENSSL - } - else if (strncmp(arg, "--tls-cipher-list=", 18) == 0) { - default_cipher_list = arg + 18; + } else if (strncmp(arg, "--tls-cipher-list=", 18) == 0) { + default_cipher_list = arg + 18; #if NODE_FIPS_MODE - } - else if (strcmp(arg, "--enable-fips") == 0) { - enable_fips_crypto = true; - } - else if (strcmp(arg, "--force-fips") == 0) { - force_fips_crypto = true; + } else if (strcmp(arg, "--enable-fips") == 0) { + enable_fips_crypto = true; + } else if (strcmp(arg, "--force-fips") == 0) { + force_fips_crypto = true; #endif /* NODE_FIPS_MODE */ - } - else if (strncmp(arg, "--openssl-config=", 17) == 0) { - openssl_config = arg + 17; + } else if (strncmp(arg, "--openssl-config=", 17) == 0) { + openssl_config = arg + 17; #endif /* HAVE_OPENSSL */ - } - else if (strcmp(arg, "--expose-internals") == 0 || - strcmp(arg, "--expose_internals") == 0) { - // consumed in js - } - else if (strcmp(arg, "--type=relauncher") == 0 || strcmp(arg, "---") == 0) { // NodeBindings::initNodeEnv https://github.com/electron/electron/pull/5837/files - - } else { - // V8 option. Pass through as-is. - new_v8_argv[new_v8_argc] = arg; - new_v8_argc += 1; - } - - memcpy(new_exec_argv + new_exec_argc, - argv + index, - args_consumed * sizeof(*argv)); - - new_exec_argc += args_consumed; - index += args_consumed; - } - - // Copy remaining arguments. - const unsigned int args_left = nargs - index; - memcpy(new_argv + new_argc, argv + index, args_left * sizeof(*argv)); - new_argc += args_left; - - *exec_argc = new_exec_argc; - *exec_argv = new_exec_argv; - *v8_argc = new_v8_argc; - *v8_argv = new_v8_argv; - - // Copy new_argv over argv and update argc. - memcpy(argv, new_argv, new_argc * sizeof(*argv)); - delete[] new_argv; - *argc = static_cast(new_argc); - - // Copy the preload_modules from the local array to an appropriately sized - // global array. - if (preload_module_count > 0) { - CHECK(!preload_modules); - preload_modules = new const char*[preload_module_count]; - memcpy(preload_modules, local_preload_modules, - preload_module_count * sizeof(*preload_modules)); - } - delete[] local_preload_modules; - } - - - // Called from V8 Debug Agent TCP thread. - static void DispatchMessagesDebugAgentCallback(Environment* env) { - // TODO(indutny): move async handle to environment - uv_async_send(&dispatch_debug_messages_async); - } - - - static void StartDebug(Environment* env, const char* path, bool wait) { - CHECK(!debugger_running); - env->debugger_agent()->set_dispatch_handler( - DispatchMessagesDebugAgentCallback); - debugger_running = - env->debugger_agent()->Start(debug_host, debug_port, wait); - if (debugger_running == false) { - fprintf(stderr, "Starting debugger on %s:%d failed\n", - debug_host.c_str(), debug_port); - fflush(stderr); - return; - } - } - // Called from the main thread. - static void EnableDebug(Environment* env) { - CHECK(debugger_running); - - // Send message to enable debug in workers - HandleScope handle_scope(env->isolate()); - - Local message = Object::New(env->isolate()); - message->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "cmd"), - FIXED_ONE_BYTE_STRING(env->isolate(), "NODE_DEBUG_ENABLED")); - Local argv[] = { - FIXED_ONE_BYTE_STRING(env->isolate(), "internalMessage"), - message - }; - MakeCallback(env, env->process_object(), "emit", arraysize(argv), argv); - - // Enabled debugger, possibly making it wait on a semaphore - env->debugger_agent()->Enable(); - } - - - // Called from an arbitrary thread. - static void TryStartDebugger() { - Mutex::ScopedLock scoped_lock(node_isolate_mutex); - if (auto isolate = node_isolate) { - v8::Debug::DebugBreak(isolate); - uv_async_send(&dispatch_debug_messages_async); - } - } - - - // Called from the main thread. - static void DispatchDebugMessagesAsyncCallback(uv_async_t* handle) { - Mutex::ScopedLock scoped_lock(node_isolate_mutex); - if (auto isolate = node_isolate) { - if (debugger_running == false) { - fprintf(stderr, "Starting debugger agent.\n"); - - HandleScope scope(isolate); - Environment* env = Environment::GetCurrent(isolate); - if (!env) - return; - Context::Scope context_scope(env->context()); + } else if (strcmp(arg, "--expose-internals") == 0 || strcmp(arg, "--expose_internals") == 0) { + // consumed in js + } else if (strcmp(arg, "--type=relauncher") == 0 || strcmp(arg, "---") == 0) { // NodeBindings::initNodeEnv https://github.com/electron/electron/pull/5837/files + + } else { + // V8 option. Pass through as-is. + new_v8_argv[new_v8_argc] = arg; + new_v8_argc += 1; + } + + memcpy(new_exec_argv + new_exec_argc, + argv + index, + args_consumed * sizeof(*argv)); - StartDebug(env, nullptr, false); - EnableDebug(env); - } + new_exec_argc += args_consumed; + index += args_consumed; + } + + // Copy remaining arguments. + const unsigned int args_left = nargs - index; + memcpy(new_argv + new_argc, argv + index, args_left * sizeof(*argv)); + new_argc += args_left; + + *exec_argc = new_exec_argc; + *exec_argv = new_exec_argv; + *v8_argc = new_v8_argc; + *v8_argv = new_v8_argv; + + // Copy new_argv over argv and update argc. + memcpy(argv, new_argv, new_argc * sizeof(*argv)); + delete[] new_argv; + *argc = static_cast(new_argc); + + // Copy the preload_modules from the local array to an appropriately sized + // global array. + if (preload_module_count > 0) { + CHECK(!preload_modules); + preload_modules = new const char*[preload_module_count]; + memcpy(preload_modules, local_preload_modules, + preload_module_count * sizeof(*preload_modules)); + } + delete[] local_preload_modules; +} + +// Called from V8 Debug Agent TCP thread. +static void DispatchMessagesDebugAgentCallback(Environment* env) +{ + // TODO(indutny): move async handle to environment + uv_async_send(&dispatch_debug_messages_async); +} + +static void StartDebug(Environment* env, const char* path, bool wait) +{ + CHECK(!debugger_running); + env->debugger_agent()->set_dispatch_handler( + DispatchMessagesDebugAgentCallback); + debugger_running = env->debugger_agent()->Start(debug_host, debug_port, wait); + if (debugger_running == false) { + fprintf(stderr, "Starting debugger on %s:%d failed\n", + debug_host.c_str(), debug_port); + fflush(stderr); + return; + } +} +// Called from the main thread. +static void EnableDebug(Environment* env) +{ + CHECK(debugger_running); + + // Send message to enable debug in workers + HandleScope handle_scope(env->isolate()); + + Local message = Object::New(env->isolate()); + message->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "cmd"), + FIXED_ONE_BYTE_STRING(env->isolate(), "NODE_DEBUG_ENABLED")); + Local argv[] = { + FIXED_ONE_BYTE_STRING(env->isolate(), "internalMessage"), + message + }; + MakeCallback(env, env->process_object(), "emit", arraysize(argv), argv); + + // Enabled debugger, possibly making it wait on a semaphore + env->debugger_agent()->Enable(); +} + +// Called from an arbitrary thread. +static void TryStartDebugger() +{ + Mutex::ScopedLock scoped_lock(node_isolate_mutex); + if (auto isolate = node_isolate) { + v8::Debug::DebugBreak(isolate); + uv_async_send(&dispatch_debug_messages_async); + } +} + +// Called from the main thread. +static void DispatchDebugMessagesAsyncCallback(uv_async_t* handle) +{ + Mutex::ScopedLock scoped_lock(node_isolate_mutex); + if (auto isolate = node_isolate) { + if (debugger_running == false) { + fprintf(stderr, "Starting debugger agent.\n"); + + HandleScope scope(isolate); + Environment* env = Environment::GetCurrent(isolate); + if (!env) + return; + Context::Scope context_scope(env->context()); + + StartDebug(env, nullptr, false); + EnableDebug(env); + } - Isolate::Scope isolate_scope(isolate); - v8::Debug::ProcessDebugMessages( + Isolate::Scope isolate_scope(isolate); + v8::Debug::ProcessDebugMessages( #if !(V8_MAJOR_VERSION == 4 && V8_MINOR_VERSION == 8) - isolate + isolate #endif - ); - } - } + ); + } +} #ifdef _WIN32 - DWORD WINAPI EnableDebugThreadProc(void* arg) { - TryStartDebugger(); - return 0; - } - static int GetDebugSignalHandlerMappingName(DWORD pid, wchar_t* buf, size_t buf_len) { - return _snwprintf(buf, buf_len, L"node-debug-handler-%u", pid); - } - static int RegisterDebugSignalHandler() { - wchar_t mapping_name[32]; - HANDLE mapping_handle; - DWORD pid; - LPTHREAD_START_ROUTINE* handler; - - pid = GetCurrentProcessId(); - - if (GetDebugSignalHandlerMappingName(pid, - mapping_name, - arraysize(mapping_name)) < 0) { - return -1; - } - - mapping_handle = CreateFileMappingW(INVALID_HANDLE_VALUE, - nullptr, - PAGE_READWRITE, - 0, - sizeof *handler, - mapping_name); - if (mapping_handle == nullptr) { - return -1; - } - - handler = reinterpret_cast( - MapViewOfFile(mapping_handle, - FILE_MAP_ALL_ACCESS, - 0, - 0, - sizeof *handler)); - if (handler == nullptr) { - CloseHandle(mapping_handle); - return -1; - } - - *handler = EnableDebugThreadProc; - - UnmapViewOfFile(static_cast(handler)); - - return 0; - } - static void DebugProcess(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); +DWORD WINAPI EnableDebugThreadProc(void* arg) +{ + TryStartDebugger(); + return 0; +} +static int GetDebugSignalHandlerMappingName(DWORD pid, wchar_t* buf, size_t buf_len) +{ + return _snwprintf(buf, buf_len, L"node-debug-handler-%u", pid); +} +static int RegisterDebugSignalHandler() +{ + wchar_t mapping_name[32]; + HANDLE mapping_handle; + DWORD pid; + LPTHREAD_START_ROUTINE* handler; + + pid = GetCurrentProcessId(); + + if (GetDebugSignalHandlerMappingName(pid, + mapping_name, + arraysize(mapping_name)) + < 0) { + return -1; + } + + mapping_handle = CreateFileMappingW(INVALID_HANDLE_VALUE, + nullptr, + PAGE_READWRITE, + 0, + sizeof *handler, + mapping_name); + if (mapping_handle == nullptr) { + return -1; + } + + handler = reinterpret_cast( + MapViewOfFile(mapping_handle, + FILE_MAP_ALL_ACCESS, + 0, + 0, + sizeof *handler)); + if (handler == nullptr) { + CloseHandle(mapping_handle); + return -1; + } + + *handler = EnableDebugThreadProc; + + UnmapViewOfFile(static_cast(handler)); + + return 0; +} +static void DebugProcess(const FunctionCallbackInfo& args) +{ + Environment* env = Environment::GetCurrent(args); if (!env) return; - Isolate* isolate = args.GetIsolate(); - DWORD pid; - HANDLE process = nullptr; - HANDLE thread = nullptr; - HANDLE mapping = nullptr; - wchar_t mapping_name[32]; - LPTHREAD_START_ROUTINE* handler = nullptr; - - if (args.Length() != 1) { - env->ThrowError("Invalid number of arguments."); - goto out; - } - - pid = (DWORD)args[0]->IntegerValue(); - - process = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | - PROCESS_VM_OPERATION | PROCESS_VM_WRITE | - PROCESS_VM_READ, - FALSE, - pid); - if (process == nullptr) { - isolate->ThrowException( - WinapiErrnoException(isolate, GetLastError(), "OpenProcess")); - goto out; - } - - if (GetDebugSignalHandlerMappingName(pid, - mapping_name, - arraysize(mapping_name)) < 0) { - env->ThrowErrnoException(errno, "sprintf"); - goto out; - } - - mapping = OpenFileMappingW(FILE_MAP_READ, FALSE, mapping_name); - if (mapping == nullptr) { - isolate->ThrowException(WinapiErrnoException(isolate, - GetLastError(), - "OpenFileMappingW")); - goto out; - } - - handler = reinterpret_cast( - MapViewOfFile(mapping, - FILE_MAP_READ, - 0, - 0, - sizeof *handler)); - if (handler == nullptr || *handler == nullptr) { - isolate->ThrowException( - WinapiErrnoException(isolate, GetLastError(), "MapViewOfFile")); - goto out; - } - - thread = CreateRemoteThread(process, - nullptr, - 0, - *handler, - nullptr, - 0, - nullptr); - if (thread == nullptr) { - isolate->ThrowException(WinapiErrnoException(isolate, - GetLastError(), - "CreateRemoteThread")); - goto out; - } - - // Wait for the thread to terminate - if (WaitForSingleObject(thread, INFINITE) != WAIT_OBJECT_0) { - isolate->ThrowException(WinapiErrnoException(isolate, - GetLastError(), - "WaitForSingleObject")); - goto out; - } - - out: - if (process != nullptr) - CloseHandle(process); - if (thread != nullptr) - CloseHandle(thread); - if (handler != nullptr) - UnmapViewOfFile(handler); - if (mapping != nullptr) - CloseHandle(mapping); - } -#endif // _WIN32 - - - static void DebugPause(const FunctionCallbackInfo& args) { - v8::Debug::DebugBreak(args.GetIsolate()); - } - static void DebugEnd(const FunctionCallbackInfo& args) { - if (debugger_running) { - Environment* env = Environment::GetCurrent(args); - if (!env) - return; - env->debugger_agent()->Stop(); - debugger_running = false; - } - } - - - void Init(int* argc, const char** argv, int* exec_argc, const char*** exec_argv) { - // Initialize prog_start_time to get relative uptime. - prog_start_time = static_cast(uv_now(uv_default_loop())); - - // Make inherited handles noninheritable. - uv_disable_stdio_inheritance(); - - // init async debug messages dispatching - // Main thread uses uv_default_loop - CHECK_EQ(0, uv_async_init(uv_default_loop(), - &dispatch_debug_messages_async, - DispatchDebugMessagesAsyncCallback)); - uv_unref(reinterpret_cast(&dispatch_debug_messages_async)); - - - // Parse a few arguments which are specific to Node. - int v8_argc; - const char** v8_argv; - ParseArgs(argc, argv, exec_argc, exec_argv, &v8_argc, &v8_argv); - - // TODO(bnoordhuis) Intercept --prof arguments and start the CPU profiler - // manually? That would give us a little more control over its runtime - // behavior but it could also interfere with the user's intentions in ways - // we fail to anticipate. Dillema. - for (int i = 1; i < v8_argc; ++i) { - if (strncmp(v8_argv[i], "--prof", sizeof("--prof") - 1) == 0) { - v8_is_profiling = true; - break; - } - } - - - // The const_cast doesn't violate conceptual const-ness. V8 doesn't modify - // the argv array or the elements it points to. - if (v8_argc > 1) - V8::SetFlagsFromCommandLine(&v8_argc, const_cast(v8_argv), true); - - // Anything that's still in v8_argv is not a V8 or a node option. - for (int i = 1; i < v8_argc; i++) { - fprintf(stderr, "%s: bad option: %s\n", argv[0], v8_argv[i]); - } - delete[] v8_argv; - v8_argv = nullptr; - - if (v8_argc > 1) { - exit(9); - } - - // Unconditionally force typed arrays to allocate outside the v8 heap. This - // 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; - } - - - struct AtExitCallback { - AtExitCallback* next_; - void(*cb_)(void* arg); - void* arg_; - }; - - static AtExitCallback* at_exit_functions_; - - - // TODO(bnoordhuis) Turn into per-context event. - void RunAtExit(Environment* env) { - AtExitCallback* p = at_exit_functions_; - at_exit_functions_ = nullptr; - - while (p) { - AtExitCallback* q = p->next_; - p->cb_(p->arg_); - delete p; - p = q; - } - } - - - void AtExit(void(*cb)(void* arg), void* arg) { - AtExitCallback* p = new AtExitCallback; - p->cb_ = cb; - p->arg_ = arg; - p->next_ = at_exit_functions_; - at_exit_functions_ = p; - } - - void AddEnvironmentCleanupHook(Isolate* isolate, void(*fun)(void* arg), void* arg) { + Isolate* isolate = args.GetIsolate(); + DWORD pid; + HANDLE process = nullptr; + HANDLE thread = nullptr; + HANDLE mapping = nullptr; + wchar_t mapping_name[32]; + LPTHREAD_START_ROUTINE* handler = nullptr; + + if (args.Length() != 1) { + env->ThrowError("Invalid number of arguments."); + goto out; + } + + pid = (DWORD)args[0]->IntegerValue(); + + process = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, + FALSE, + pid); + if (process == nullptr) { + isolate->ThrowException( + WinapiErrnoException(isolate, GetLastError(), "OpenProcess")); + goto out; + } + + if (GetDebugSignalHandlerMappingName(pid, + mapping_name, + arraysize(mapping_name)) + < 0) { + env->ThrowErrnoException(errno, "sprintf"); + goto out; + } + + mapping = OpenFileMappingW(FILE_MAP_READ, FALSE, mapping_name); + if (mapping == nullptr) { + isolate->ThrowException(WinapiErrnoException(isolate, + GetLastError(), + "OpenFileMappingW")); + goto out; + } + + handler = reinterpret_cast( + MapViewOfFile(mapping, + FILE_MAP_READ, + 0, + 0, + sizeof *handler)); + if (handler == nullptr || *handler == nullptr) { + isolate->ThrowException( + WinapiErrnoException(isolate, GetLastError(), "MapViewOfFile")); + goto out; + } + + thread = CreateRemoteThread(process, + nullptr, + 0, + *handler, + nullptr, + 0, + nullptr); + if (thread == nullptr) { + isolate->ThrowException(WinapiErrnoException(isolate, + GetLastError(), + "CreateRemoteThread")); + goto out; + } + + // Wait for the thread to terminate + if (WaitForSingleObject(thread, INFINITE) != WAIT_OBJECT_0) { + isolate->ThrowException(WinapiErrnoException(isolate, + GetLastError(), + "WaitForSingleObject")); + goto out; + } + +out: + if (process != nullptr) + CloseHandle(process); + if (thread != nullptr) + CloseHandle(thread); + if (handler != nullptr) + UnmapViewOfFile(handler); + if (mapping != nullptr) + CloseHandle(mapping); +} +#endif // _WIN32 + +static void DebugPause(const FunctionCallbackInfo& args) +{ + v8::Debug::DebugBreak(args.GetIsolate()); +} +static void DebugEnd(const FunctionCallbackInfo& args) +{ + if (debugger_running) { + Environment* env = Environment::GetCurrent(args); + if (!env) + return; + env->debugger_agent()->Stop(); + debugger_running = false; + } +} + +void Init(int* argc, const char** argv, int* exec_argc, const char*** exec_argv) +{ + // Initialize prog_start_time to get relative uptime. + prog_start_time = static_cast(uv_now(uv_default_loop())); + + // Make inherited handles noninheritable. + uv_disable_stdio_inheritance(); + + // init async debug messages dispatching + // Main thread uses uv_default_loop + CHECK_EQ(0, uv_async_init(uv_default_loop(), &dispatch_debug_messages_async, DispatchDebugMessagesAsyncCallback)); + uv_unref(reinterpret_cast(&dispatch_debug_messages_async)); + + // Parse a few arguments which are specific to Node. + int v8_argc; + const char** v8_argv; + ParseArgs(argc, argv, exec_argc, exec_argv, &v8_argc, &v8_argv); + + // TODO(bnoordhuis) Intercept --prof arguments and start the CPU profiler + // manually? That would give us a little more control over its runtime + // behavior but it could also interfere with the user's intentions in ways + // we fail to anticipate. Dillema. + for (int i = 1; i < v8_argc; ++i) { + if (strncmp(v8_argv[i], "--prof", sizeof("--prof") - 1) == 0) { + v8_is_profiling = true; + break; + } + } + + // The const_cast doesn't violate conceptual const-ness. V8 doesn't modify + // the argv array or the elements it points to. + if (v8_argc > 1) + V8::SetFlagsFromCommandLine(&v8_argc, const_cast(v8_argv), true); + + // Anything that's still in v8_argv is not a V8 or a node option. + for (int i = 1; i < v8_argc; i++) { + fprintf(stderr, "%s: bad option: %s\n", argv[0], v8_argv[i]); + } + delete[] v8_argv; + v8_argv = nullptr; + + if (v8_argc > 1) { + exit(9); + } + + // Unconditionally force typed arrays to allocate outside the v8 heap. This + // 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; +} + +struct AtExitCallback { + AtExitCallback* next_; + void (*cb_)(void* arg); + void* arg_; +}; + +static AtExitCallback* at_exit_functions_; + +// TODO(bnoordhuis) Turn into per-context event. +void RunAtExit(Environment* env) +{ + AtExitCallback* p = at_exit_functions_; + at_exit_functions_ = nullptr; + + while (p) { + AtExitCallback* q = p->next_; + p->cb_(p->arg_); + delete p; + p = q; + } +} + +void AtExit(void (*cb)(void* arg), void* arg) +{ + AtExitCallback* p = new AtExitCallback; + p->cb_ = cb; + p->arg_ = arg; + p->next_ = at_exit_functions_; + at_exit_functions_ = p; +} + +void AddEnvironmentCleanupHook(Isolate* isolate, void (*fun)(void* arg), void* arg) +{ Environment* env = Environment::GetCurrent(isolate); env->AddCleanupHook(fun, arg); - } +} - void RemoveEnvironmentCleanupHook(Isolate* isolate, void(*fun)(void* arg), void* arg) { +void RemoveEnvironmentCleanupHook(Isolate* isolate, void (*fun)(void* arg), void* arg) +{ Environment* env = Environment::GetCurrent(isolate); env->RemoveCleanupHook(fun, arg); - } - - void EmitBeforeExit(Environment* env) { - HandleScope handle_scope(env->isolate()); - Context::Scope context_scope(env->context()); - Local process_object = env->process_object(); - Local exit_code = FIXED_ONE_BYTE_STRING(env->isolate(), "exitCode"); - Local args[] = { - FIXED_ONE_BYTE_STRING(env->isolate(), "beforeExit"), - process_object->Get(exit_code)->ToInteger(env->isolate()) - }; - MakeCallback(env, process_object, "emit", arraysize(args), args); - } - - - int EmitExit(Environment* env) { - // process.emit('exit') - HandleScope handle_scope(env->isolate()); - Context::Scope context_scope(env->context()); - Local process_object = env->process_object(); - process_object->Set(env->exiting_string(), True(env->isolate())); - - Local exitCode = env->exit_code_string(); - int code = process_object->Get(exitCode)->Int32Value(); - - Local args[] = { - env->exit_string(), - Integer::New(env->isolate(), code) - }; - - MakeCallback(env, process_object, "emit", arraysize(args), args); - - // Reload exit code, it may be changed by `emit('exit')` - return process_object->Get(exitCode)->Int32Value(); - } - - - // Just a convenience method - Environment* CreateEnvironment(Isolate* isolate, Local context, int argc, const char* const* argv, int exec_argc, const char* const* exec_argv) { - Environment* env; - Context::Scope context_scope(context); - - env = CreateEnvironment(isolate, - uv_default_loop(), - context, - argc, - argv, - exec_argc, - exec_argv); - - LoadEnvironment(env); - - return env; - } - - static Environment* CreateEnvironment(Isolate* isolate, Local context, NodeInstanceData* instance_data) { - return CreateEnvironment(isolate, - instance_data->event_loop(), - context, - instance_data->argc(), - instance_data->argv(), - instance_data->exec_argc(), - instance_data->exec_argv()); - } - static void HandleCloseCb(uv_handle_t* handle) { - Environment* env = reinterpret_cast(handle->data); - env->FinishHandleCleanup(handle); - } - static void HandleCleanup(Environment* env, uv_handle_t* handle, void* arg) { - handle->data = env; - uv_close(handle, HandleCloseCb); - } - - Environment* CreateEnvironment(Isolate* isolate, uv_loop_t* loop, Local context, int argc, const char* const* argv, int exec_argc, const char* const* exec_argv) { - HandleScope handle_scope(isolate); - - Context::Scope context_scope(context); - Environment* env = Environment::New(context, loop); +} + +void EmitBeforeExit(Environment* env) +{ + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); + Local process_object = env->process_object(); + Local exit_code = FIXED_ONE_BYTE_STRING(env->isolate(), "exitCode"); + Local args[] = { + FIXED_ONE_BYTE_STRING(env->isolate(), "beforeExit"), + process_object->Get(exit_code)->ToInteger(env->isolate()) + }; + MakeCallback(env, process_object, "emit", arraysize(args), args); +} + +int EmitExit(Environment* env) +{ + // process.emit('exit') + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); + Local process_object = env->process_object(); + process_object->Set(env->exiting_string(), True(env->isolate())); + + Local exitCode = env->exit_code_string(); + int code = process_object->Get(exitCode)->Int32Value(); + + Local args[] = { + env->exit_string(), + Integer::New(env->isolate(), code) + }; + + MakeCallback(env, process_object, "emit", arraysize(args), args); + + // Reload exit code, it may be changed by `emit('exit')` + return process_object->Get(exitCode)->Int32Value(); +} + +// Just a convenience method +Environment* CreateEnvironment(Isolate* isolate, Local context, int argc, const char* const* argv, int exec_argc, const char* const* exec_argv) +{ + Environment* env; + Context::Scope context_scope(context); + + env = CreateEnvironment(isolate, + uv_default_loop(), + context, + argc, + argv, + exec_argc, + exec_argv); + + LoadEnvironment(env); + + return env; +} + +static Environment* CreateEnvironment(Isolate* isolate, Local context, NodeInstanceData* instance_data) +{ + return CreateEnvironment(isolate, + instance_data->event_loop(), + context, + instance_data->argc(), + instance_data->argv(), + instance_data->exec_argc(), + instance_data->exec_argv()); +} +static void HandleCloseCb(uv_handle_t* handle) +{ + Environment* env = reinterpret_cast(handle->data); + env->FinishHandleCleanup(handle); +} +static void HandleCleanup(Environment* env, uv_handle_t* handle, void* arg) +{ + handle->data = env; + uv_close(handle, HandleCloseCb); +} + +Environment* CreateEnvironment(Isolate* isolate, uv_loop_t* loop, Local context, int argc, const char* const* argv, int exec_argc, const char* const* exec_argv) +{ + HandleScope handle_scope(isolate); + + Context::Scope context_scope(context); + Environment* env = Environment::New(context, loop); #ifndef MINIBLINK_NOT_IMPLEMENTED - env->InitBlinkMicrotaskSuppression(); -#endif - - isolate->SetAutorunMicrotasks(false); - - uv_check_init(env->event_loop(), env->immediate_check_handle()); - uv_unref( - reinterpret_cast(env->immediate_check_handle())); - - uv_idle_init(env->event_loop(), env->immediate_idle_handle()); - - // Inform V8's CPU profiler when we're idle. The profiler is sampling-based - // but not all samples are created equal; mark the wall clock time spent in - // epoll_wait() and friends so profiling tools can filter it out. The samples - // still end up in v8.log but with state=IDLE rather than state=EXTERNAL. - // TODO(bnoordhuis) Depends on a libuv implementation detail that we should - // probably fortify in the API contract, namely that the last started prepare - // or check watcher runs first. It's not 100% foolproof; if an add-on starts - // a prepare or check watcher after us, any samples attributed to its callback - // will be recorded with state=IDLE. - uv_prepare_init(env->event_loop(), env->idle_prepare_handle()); - uv_check_init(env->event_loop(), env->idle_check_handle()); - uv_unref(reinterpret_cast(env->idle_prepare_handle())); - uv_unref(reinterpret_cast(env->idle_check_handle())); - - uv_idle_init(env->event_loop(), env->destroy_ids_idle_handle()); - uv_unref(reinterpret_cast(env->destroy_ids_idle_handle())); - - // Register handle cleanups - env->RegisterHandleCleanup( - reinterpret_cast(env->immediate_check_handle()), - HandleCleanup, - nullptr); - env->RegisterHandleCleanup( - reinterpret_cast(env->immediate_idle_handle()), - HandleCleanup, - nullptr); - env->RegisterHandleCleanup( - reinterpret_cast(env->idle_prepare_handle()), - HandleCleanup, - nullptr); - env->RegisterHandleCleanup( - reinterpret_cast(env->idle_check_handle()), - HandleCleanup, - nullptr); - - if (v8_is_profiling) { - StartProfilerIdleNotifier(env); - } - - Local process_template = FunctionTemplate::New(isolate); - process_template->SetClassName(FIXED_ONE_BYTE_STRING(isolate, "process")); - - Local process_object = - process_template->GetFunction()->NewInstance(context).ToLocalChecked(); - env->set_process_object(process_object); - - SetupProcessObject(env, argc, argv, exec_argc, exec_argv); - LoadAsyncWrapperInfo(env); - - return env; - } - -} // namespace node + env->InitBlinkMicrotaskSuppression(); +#endif + + isolate->SetAutorunMicrotasks(false); + + uv_check_init(env->event_loop(), env->immediate_check_handle()); + uv_unref( + reinterpret_cast(env->immediate_check_handle())); + + uv_idle_init(env->event_loop(), env->immediate_idle_handle()); + + // Inform V8's CPU profiler when we're idle. The profiler is sampling-based + // but not all samples are created equal; mark the wall clock time spent in + // epoll_wait() and friends so profiling tools can filter it out. The samples + // still end up in v8.log but with state=IDLE rather than state=EXTERNAL. + // TODO(bnoordhuis) Depends on a libuv implementation detail that we should + // probably fortify in the API contract, namely that the last started prepare + // or check watcher runs first. It's not 100% foolproof; if an add-on starts + // a prepare or check watcher after us, any samples attributed to its callback + // will be recorded with state=IDLE. + uv_prepare_init(env->event_loop(), env->idle_prepare_handle()); + uv_check_init(env->event_loop(), env->idle_check_handle()); + uv_unref(reinterpret_cast(env->idle_prepare_handle())); + uv_unref(reinterpret_cast(env->idle_check_handle())); + + uv_idle_init(env->event_loop(), env->destroy_ids_idle_handle()); + uv_unref(reinterpret_cast(env->destroy_ids_idle_handle())); + + // Register handle cleanups + env->RegisterHandleCleanup( + reinterpret_cast(env->immediate_check_handle()), + HandleCleanup, + nullptr); + env->RegisterHandleCleanup( + reinterpret_cast(env->immediate_idle_handle()), + HandleCleanup, + nullptr); + env->RegisterHandleCleanup( + reinterpret_cast(env->idle_prepare_handle()), + HandleCleanup, + nullptr); + env->RegisterHandleCleanup( + reinterpret_cast(env->idle_check_handle()), + HandleCleanup, + nullptr); + + if (v8_is_profiling) { + StartProfilerIdleNotifier(env); + } + + Local process_template = FunctionTemplate::New(isolate); + process_template->SetClassName(FIXED_ONE_BYTE_STRING(isolate, "process")); + + Local process_object = process_template->GetFunction()->NewInstance(context).ToLocalChecked(); + env->set_process_object(process_object); + + SetupProcessObject(env, argc, argv, exec_argc, exec_argv); + LoadAsyncWrapperInfo(env); + + return env; +} + +} // namespace node diff --git a/node/src/node_api.cc b/node/src/node_api.cc index fc4a8cea96..600e0f824b 100644 --- a/node/src/node_api.cc +++ b/node/src/node_api.cc @@ -1,7 +1,7 @@ #include "node_buffer.h" #include "node_object_wrap.h" -#include // INT_MAX +#include // INT_MAX #include #include #include @@ -21,35 +21,35 @@ // using v8::Local; // using v8::Object; -namespace node { - -template -struct ResetInDestructorPersistentTraits { - static const bool kResetInDestructor = true; - template - // Disallow copy semantics by leaving this unimplemented. - inline static void Copy( - const v8::Persistent&, - v8::Persistent>*); -}; - -// v8::Persistent does not reset the object slot in its destructor. That is -// acknowledged as a flaw in the V8 API and expected to change in the future -// but for now node::Persistent is the easier and safer alternative. -template -using Persistent = v8::Persistent>; - +namespace node { + +template +struct ResetInDestructorPersistentTraits { + static const bool kResetInDestructor = true; + template + // Disallow copy semantics by leaving this unimplemented. + inline static void Copy( + const v8::Persistent&, + v8::Persistent>*); +}; + +// v8::Persistent does not reset the object slot in its destructor. That is +// acknowledged as a flaw in the V8 API and expected to change in the future +// but for now node::Persistent is the easier and safer alternative. +template +using Persistent = v8::Persistent>; + class InternalCallbackScope; -typedef double async_id; -struct async_context { - async_context() - { - async_id = -1; - trigger_async_id = -1; - } - double async_id; - double trigger_async_id; +typedef double async_id; +struct async_context { + async_context() + { + async_id = -1; + trigger_async_id = -1; + } + double async_id; + double trigger_async_id; }; /* This class works like `MakeCallback()` in that it sets up a specific @@ -66,8 +66,8 @@ struct async_context { * Exceptions happening within this scope will be treated like uncaught * exceptions. If this behaviour is undesirable, a new `v8::TryCatch` scope * needs to be created inside of this scope. -*/ - +*/ + class NODE_EXTERN CallbackScope { public: CallbackScope(v8::Isolate* isolate, v8::Local resource, async_context asyncContext); @@ -81,161 +81,161 @@ class NODE_EXTERN CallbackScope { void operator=(CallbackScope&&) = delete; CallbackScope(const CallbackScope&) = delete; CallbackScope(CallbackScope&&) = delete; -}; - -class InternalCallbackScope { -public: - // Tell the constructor whether its `object` parameter may be empty or not. - enum ResourceExpectation { kRequireResource, kAllowEmptyResource }; - InternalCallbackScope(Environment* env, - v8::Local object, - const async_context& asyncContext, - ResourceExpectation expect = kRequireResource); - // Utility that can be used by AsyncWrap classes. - explicit InternalCallbackScope(node::AsyncWrap* async_wrap); - ~InternalCallbackScope(); - void Close(); - - inline bool Failed() const { return failed_; } - inline void MarkAsFailed() { failed_ = true; } - -private: - Environment* env_; - async_context async_context_; - v8::Local object_; - //Environment::AsyncCallbackScope callback_scope_; - bool failed_ = false; - bool pushed_ids_ = false; - bool closed_ = false; -}; - -using AsyncHooks = Environment::AsyncHooks; - -CallbackScope::CallbackScope(v8::Isolate* isolate, - v8::Local object, - async_context asyncContext) - : private_(new InternalCallbackScope( +}; + +class InternalCallbackScope { +public: + // Tell the constructor whether its `object` parameter may be empty or not. + enum ResourceExpectation { kRequireResource, + kAllowEmptyResource }; + InternalCallbackScope(Environment* env, + v8::Local object, + const async_context& asyncContext, + ResourceExpectation expect = kRequireResource); + // Utility that can be used by AsyncWrap classes. + explicit InternalCallbackScope(node::AsyncWrap* async_wrap); + ~InternalCallbackScope(); + void Close(); + + inline bool Failed() const { return failed_; } + inline void MarkAsFailed() { failed_ = true; } + +private: + Environment* env_; + async_context async_context_; + v8::Local object_; + //Environment::AsyncCallbackScope callback_scope_; + bool failed_ = false; + bool pushed_ids_ = false; + bool closed_ = false; +}; + +using AsyncHooks = Environment::AsyncHooks; + +CallbackScope::CallbackScope(v8::Isolate* isolate, + v8::Local object, + async_context asyncContext) + : private_(new InternalCallbackScope( // Environment::GetCurrent(isolate), nullptr, object, - asyncContext - )), - try_catch_(isolate) -{ - try_catch_.SetVerbose(true); -} - -CallbackScope::~CallbackScope() -{ - if (try_catch_.HasCaught()) - private_->MarkAsFailed(); - delete private_; -} - -InternalCallbackScope::InternalCallbackScope(AsyncWrap* async_wrap) - : InternalCallbackScope( + asyncContext)) + , try_catch_(isolate) +{ + try_catch_.SetVerbose(true); +} + +CallbackScope::~CallbackScope() +{ + if (try_catch_.HasCaught()) + private_->MarkAsFailed(); + delete private_; +} + +InternalCallbackScope::InternalCallbackScope(AsyncWrap* async_wrap) + : InternalCallbackScope( async_wrap->env(), async_wrap->object(), async_context() - // { async_wrap->get_async_id(), async_wrap->get_trigger_async_id() } - ) -{ -} - -InternalCallbackScope::InternalCallbackScope(Environment* env, - v8::Local object, - const async_context& asyncContext, - ResourceExpectation expect) - : env_(env), - async_context_(asyncContext), - object_(object) - //callback_scope_(env) -{ -// CHECK_IMPLIES(expect == kRequireResource, !object.IsEmpty()); -// CHECK_NOT_NULL(env); - -// if (!env->can_call_into_js()) { -// failed_ = true; -// return; -// } - -// v8::HandleScope handle_scope(env->isolate()); -// // If you hit this assertion, you forgot to enter the v8::Context first. -// CHECK_EQ(Environment::GetCurrent(env->isolate()), env); - -// if (asyncContext.async_id != 0) { -// // No need to check a return value because the application will exit if -// // an exception occurs. -// AsyncWrap::EmitBefore(env, asyncContext.async_id); -// } -// -// CHECK_GE(env->makecallback_depth(), 1); -// if (env->makecallback_depth() == 1) { -// env->tick_info()->set_has_thrown(false); -// } -// -// env->async_hooks()->push_async_ids(async_context_.async_id, async_context_.trigger_async_id); -// pushed_ids_ = true; -} - -InternalCallbackScope::~InternalCallbackScope() -{ - Close(); -} - -void InternalCallbackScope::Close() -{ -// if (closed_) -// return; -// closed_ = true; -// v8::HandleScope handle_scope(env_->isolate()); - -// if (!env_->can_call_into_js()) return; -// if (failed_ && !env_->is_main_thread() && env_->is_stopping_worker()) { -// env_->async_hooks()->clear_async_id_stack(); -// } -// -// if (pushed_ids_) -// env_->async_hooks()->pop_async_id(async_context_.async_id); -// -// if (failed_) return; -// -// if (async_context_.async_id != 0) { -// AsyncWrap::EmitAfter(env_, async_context_.async_id); -// } -// -// if (env_->makecallback_depth() > 1) { -// return; -// } - -// Environment::TickInfo* tick_info = env_->tick_info(); - -// if (!env_->can_call_into_js()) return; -// if (!tick_info->has_scheduled()) { -// env_->isolate()->RunMicrotasks(); -// } -// -// // Make sure the stack unwound properly. If there are nested MakeCallback's -// // then it should return early and not reach this code. -// if (env_->async_hooks()->fields()[AsyncHooks::kTotals]) { -// CHECK_EQ(env_->execution_async_id(), 0); -// CHECK_EQ(env_->trigger_async_id(), 0); -// } -// -// if (!tick_info->has_scheduled() && !tick_info->has_promise_rejections()) { -// return; -// } -// -// Local process = env_->process_object(); -// -// if (!env_->can_call_into_js()) return; -// -// if (env_->tick_callback_function()->Call(process, 0, nullptr).IsEmpty()) { -// env_->tick_info()->set_has_thrown(true); -// failed_ = true; -// } -} - + // { async_wrap->get_async_id(), async_wrap->get_trigger_async_id() } + ) +{ +} + +InternalCallbackScope::InternalCallbackScope(Environment* env, + v8::Local object, + const async_context& asyncContext, + ResourceExpectation expect) + : env_(env) + , async_context_(asyncContext) + , object_(object) +//callback_scope_(env) +{ + // NODE_CHECK_IMPLIES(expect == kRequireResource, !object.IsEmpty()); + // NODE_CHECK_NOT_NULL(env); + // + // if (!env->can_call_into_js()) { + // failed_ = true; + // return; + // } + // + // v8::HandleScope handle_scope(env->isolate()); + // // If you hit this NODE_ASSERTion, you forgot to enter the v8::Context first. + // NODE_CHECK_EQ(Environment::GetCurrent(env->isolate()), env); + // + // if (asyncContext.async_id != 0) { + // // No need to check a return value because the application will exit if + // // an exception occurs. + // AsyncWrap::EmitBefore(env, asyncContext.async_id); + // } + // + // NODE_CHECK_GE(env->makecallback_depth(), 1); + // if (env->makecallback_depth() == 1) { + // env->tick_info()->set_has_thrown(false); + // } + // + // env->async_hooks()->push_async_ids(async_context_.async_id, async_context_.trigger_async_id); + // pushed_ids_ = true; +} + +InternalCallbackScope::~InternalCallbackScope() +{ + Close(); +} + +void InternalCallbackScope::Close() +{ + // if (closed_) + // return; + // closed_ = true; + // v8::HandleScope handle_scope(env_->isolate()); + // + // if (!env_->can_call_into_js()) return; + // if (failed_ && !env_->is_main_thread() && env_->is_stopping_worker()) { + // env_->async_hooks()->clear_async_id_stack(); + // } + // + // if (pushed_ids_) + // env_->async_hooks()->pop_async_id(async_context_.async_id); + // + // if (failed_) return; + // + // if (async_context_.async_id != 0) { + // AsyncWrap::EmitAfter(env_, async_context_.async_id); + // } + // + // if (env_->makecallback_depth() > 1) { + // return; + // } + // + // Environment::TickInfo* tick_info = env_->tick_info(); + // + // if (!env_->can_call_into_js()) return; + // if (!tick_info->has_scheduled()) { + // env_->isolate()->RunMicrotasks(); + // } + // + // // Make sure the stack unwound properly. If there are nested MakeCallback's + // // then it should return early and not reach this code. + // if (env_->async_hooks()->fields()[AsyncHooks::kTotals]) { + // NODE_CHECK_EQ(env_->execution_async_id(), 0); + // NODE_CHECK_EQ(env_->trigger_async_id(), 0); + // } + // + // if (!tick_info->has_scheduled() && !tick_info->has_promise_rejections()) { + // return; + // } + // + // Local process = env_->process_object(); + // + // if (!env_->can_call_into_js()) return; + // + // if (env_->tick_callback_function()->Call(process, 0, nullptr).IsEmpty()) { + // env_->tick_info()->set_has_thrown(true); + // failed_ = true; + // } +} + NODE_EXTERN async_context EmitAsyncInit(v8::Isolate* isolate, v8::Local resource, const char* name, @@ -249,116 +249,117 @@ NODE_EXTERN async_context EmitAsyncInit(v8::Isolate* isolate, NODE_EXTERN async_context EmitAsyncInit(v8::Isolate* isolate, v8::Local resource, v8::Local name, - async_id trigger_async_id = -1) + async_id trigger_async_id = -1) { //DebugBreak(); async_context ret; return ret; -} - -/* Helper class users can optionally inherit from. If -* `AsyncResource::MakeCallback()` is used, then all four callbacks will be -* called automatically. */ -class AsyncResource { -public: - AsyncResource(v8::Isolate* isolate, - v8::Local resource, - const char* name, - async_id trigger_async_id = -1) - : isolate_(isolate), - resource_(isolate, resource) - { - async_context_ = EmitAsyncInit(isolate, resource, name, trigger_async_id); - } - - AsyncResource(v8::Isolate* isolate, - v8::Local resource, - v8::Local name, - async_id trigger_async_id = -1) - : isolate_(isolate), - resource_(isolate, resource) - { - //async_context_ = EmitAsyncInit(isolate, resource, name, trigger_async_id); - DebugBreak(); - } - - virtual ~AsyncResource() - { - //EmitAsyncDestroy(isolate_, async_context_); - DebugBreak(); - resource_.Reset(); - } - - v8::MaybeLocal MakeCallback( - v8::Local callback, - int argc, - v8::Local* argv) - { - return node::MakeCallback(isolate_, get_resource(), - callback, argc, argv - //, async_context_ - ); - } - - v8::MaybeLocal MakeCallback( - const char* method, - int argc, - v8::Local* argv) - { - return node::MakeCallback(isolate_, get_resource(), - method, argc, argv - //, async_context_ - ); - } - - v8::MaybeLocal MakeCallback( - v8::Local symbol, - int argc, - v8::Local* argv) - { - return node::MakeCallback(isolate_, get_resource(), - symbol, argc, argv - //, async_context_ - ); - } - - v8::Local get_resource() - { - return resource_.Get(isolate_); - } - - async_id get_async_id() const - { - return async_context_.async_id; - } - - async_id get_trigger_async_id() const - { - return async_context_.trigger_async_id; - } - -protected: - class CallbackScope : public node::CallbackScope { - public: - explicit CallbackScope(AsyncResource* res) - : node::CallbackScope(res->isolate_, - res->resource_.Get(res->isolate_), - res->async_context_) - { - } - }; - -private: - v8::Isolate* isolate_; - v8::Persistent resource_; - async_context async_context_; -}; - +} + +/* Helper class users can optionally inherit from. If +* `AsyncResource::MakeCallback()` is used, then all four callbacks will be +* called automatically. */ +class AsyncResource { +public: + AsyncResource(v8::Isolate* isolate, + v8::Local resource, + const char* name, + async_id trigger_async_id = -1) + : isolate_(isolate) + , resource_(isolate, resource) + { + async_context_ = EmitAsyncInit(isolate, resource, name, trigger_async_id); + } + + AsyncResource(v8::Isolate* isolate, + v8::Local resource, + v8::Local name, + async_id trigger_async_id = -1) + : isolate_(isolate) + , resource_(isolate, resource) + { + //async_context_ = EmitAsyncInit(isolate, resource, name, trigger_async_id); + DebugBreak(); + } + + virtual ~AsyncResource() + { + //EmitAsyncDestroy(isolate_, async_context_); + DebugBreak(); + resource_.Reset(); + } + + v8::MaybeLocal MakeCallback( + v8::Local callback, + int argc, + v8::Local* argv) + { + return node::MakeCallback(isolate_, get_resource(), + callback, argc, argv + //, async_context_ + ); + } + + v8::MaybeLocal MakeCallback( + const char* method, + int argc, + v8::Local* argv) + { + return node::MakeCallback(isolate_, get_resource(), + method, argc, argv + //, async_context_ + ); + } + + v8::MaybeLocal MakeCallback( + v8::Local symbol, + int argc, + v8::Local* argv) + { + return node::MakeCallback(isolate_, get_resource(), + symbol, argc, argv + //, async_context_ + ); + } + + v8::Local get_resource() + { + return resource_.Get(isolate_); + } + + async_id get_async_id() const + { + return async_context_.async_id; + } + + async_id get_trigger_async_id() const + { + return async_context_.trigger_async_id; + } + +protected: + class CallbackScope : public node::CallbackScope { + public: + explicit CallbackScope(AsyncResource* res) + : node::CallbackScope(res->isolate_, + res->resource_.Get(res->isolate_), + res->async_context_) + { + } + }; + +private: + v8::Isolate* isolate_; + v8::Persistent resource_; + async_context async_context_; +}; + class ThreadPoolWork { public: - explicit inline ThreadPoolWork(Environment* env) : env_(env) + explicit inline ThreadPoolWork(Environment* env) + : env_(env) { - //CHECK_NOT_NULL(env); + //NODE_CHECK_NOT_NULL(env); } inline virtual ~ThreadPoolWork() = default; @@ -380,36 +381,34 @@ void ThreadPoolWork::ScheduleWork() env_->event_loop(), &work_req_, [](uv_work_t* req) { - ThreadPoolWork* self = ContainerOf(&ThreadPoolWork::work_req_, req); - self->DoThreadPoolWork(); - }, + ThreadPoolWork* self = ContainerOf(&ThreadPoolWork::work_req_, req); + self->DoThreadPoolWork(); + }, [](uv_work_t* req, int status) { - ThreadPoolWork* self = ContainerOf(&ThreadPoolWork::work_req_, req); - //self->env_->DecreaseWaitingRequestCounter(); - self->AfterThreadPoolWork(status); - }); - CHECK_EQ(status, 0); + ThreadPoolWork* self = ContainerOf(&ThreadPoolWork::work_req_, req); + //self->env_->DecreaseWaitingRequestCounter(); + self->AfterThreadPoolWork(status); + }); + NODE_CHECK_EQ(status, 0); } int ThreadPoolWork::CancelWork() { return uv_cancel(reinterpret_cast(&work_req_)); -} - -} // namespace node +} + +} // namespace node -static -napi_status napi_set_last_error(napi_env env, napi_status error_code, +static napi_status napi_set_last_error(napi_env env, napi_status error_code, uint32_t engine_error_code = 0, void* engine_reserved = nullptr); -static -napi_status napi_clear_last_error(napi_env env); +static napi_status napi_clear_last_error(napi_env env); struct napi_env__ { explicit napi_env__(v8::Isolate* _isolate, uv_loop_t* _loop) - : isolate(_isolate), - last_error(), - loop(_loop) + : isolate(_isolate) + , last_error() + , loop(_loop) { } v8::Isolate* isolate; @@ -418,137 +417,140 @@ struct napi_env__ { int open_handle_scopes = 0; int open_callback_scopes = 0; uv_loop_t* loop = nullptr; + + void* instance_data = nullptr; + napi_finalize finalize_cb = nullptr; + void* finalize_hint = nullptr; }; #define NAPI_PRIVATE_KEY(context, suffix) \ - (node::Environment::GetCurrent((context))->napi_ ## suffix()) + (node::Environment::GetCurrent((context))->napi_##suffix()) -#define RETURN_STATUS_IF_FALSE(env, condition, status) \ - do { \ - if (!(condition)) { \ - return napi_set_last_error((env), (status)); \ - } \ - } while (0) +#define RETURN_STATUS_IF_FALSE(env, condition, status) \ + do { \ + if (!(condition)) { \ + return napi_set_last_error((env), (status)); \ + } \ + } while (0) -#define CHECK_ENV(env) \ - do { \ - if ((env) == nullptr) { \ - return napi_invalid_arg; \ - } \ - } while (0) +#define NODE_CHECK_ENV(env) \ + do { \ + if ((env) == nullptr) { \ + return napi_invalid_arg; \ + } \ + } while (0) -#define CHECK_ARG(env, arg) \ - RETURN_STATUS_IF_FALSE((env), ((arg) != nullptr), napi_invalid_arg) +#define NODE_CHECK_ARG(env, arg) \ + RETURN_STATUS_IF_FALSE((env), ((arg) != nullptr), napi_invalid_arg) -#define CHECK_MAYBE_EMPTY(env, maybe, status) \ - RETURN_STATUS_IF_FALSE((env), !((maybe).IsEmpty()), (status)) +#define NODE_CHECK_MAYBE_EMPTY(env, maybe, status) \ + RETURN_STATUS_IF_FALSE((env), !((maybe).IsEmpty()), (status)) -#define CHECK_MAYBE_NOTHING(env, maybe, status) \ - RETURN_STATUS_IF_FALSE((env), !((maybe).IsNothing()), (status)) +#define NODE_CHECK_MAYBE_NOTHING(env, maybe, status) \ + RETURN_STATUS_IF_FALSE((env), !((maybe).IsNothing()), (status)) // NAPI_PREAMBLE is not wrapped in do..while: try_catch must have function scope -#define NAPI_PREAMBLE(env) \ - CHECK_ENV((env)); \ - RETURN_STATUS_IF_FALSE((env), (env)->last_exception.IsEmpty(), \ - napi_pending_exception); \ - napi_clear_last_error((env)); \ - v8impl::TryCatch try_catch((env)) - -#define CHECK_TO_TYPE(env, type, context, result, src, status) \ - do { \ - CHECK_ARG((env), (src)); \ - auto maybe = v8impl::V8LocalValueFromJsValue((src))->To##type((context)); \ - CHECK_MAYBE_EMPTY((env), maybe, (status)); \ - (result) = maybe.ToLocalChecked(); \ - } while (0) - -#define CHECK_TO_FUNCTION(env, result, src) \ - do { \ - CHECK_ARG((env), (src)); \ - v8::Local v8value = v8impl::V8LocalValueFromJsValue((src)); \ - RETURN_STATUS_IF_FALSE((env), v8value->IsFunction(), napi_invalid_arg); \ - (result) = v8value.As(); \ - } while (0) - -#define CHECK_TO_OBJECT(env, context, result, src) \ - CHECK_TO_TYPE((env), Object, (context), (result), (src), napi_object_expected) - -#define CHECK_TO_STRING(env, context, result, src) \ - CHECK_TO_TYPE((env), String, (context), (result), (src), napi_string_expected) - -#define CHECK_TO_NUMBER(env, context, result, src) \ - CHECK_TO_TYPE((env), Number, (context), (result), (src), napi_number_expected) - -#define CHECK_TO_BOOL(env, context, result, src) \ - CHECK_TO_TYPE((env), Boolean, (context), (result), (src), \ - napi_boolean_expected) +#define NAPI_PREAMBLE(env) \ + NODE_CHECK_ENV((env)); \ + RETURN_STATUS_IF_FALSE((env), (env)->last_exception.IsEmpty(), \ + napi_pending_exception); \ + napi_clear_last_error((env)); \ + v8impl::TryCatch try_catch((env)) + +#define NODE_CHECK_TO_TYPE(env, type, context, result, src, status) \ + do { \ + NODE_CHECK_ARG((env), (src)); \ + auto maybe = v8impl::V8LocalValueFromJsValue((src))->To##type((context)); \ + NODE_CHECK_MAYBE_EMPTY((env), maybe, (status)); \ + (result) = maybe.ToLocalChecked(); \ + } while (0) + +#define NODE_CHECK_TO_FUNCTION(env, result, src) \ + do { \ + NODE_CHECK_ARG((env), (src)); \ + v8::Local v8value = v8impl::V8LocalValueFromJsValue((src)); \ + RETURN_STATUS_IF_FALSE((env), v8value->IsFunction(), napi_invalid_arg); \ + (result) = v8value.As(); \ + } while (0) + +#define NODE_CHECK_TO_OBJECT(env, context, result, src) \ + NODE_CHECK_TO_TYPE((env), Object, (context), (result), (src), napi_object_expected) + +#define NODE_CHECK_TO_STRING(env, context, result, src) \ + NODE_CHECK_TO_TYPE((env), String, (context), (result), (src), napi_string_expected) + +#define NODE_CHECK_TO_NUMBER(env, context, result, src) \ + NODE_CHECK_TO_TYPE((env), Number, (context), (result), (src), napi_number_expected) + +#define NODE_CHECK_TO_BOOL(env, context, result, src) \ + NODE_CHECK_TO_TYPE((env), Boolean, (context), (result), (src), \ + napi_boolean_expected) // n-api defines NAPI_AUTO_LENGHTH as the indicator that a string -// is null terminated. For V8 the equivalent is -1. The assert +// is null terminated. For V8 the equivalent is -1. The NODE_ASSERT // validates that our cast of NAPI_AUTO_LENGTH results in -1 as // needed by V8. -#define CHECK_NEW_FROM_UTF8_LEN(env, result, str, len) \ - do { \ - static_assert(static_cast(NAPI_AUTO_LENGTH) == -1, \ - "Casting NAPI_AUTO_LENGTH to int must result in -1"); \ - RETURN_STATUS_IF_FALSE((env), \ - (len == NAPI_AUTO_LENGTH) || len <= INT_MAX, \ - napi_invalid_arg); \ - auto str_maybe = v8::String::NewFromUtf8( \ - (env)->isolate, (str), v8::NewStringType::kInternalized, \ - static_cast(len)); \ - CHECK_MAYBE_EMPTY((env), str_maybe, napi_generic_failure); \ - (result) = str_maybe.ToLocalChecked(); \ - } while (0) - -#define CHECK_NEW_FROM_UTF8(env, result, str) \ - CHECK_NEW_FROM_UTF8_LEN((env), (result), (str), NAPI_AUTO_LENGTH) - -#define GET_RETURN_STATUS(env) \ - (!try_catch.HasCaught() ? napi_ok \ - : napi_set_last_error((env), napi_pending_exception)) - -#define THROW_RANGE_ERROR_IF_FALSE(env, condition, error, message) \ - do { \ - if (!(condition)) { \ - napi_throw_range_error((env), (error), (message)); \ - return napi_set_last_error((env), napi_generic_failure); \ - } \ - } while (0) - -#define CREATE_TYPED_ARRAY( \ - env, type, size_of_element, buffer, byte_offset, length, out) \ - do { \ - if ((size_of_element) > 1) { \ - THROW_RANGE_ERROR_IF_FALSE( \ - (env), (byte_offset) % (size_of_element) == 0, \ - "ERR_NAPI_INVALID_TYPEDARRAY_ALIGNMENT", \ - "start offset of "#type" should be a multiple of "#size_of_element); \ - } \ - THROW_RANGE_ERROR_IF_FALSE((env), (length) * (size_of_element) + \ - (byte_offset) <= buffer->ByteLength(), \ - "ERR_NAPI_INVALID_TYPEDARRAY_LENGTH", \ - "Invalid typed array length"); \ - (out) = v8::type::New((buffer), (byte_offset), (length)); \ - } while (0) - -#define NAPI_CALL_INTO_MODULE(env, call, handle_exception) \ - do { \ - int open_handle_scopes = (env)->open_handle_scopes; \ - int open_callback_scopes = (env)->open_callback_scopes; \ - napi_clear_last_error((env)); \ - call; \ - CHECK_EQ((env)->open_handle_scopes, open_handle_scopes); \ - CHECK_EQ((env)->open_callback_scopes, open_callback_scopes); \ - if (!(env)->last_exception.IsEmpty()) { \ - handle_exception( \ - v8::Local::New((env)->isolate, (env)->last_exception)); \ - (env)->last_exception.Reset(); \ - } \ - } while (0) +#define NODE_CHECK_NEW_FROM_UTF8_LEN(env, result, str, len) \ + do { \ + static_assert(static_cast(NAPI_AUTO_LENGTH) == -1, \ + "Casting NAPI_AUTO_LENGTH to int must result in -1"); \ + RETURN_STATUS_IF_FALSE((env), \ + (len == NAPI_AUTO_LENGTH) || len <= INT_MAX, \ + napi_invalid_arg); \ + auto str_maybe = v8::String::NewFromUtf8( \ + (env)->isolate, (str), v8::NewStringType::kInternalized, \ + static_cast(len)); \ + NODE_CHECK_MAYBE_EMPTY((env), str_maybe, napi_generic_failure); \ + (result) = str_maybe.ToLocalChecked(); \ + } while (0) + +#define NODE_CHECK_NEW_FROM_UTF8(env, result, str) \ + NODE_CHECK_NEW_FROM_UTF8_LEN((env), (result), (str), NAPI_AUTO_LENGTH) + +#define GET_RETURN_STATUS(env) \ + (!try_catch.HasCaught() ? napi_ok \ + : napi_set_last_error((env), napi_pending_exception)) + +#define THROW_RANGE_ERROR_IF_FALSE(env, condition, error, message) \ + do { \ + if (!(condition)) { \ + napi_throw_range_error((env), (error), (message)); \ + return napi_set_last_error((env), napi_generic_failure); \ + } \ + } while (0) + +#define CREATE_TYPED_ARRAY( \ + env, type, size_of_element, buffer, byte_offset, length, out) \ + do { \ + if ((size_of_element) > 1) { \ + THROW_RANGE_ERROR_IF_FALSE( \ + (env), (byte_offset) % (size_of_element) == 0, \ + "ERR_NAPI_INVALID_TYPEDARRAY_ALIGNMENT", \ + "start offset of " #type " should be a multiple of " #size_of_element); \ + } \ + THROW_RANGE_ERROR_IF_FALSE((env), (length) * (size_of_element) + (byte_offset) <= buffer->ByteLength(), \ + "ERR_NAPI_INVALID_TYPEDARRAY_LENGTH", \ + "Invalid typed array length"); \ + (out) = v8::type::New((buffer), (byte_offset), (length)); \ + } while (0) + +#define NAPI_CALL_INTO_MODULE(env, call, handle_exception) \ + do { \ + int open_handle_scopes = (env)->open_handle_scopes; \ + int open_callback_scopes = (env)->open_callback_scopes; \ + napi_clear_last_error((env)); \ + call; \ + NODE_CHECK_EQ((env)->open_handle_scopes, open_handle_scopes); \ + NODE_CHECK_EQ((env)->open_callback_scopes, open_callback_scopes); \ + if (!(env)->last_exception.IsEmpty()) { \ + handle_exception( \ + v8::Local::New((env)->isolate, (env)->last_exception)); \ + (env)->last_exception.Reset(); \ + } \ + } while (0) #define NAPI_CALL_INTO_MODULE_THROW(env, call) \ - NAPI_CALL_INTO_MODULE((env), call, (env)->isolate->ThrowException) + NAPI_CALL_INTO_MODULE((env), call, (env)->isolate->ThrowException) void napi_module_register_by_symbol(v8::Local exports, v8::Local module, @@ -558,1124 +560,1151 @@ void napi_module_register_by_symbol(v8::Local exports, namespace { namespace v8impl { -// convert from n-api property attributes to v8::PropertyAttribute -static inline v8::PropertyAttribute V8PropertyAttributesFromDescriptor( - const napi_property_descriptor* descriptor) -{ - unsigned int attribute_flags = v8::PropertyAttribute::None; - - if (descriptor->getter != nullptr || descriptor->setter != nullptr) { - // The napi_writable attribute is ignored for accessor descriptors, but - // V8 requires the ReadOnly attribute to match nonexistence of a setter. - attribute_flags |= (descriptor->setter == nullptr ? - v8::PropertyAttribute::ReadOnly : v8::PropertyAttribute::None); - } else if ((descriptor->attributes & napi_writable) == 0) { - attribute_flags |= v8::PropertyAttribute::ReadOnly; - } - - if ((descriptor->attributes & napi_enumerable) == 0) { - attribute_flags |= v8::PropertyAttribute::DontEnum; - } - if ((descriptor->attributes & napi_configurable) == 0) { - attribute_flags |= v8::PropertyAttribute::DontDelete; - } - - return static_cast(attribute_flags); -} - -class HandleScopeWrapper { -public: - explicit HandleScopeWrapper(v8::Isolate* isolate) : scope(isolate) {} - -private: - v8::HandleScope scope; -}; - -// In node v0.10 version of v8, there is no EscapableHandleScope and the -// node v0.10 port use HandleScope::Close(Local v) to mimic the behavior -// of a EscapableHandleScope::Escape(Local v), but it is not the same -// semantics. This is an example of where the api abstraction fail to work -// across different versions. -class EscapableHandleScopeWrapper { -public: - explicit EscapableHandleScopeWrapper(v8::Isolate* isolate) - : scope(isolate), escape_called_(false) {} - bool escape_called() const - { - return escape_called_; - } - template - v8::Local Escape(v8::Local handle) + // convert from n-api property attributes to v8::PropertyAttribute + static inline v8::PropertyAttribute V8PropertyAttributesFromDescriptor( + const napi_property_descriptor* descriptor) { - escape_called_ = true; - return scope.Escape(handle); - } - -private: - v8::EscapableHandleScope scope; - bool escape_called_; -}; - -static -napi_handle_scope JsHandleScopeFromV8HandleScope(HandleScopeWrapper* s) -{ - return reinterpret_cast(s); -} - -static -HandleScopeWrapper* V8HandleScopeFromJsHandleScope(napi_handle_scope s) -{ - return reinterpret_cast(s); -} - -static -napi_escapable_handle_scope JsEscapableHandleScopeFromV8EscapableHandleScope( - EscapableHandleScopeWrapper* s) -{ - return reinterpret_cast(s); -} - -static -EscapableHandleScopeWrapper* -V8EscapableHandleScopeFromJsEscapableHandleScope( - napi_escapable_handle_scope s) -{ - return reinterpret_cast(s); -} - -static -napi_callback_scope JsCallbackScopeFromV8CallbackScope( - node::CallbackScope* s) -{ - return reinterpret_cast(s); -} - -static -node::CallbackScope* V8CallbackScopeFromJsCallbackScope( - napi_callback_scope s) -{ - return reinterpret_cast(s); -} - -//=== Conversion between V8 Handles and napi_value ======================== - -// This asserts v8::Local<> will always be implemented with a single -// pointer field so that we can pass it around as a void*. -static_assert(sizeof(v8::Local) == sizeof(napi_value), - "Cannot convert between v8::Local and napi_value"); + unsigned int attribute_flags = v8::PropertyAttribute::None; + + if (descriptor->getter != nullptr || descriptor->setter != nullptr) { + // The napi_writable attribute is ignored for accessor descriptors, but + // V8 requires the ReadOnly attribute to match nonexistence of a setter. + attribute_flags |= (descriptor->setter == nullptr ? v8::PropertyAttribute::ReadOnly : v8::PropertyAttribute::None); + } else if ((descriptor->attributes & napi_writable) == 0) { + attribute_flags |= v8::PropertyAttribute::ReadOnly; + } -static -napi_deferred JsDeferredFromNodePersistent(node::Persistent* local) -{ - return reinterpret_cast(local); -} + if ((descriptor->attributes & napi_enumerable) == 0) { + attribute_flags |= v8::PropertyAttribute::DontEnum; + } + if ((descriptor->attributes & napi_configurable) == 0) { + attribute_flags |= v8::PropertyAttribute::DontDelete; + } -static -node::Persistent* NodePersistentFromJsDeferred(napi_deferred local) -{ - return reinterpret_cast*>(local); -} + return static_cast(attribute_flags); + } -static -napi_value JsValueFromV8LocalValue(v8::Local local) -{ - return reinterpret_cast(*local); -} + class HandleScopeWrapper { + public: + explicit HandleScopeWrapper(v8::Isolate* isolate) + : scope(isolate) + { + } -static -v8::Local V8LocalValueFromJsValue(napi_value v) -{ - v8::Local local; - memcpy(&local, &v, sizeof(v)); - return local; -} + private: + v8::HandleScope scope; + }; -static inline void trigger_fatal_exception( - napi_env env, v8::Local local_err) -{ - // v8::Local local_msg = v8::Exception::CreateMessage(env->isolate, local_err); - // node::FatalException(env->isolate, local_err, local_msg); - DebugBreak(); // weolar TODO -} + // In node v0.10 version of v8, there is no EscapableHandleScope and the + // node v0.10 port use HandleScope::Close(Local v) to mimic the behavior + // of a EscapableHandleScope::Escape(Local v), but it is not the same + // semantics. This is an example of where the api abstraction fail to work + // across different versions. + class EscapableHandleScopeWrapper { + public: + explicit EscapableHandleScopeWrapper(v8::Isolate* isolate) + : scope(isolate) + , escape_called_(false) + { + } + bool escape_called() const + { + return escape_called_; + } + template + v8::Local Escape(v8::Local handle) + { + escape_called_ = true; + return scope.Escape(handle); + } -static inline napi_status V8NameFromPropertyDescriptor(napi_env env, - const napi_property_descriptor* p, - v8::Local* result) -{ - if (p->utf8name != nullptr) { - CHECK_NEW_FROM_UTF8(env, *result, p->utf8name); - } else { - v8::Local property_value = - v8impl::V8LocalValueFromJsValue(p->name); + private: + v8::EscapableHandleScope scope; + bool escape_called_; + }; - RETURN_STATUS_IF_FALSE(env, property_value->IsName(), napi_name_expected); - *result = property_value.As(); + static napi_handle_scope JsHandleScopeFromV8HandleScope(HandleScopeWrapper* s) + { + return reinterpret_cast(s); } - return napi_ok; -} - -// Adapter for napi_finalize callbacks. -class Finalizer { -protected: - Finalizer(napi_env env, - napi_finalize finalize_callback, - void* finalize_data, - void* finalize_hint) - : _env(env), - _finalize_callback(finalize_callback), - _finalize_data(finalize_data), - _finalize_hint(finalize_hint) + static HandleScopeWrapper* V8HandleScopeFromJsHandleScope(napi_handle_scope s) { + return reinterpret_cast(s); } - ~Finalizer() + static napi_escapable_handle_scope JsEscapableHandleScopeFromV8EscapableHandleScope( + EscapableHandleScopeWrapper* s) { + return reinterpret_cast(s); } -public: - static Finalizer* New(napi_env env, - napi_finalize finalize_callback = nullptr, - void* finalize_data = nullptr, - void* finalize_hint = nullptr) + static EscapableHandleScopeWrapper* + V8EscapableHandleScopeFromJsEscapableHandleScope( + napi_escapable_handle_scope s) { - return new Finalizer( - env, finalize_callback, finalize_data, finalize_hint); + return reinterpret_cast(s); } - static void Delete(Finalizer* finalizer) + static napi_callback_scope JsCallbackScopeFromV8CallbackScope( + node::CallbackScope* s) { - delete finalizer; + return reinterpret_cast(s); } - // node::Buffer::FreeCallback - static void FinalizeBufferCallback(char* data, void* hint) + static node::CallbackScope* V8CallbackScopeFromJsCallbackScope( + napi_callback_scope s) { - Finalizer* finalizer = static_cast(hint); - if (finalizer->_finalize_callback != nullptr) { - NAPI_CALL_INTO_MODULE_THROW(finalizer->_env, - finalizer->_finalize_callback( - finalizer->_env, - data, - finalizer->_finalize_hint)); - } - - Delete(finalizer); + return reinterpret_cast(s); } -protected: - napi_env _env; - napi_finalize _finalize_callback; - void* _finalize_data; - void* _finalize_hint; -}; + //=== Conversion between V8 Handles and napi_value ======================== -// Wrapper around node::Persistent that implements reference counting. -class Reference : private Finalizer { -private: - Reference(napi_env env, - v8::Local value, - uint32_t initial_refcount, - bool delete_self, - napi_finalize finalize_callback, - void* finalize_data, - void* finalize_hint) - : Finalizer(env, finalize_callback, finalize_data, finalize_hint), - _persistent(env->isolate, value), - _refcount(initial_refcount), - _delete_self(delete_self) + // This NODE_ASSERTs v8::Local<> will always be implemented with a single + // pointer field so that we can pass it around as a void*. + static_assert(sizeof(v8::Local) == sizeof(napi_value), + "Cannot convert between v8::Local and napi_value"); + + static napi_deferred JsDeferredFromNodePersistent(node::Persistent* local) { - if (initial_refcount == 0) { - _persistent.SetWeak( - this, FinalizeCallback, v8::WeakCallbackType::kParameter); - } + return reinterpret_cast(local); } -public: - void* Data() + static node::Persistent* NodePersistentFromJsDeferred(napi_deferred local) { - return _finalize_data; + return reinterpret_cast*>(local); } - static Reference* New(napi_env env, - v8::Local value, - uint32_t initial_refcount, - bool delete_self, - napi_finalize finalize_callback = nullptr, - void* finalize_data = nullptr, - void* finalize_hint = nullptr) + static napi_value JsValueFromV8LocalValue(v8::Local local) { - return new Reference(env, - value, - initial_refcount, - delete_self, - finalize_callback, - finalize_data, - finalize_hint); + return reinterpret_cast(*local); } - static void Delete(Reference* reference) + static v8::Local V8LocalValueFromJsValue(napi_value v) { - delete reference; + v8::Local local; + memcpy(&local, &v, sizeof(v)); + return local; } - uint32_t Ref() + static inline void trigger_fatal_exception( + napi_env env, v8::Local local_err) { - if (++_refcount == 1) { - _persistent.ClearWeak(); - } - - return _refcount; + // v8::Local local_msg = v8::Exception::CreateMessage(env->isolate, local_err); + // node::FatalException(env->isolate, local_err, local_msg); + DebugBreak(); // weolar TODO } - uint32_t Unref() + static inline napi_status V8NameFromPropertyDescriptor(napi_env env, + const napi_property_descriptor* p, + v8::Local* result) { - if (_refcount == 0) { - return 0; - } - if (--_refcount == 0) { - _persistent.SetWeak( - this, FinalizeCallback, v8::WeakCallbackType::kParameter); - } + if (p->utf8name != nullptr) { + NODE_CHECK_NEW_FROM_UTF8(env, *result, p->utf8name); + } else { + v8::Local property_value = v8impl::V8LocalValueFromJsValue(p->name); - return _refcount; - } + RETURN_STATUS_IF_FALSE(env, property_value->IsName(), napi_name_expected); + *result = property_value.As(); + } - uint32_t RefCount() - { - return _refcount; + return napi_ok; } - v8::Local Get() - { - if (_persistent.IsEmpty()) { - return v8::Local(); - } else { - return v8::Local::New(_env->isolate, _persistent); + // Adapter for napi_finalize callbacks. + class Finalizer { + protected: + Finalizer(napi_env env, + napi_finalize finalize_callback, + void* finalize_data, + void* finalize_hint) + : _env(env) + , _finalize_callback(finalize_callback) + , _finalize_data(finalize_data) + , _finalize_hint(finalize_hint) + { } - } -private: - static void FinalizeCallback(const v8::WeakCallbackInfo& data) - { - Reference* reference = data.GetParameter(); - reference->_persistent.Reset(); - - // Check before calling the finalize callback, because the callback might - // delete it. - bool delete_self = reference->_delete_self; - napi_env env = reference->_env; - - if (reference->_finalize_callback != nullptr) { - NAPI_CALL_INTO_MODULE_THROW(env, - reference->_finalize_callback( - reference->_env, - reference->_finalize_data, - reference->_finalize_hint)); + ~Finalizer() + { } - if (delete_self) { - Delete(reference); + public: + static Finalizer* New(napi_env env, + napi_finalize finalize_callback = nullptr, + void* finalize_data = nullptr, + void* finalize_hint = nullptr) + { + return new Finalizer( + env, finalize_callback, finalize_data, finalize_hint); } - } - node::Persistent _persistent; - uint32_t _refcount; - bool _delete_self; -}; + static void Delete(Finalizer* finalizer) + { + delete finalizer; + } -class TryCatch : public v8::TryCatch { -public: - explicit TryCatch(napi_env env) - : v8::TryCatch(env->isolate), _env(env) {} + // node::Buffer::FreeCallback + static void FinalizeBufferCallback(char* data, void* hint) + { + Finalizer* finalizer = static_cast(hint); + if (finalizer->_finalize_callback != nullptr) { + NAPI_CALL_INTO_MODULE_THROW(finalizer->_env, + finalizer->_finalize_callback( + finalizer->_env, + data, + finalizer->_finalize_hint)); + } - ~TryCatch() - { - if (HasCaught()) { - _env->last_exception.Reset(_env->isolate, Exception()); + Delete(finalizer); } - } -private: - napi_env _env; -}; + protected: + napi_env _env; + napi_finalize _finalize_callback; + void* _finalize_data; + void* _finalize_hint; + }; -//=== Function napi_callback wrapper ================================= - -// Use this data structure to associate callback data with each N-API function -// exposed to JavaScript. The structure is stored in a v8::External which gets -// passed into our callback wrapper. This reduces the performance impact of -// calling through N-API. -// Ref: benchmark/misc/function_call -// Discussion (incl. perf. data): https://github.com/nodejs/node/pull/21072 -struct CallbackBundle { - // Bind the lifecycle of `this` C++ object to a JavaScript object. - // We never delete a CallbackBundle C++ object directly. - void BindLifecycleTo(v8::Isolate* isolate, v8::Local target) - { - handle.Reset(isolate, target); - handle.SetWeak(this, WeakCallback, v8::WeakCallbackType::kParameter); - } + // Wrapper around node::Persistent that implements reference counting. + class Reference : private Finalizer { + private: + Reference(napi_env env, + v8::Local value, + uint32_t initial_refcount, + bool delete_self, + napi_finalize finalize_callback, + void* finalize_data, + void* finalize_hint) + : Finalizer(env, finalize_callback, finalize_data, finalize_hint) + , _persistent(env->isolate, value) + , _refcount(initial_refcount) + , _delete_self(delete_self) + { + if (initial_refcount == 0) { + _persistent.SetWeak(this, FinalizeCallback, v8::WeakCallbackType::kParameter); + } + } - napi_env env; // Necessary to invoke C++ NAPI callback - void* cb_data; // The user provided callback data - napi_callback function_or_getter; - napi_callback setter; - node::Persistent handle; // Die with this JavaScript object +// ~Reference() +// { +// if (!_persistent.IsEmpty()) +// _persistent.ClearWeak(); +// } -private: - static void WeakCallback(v8::WeakCallbackInfo const& info) - { - // Use the "WeakCallback mechanism" to delete the C++ `bundle` object. - // This will be called when the v8::External containing `this` pointer - // is being GC-ed. - CallbackBundle* bundle = info.GetParameter(); - if (bundle != nullptr) { - delete bundle; + public: + void* Data() + { + return _finalize_data; } - } -}; -// Base class extended by classes that wrap V8 function and property callback -// info. -class CallbackWrapper { -public: - CallbackWrapper(napi_value this_arg, size_t args_length, void* data) - : _this(this_arg), _args_length(args_length), _data(data) {} + static Reference* New(napi_env env, + v8::Local value, + uint32_t initial_refcount, + bool delete_self, + napi_finalize finalize_callback = nullptr, + void* finalize_data = nullptr, + void* finalize_hint = nullptr) + { + return new Reference(env, + value, + initial_refcount, + delete_self, + finalize_callback, + finalize_data, + finalize_hint); + } - virtual napi_value GetNewTarget() = 0; - virtual void Args(napi_value* buffer, size_t bufferlength) = 0; - virtual void SetReturnValue(napi_value value) = 0; + static void Delete(Reference* reference) + { + if (!reference->_persistent.IsEmpty() +#if V8_MAJOR_VERSION > 5 + && reference->_persistent.GetWeak() +#endif + ) { + reference->_delete_self = true; + } else + delete reference; + } - napi_value This() { return _this; } + uint32_t Ref() + { + if (++_refcount == 1) { + _persistent.ClearWeak(); + } - size_t ArgsLength() { return _args_length; } + return _refcount; + } - void* Data() { return _data; } + uint32_t Unref() + { + if (_refcount == 0) { + return 0; + } + if (--_refcount == 0) { + _persistent.SetWeak(this, FinalizeCallback, v8::WeakCallbackType::kParameter); + } -protected: - const napi_value _this; - const size_t _args_length; - void* _data; -}; + return _refcount; + } -template -class CallbackWrapperBase : public CallbackWrapper { -public: - CallbackWrapperBase(const Info& cbinfo, const size_t args_length) - : CallbackWrapper(JsValueFromV8LocalValue(cbinfo.This()), - args_length, - nullptr), - _cbinfo(cbinfo) - { - _bundle = reinterpret_cast( - v8::Local::Cast(cbinfo.Data())->Value()); - _data = _bundle->cb_data; - } + uint32_t RefCount() + { + return _refcount; + } - napi_value GetNewTarget() override { return nullptr; } + v8::Local Get() + { + if (_persistent.IsEmpty()) { + return v8::Local(); + } else { + return v8::Local::New(_env->isolate, _persistent); + } + } -protected: - void InvokeCallback() - { - napi_callback_info cbinfo_wrapper = reinterpret_cast( - static_cast(this)); + private: + static void FinalizeCallback(const v8::WeakCallbackInfo& data) + { + Reference* reference = data.GetParameter(); + reference->_persistent.Reset(); + + // Check before calling the finalize callback, because the callback might + // delete it. + bool delete_self = reference->_delete_self; + napi_env env = reference->_env; + + if (reference->_finalize_callback != nullptr) { + NAPI_CALL_INTO_MODULE_THROW(env, + reference->_finalize_callback( + reference->_env, + reference->_finalize_data, + reference->_finalize_hint)); + } - // All other pointers we need are stored in `_bundle` - napi_env env = _bundle->env; - napi_callback cb = _bundle->*FunctionField; + if (delete_self) { + Delete(reference); + } + } - napi_value result; - NAPI_CALL_INTO_MODULE_THROW(env, result = cb(env, cbinfo_wrapper)); + node::Persistent _persistent; + uint32_t _refcount; + bool _delete_self; + }; - if (result != nullptr) { - this->SetReturnValue(result); + class TryCatch : public v8::TryCatch { + public: + explicit TryCatch(napi_env env) + : v8::TryCatch(env->isolate) + , _env(env) + { } - } - - const Info& _cbinfo; - CallbackBundle* _bundle; -}; -class FunctionCallbackWrapper - : public CallbackWrapperBase, - &CallbackBundle::function_or_getter> { -public: - static void Invoke(const v8::FunctionCallbackInfo& info) - { - FunctionCallbackWrapper cbwrapper(info); - cbwrapper.InvokeCallback(); - } + ~TryCatch() + { + if (HasCaught()) { + _env->last_exception.Reset(_env->isolate, Exception()); + } + } - explicit FunctionCallbackWrapper( - const v8::FunctionCallbackInfo& cbinfo) - : CallbackWrapperBase(cbinfo, cbinfo.Length()) - { - } + private: + napi_env _env; + }; - napi_value GetNewTarget() override - { - if (_cbinfo.IsConstructCall()) { - return v8impl::JsValueFromV8LocalValue(_cbinfo.NewTarget()); - } else { - return nullptr; + //=== Function napi_callback wrapper ================================= + + // Use this data structure to associate callback data with each N-API function + // exposed to JavaScript. The structure is stored in a v8::External which gets + // passed into our callback wrapper. This reduces the performance impact of + // calling through N-API. + // Ref: benchmark/misc/function_call + // Discussion (incl. perf. data): https://github.com/nodejs/node/pull/21072 + struct CallbackBundle { + // Bind the lifecycle of `this` C++ object to a JavaScript object. + // We never delete a CallbackBundle C++ object directly. + void BindLifecycleTo(v8::Isolate* isolate, v8::Local target) + { + handle.Reset(isolate, target); + handle.SetWeak(this, WeakCallback, v8::WeakCallbackType::kParameter); } - } - /*virtual*/ - void Args(napi_value* buffer, size_t buffer_length) override - { - size_t i = 0; - size_t min = std::min(buffer_length, _args_length); + napi_env env; // Necessary to invoke C++ NAPI callback + void* cb_data; // The user provided callback data + napi_callback function_or_getter; + napi_callback setter; + node::Persistent handle; // Die with this JavaScript object - for (; i < min; i += 1) { - buffer[i] = v8impl::JsValueFromV8LocalValue(_cbinfo[i]); + private: + static void WeakCallback(v8::WeakCallbackInfo const& info) + { + // Use the "WeakCallback mechanism" to delete the C++ `bundle` object. + // This will be called when the v8::External containing `this` pointer + // is being GC-ed. + CallbackBundle* bundle = info.GetParameter(); + if (bundle != nullptr) { + delete bundle; + } } + }; - if (i < buffer_length) { - napi_value undefined = - v8impl::JsValueFromV8LocalValue(v8::Undefined(_cbinfo.GetIsolate())); - for (; i < buffer_length; i += 1) { - buffer[i] = undefined; - } + // Base class extended by classes that wrap V8 function and property callback + // info. + class CallbackWrapper { + public: + CallbackWrapper(napi_value this_arg, size_t args_length, void* data) + : _this(this_arg) + , _args_length(args_length) + , _data(data) + { } - } - /*virtual*/ - void SetReturnValue(napi_value value) override - { - v8::Local val = v8impl::V8LocalValueFromJsValue(value); - _cbinfo.GetReturnValue().Set(val); - } -}; + virtual napi_value GetNewTarget() = 0; + virtual void Args(napi_value* buffer, size_t bufferlength) = 0; + virtual void SetReturnValue(napi_value value) = 0; -class GetterCallbackWrapper - : public CallbackWrapperBase, - &CallbackBundle::function_or_getter> { -public: - static void Invoke(v8::Local property, - const v8::PropertyCallbackInfo& info) - { - GetterCallbackWrapper cbwrapper(info); - cbwrapper.InvokeCallback(); - } + napi_value This() { return _this; } - explicit GetterCallbackWrapper( - const v8::PropertyCallbackInfo& cbinfo) - : CallbackWrapperBase(cbinfo, 0) - { - } + size_t ArgsLength() { return _args_length; } - /*virtual*/ - void Args(napi_value* buffer, size_t buffer_length) override - { - if (buffer_length > 0) { - napi_value undefined = - v8impl::JsValueFromV8LocalValue(v8::Undefined(_cbinfo.GetIsolate())); - for (size_t i = 0; i < buffer_length; i += 1) { - buffer[i] = undefined; - } + void* Data() { return _data; } + + protected: + const napi_value _this; + const size_t _args_length; + void* _data; + }; + + template + class CallbackWrapperBase : public CallbackWrapper { + public: + CallbackWrapperBase(const Info& cbinfo, const size_t args_length) + : CallbackWrapper(JsValueFromV8LocalValue(cbinfo.This()), + args_length, + nullptr) + , _cbinfo(cbinfo) + { + _bundle = reinterpret_cast( + v8::Local::Cast(cbinfo.Data())->Value()); + _data = _bundle->cb_data; } - } - /*virtual*/ - void SetReturnValue(napi_value value) override - { - v8::Local val = v8impl::V8LocalValueFromJsValue(value); - _cbinfo.GetReturnValue().Set(val); - } -}; + napi_value GetNewTarget() override { return nullptr; } -class SetterCallbackWrapper - : public CallbackWrapperBase, - &CallbackBundle::setter> { -public: - static void Invoke(v8::Local property, - v8::Local value, - const v8::PropertyCallbackInfo& info) - { - SetterCallbackWrapper cbwrapper(info, value); - cbwrapper.InvokeCallback(); - } + protected: + void InvokeCallback() + { + napi_callback_info cbinfo_wrapper = reinterpret_cast( + static_cast(this)); - SetterCallbackWrapper(const v8::PropertyCallbackInfo& cbinfo, - const v8::Local& value) - : CallbackWrapperBase(cbinfo, 1), _value(value) - { - } + // All other pointers we need are stored in `_bundle` + napi_env env = _bundle->env; + napi_callback cb = _bundle->*FunctionField; - /*virtual*/ - void Args(napi_value* buffer, size_t buffer_length) override - { - if (buffer_length > 0) { - buffer[0] = v8impl::JsValueFromV8LocalValue(_value); + napi_value result; + NAPI_CALL_INTO_MODULE_THROW(env, result = cb(env, cbinfo_wrapper)); - if (buffer_length > 1) { - napi_value undefined = v8impl::JsValueFromV8LocalValue( - v8::Undefined(_cbinfo.GetIsolate())); - for (size_t i = 1; i < buffer_length; i += 1) { - buffer[i] = undefined; - } + if (result != nullptr) { + this->SetReturnValue(result); } } - } - - /*virtual*/ - void SetReturnValue(napi_value value) override - { - // Ignore any value returned from a setter callback. - } -private: - const v8::Local& _value; -}; - -// Creates an object to be made available to the static function callback -// wrapper, used to retrieve the native callback function and data pointer. -static v8::Local CreateFunctionCallbackData(napi_env env, napi_callback cb, void* data) -{ - CallbackBundle* bundle = new CallbackBundle(); - bundle->function_or_getter = cb; - bundle->cb_data = data; - bundle->env = env; - v8::Local cbdata = v8::External::New(env->isolate, bundle); - bundle->BindLifecycleTo(env->isolate, cbdata); + const Info& _cbinfo; + CallbackBundle* _bundle; + }; - return cbdata; -} + class FunctionCallbackWrapper + : public CallbackWrapperBase, + &CallbackBundle::function_or_getter> { + public: + static void Invoke(const v8::FunctionCallbackInfo& info) + { + FunctionCallbackWrapper cbwrapper(info); + cbwrapper.InvokeCallback(); + } -// Creates an object to be made available to the static getter/setter -// callback wrapper, used to retrieve the native getter/setter callback -// function and data pointer. -static v8::Local CreateAccessorCallbackData(napi_env env, - napi_callback getter, - napi_callback setter, - void* data) -{ - CallbackBundle* bundle = new CallbackBundle(); - bundle->function_or_getter = getter; - bundle->setter = setter; - bundle->cb_data = data; - bundle->env = env; - v8::Local cbdata = v8::External::New(env->isolate, bundle); - bundle->BindLifecycleTo(env->isolate, cbdata); + explicit FunctionCallbackWrapper( + const v8::FunctionCallbackInfo& cbinfo) + : CallbackWrapperBase(cbinfo, cbinfo.Length()) + { + } - return cbdata; -} + napi_value GetNewTarget() override + { + if (_cbinfo.IsConstructCall()) { + return v8impl::JsValueFromV8LocalValue(_cbinfo.NewTarget()); + } else { + return nullptr; + } + } -static void DeleteEnv(napi_env env, void* data, void* hint) -{ - delete env; -} + /*virtual*/ + void Args(napi_value* buffer, size_t buffer_length) override + { + size_t i = 0; + size_t min = std::min(buffer_length, _args_length); -static uv_loop_t* GetCurrentEventLoop(v8::Isolate* isolate) -{ - v8::HandleScope handle_scope(isolate); - v8::Local context = isolate->GetCurrentContext(); - if (context.IsEmpty()) - return nullptr; - node::Environment* env = node::Environment::GetCurrent(context); - if (env == nullptr) - return nullptr; - return env->event_loop(); -} + for (; i < min; i += 1) { + buffer[i] = v8impl::JsValueFromV8LocalValue(_cbinfo[i]); + } -static napi_env GetEnv(v8::Local context) -{ - napi_env result; + if (i < buffer_length) { + napi_value undefined = v8impl::JsValueFromV8LocalValue(v8::Undefined(_cbinfo.GetIsolate())); + for (; i < buffer_length; i += 1) { + buffer[i] = undefined; + } + } + } - v8::Isolate* isolate = context->GetIsolate(); - v8::Local global = context->Global(); + /*virtual*/ + void SetReturnValue(napi_value value) override + { + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + _cbinfo.GetReturnValue().Set(val); + } + }; - // In the case of the string for which we grab the private and the value of - // the private on the global object we can call .ToLocalChecked() directly - // because we need to stop hard if either of them is empty. - // - // Re https://github.com/nodejs/node/pull/14217#discussion_r128775149 - v8::Local value = global->GetPrivate(context, NAPI_PRIVATE_KEY(context, env)).ToLocalChecked(); + class GetterCallbackWrapper + : public CallbackWrapperBase, + &CallbackBundle::function_or_getter> { + public: + static void Invoke(v8::Local property, + const v8::PropertyCallbackInfo& info) + { + GetterCallbackWrapper cbwrapper(info); + cbwrapper.InvokeCallback(); + } - if (value->IsExternal()) { - result = static_cast(value.As()->Value()); - } else { - result = new napi_env__(isolate, GetCurrentEventLoop(isolate)); + explicit GetterCallbackWrapper( + const v8::PropertyCallbackInfo& cbinfo) + : CallbackWrapperBase(cbinfo, 0) + { + } - v8::Local external = v8::External::New(isolate, result); + /*virtual*/ + void Args(napi_value* buffer, size_t buffer_length) override + { + if (buffer_length > 0) { + napi_value undefined = v8impl::JsValueFromV8LocalValue(v8::Undefined(_cbinfo.GetIsolate())); + for (size_t i = 0; i < buffer_length; i += 1) { + buffer[i] = undefined; + } + } + } - // We must also stop hard if the result of assigning the env to the global - // is either nothing or false. - CHECK(global->SetPrivate(context, NAPI_PRIVATE_KEY(context, env), external).FromJust()); + /*virtual*/ + void SetReturnValue(napi_value value) override + { + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + _cbinfo.GetReturnValue().Set(val); + } + }; - // Create a self-destructing reference to external that will get rid of the - // napi_env when external goes out of scope. - Reference::New(result, external, 0, true, DeleteEnv, nullptr, nullptr); - } + class SetterCallbackWrapper + : public CallbackWrapperBase, + &CallbackBundle::setter> { + public: + static void Invoke(v8::Local property, + v8::Local value, + const v8::PropertyCallbackInfo& info) + { + SetterCallbackWrapper cbwrapper(info, value); + cbwrapper.InvokeCallback(); + } - return result; -} + SetterCallbackWrapper(const v8::PropertyCallbackInfo& cbinfo, + const v8::Local& value) + : CallbackWrapperBase(cbinfo, 1) + , _value(value) + { + } -enum UnwrapAction { - KeepWrap, - RemoveWrap -}; + /*virtual*/ + void Args(napi_value* buffer, size_t buffer_length) override + { + if (buffer_length > 0) { + buffer[0] = v8impl::JsValueFromV8LocalValue(_value); + + if (buffer_length > 1) { + napi_value undefined = v8impl::JsValueFromV8LocalValue( + v8::Undefined(_cbinfo.GetIsolate())); + for (size_t i = 1; i < buffer_length; i += 1) { + buffer[i] = undefined; + } + } + } + } -static napi_status Unwrap(napi_env env, napi_value js_object, void** result, UnwrapAction action) -{ - NAPI_PREAMBLE(env); - CHECK_ARG(env, js_object); - if (action == KeepWrap) { - CHECK_ARG(env, result); - } + /*virtual*/ + void SetReturnValue(napi_value value) override + { + // Ignore any value returned from a setter callback. + } - v8::Isolate* isolate = env->isolate; - v8::Local context = isolate->GetCurrentContext(); + private: + const v8::Local& _value; + }; - v8::Local value = v8impl::V8LocalValueFromJsValue(js_object); - RETURN_STATUS_IF_FALSE(env, value->IsObject(), napi_invalid_arg); - v8::Local obj = value.As(); + // Creates an object to be made available to the static function callback + // wrapper, used to retrieve the native callback function and data pointer. + static v8::Local CreateFunctionCallbackData(napi_env env, napi_callback cb, void* data) + { + CallbackBundle* bundle = new CallbackBundle(); + bundle->function_or_getter = cb; + bundle->cb_data = data; + bundle->env = env; + v8::Local cbdata = v8::External::New(env->isolate, bundle); + bundle->BindLifecycleTo(env->isolate, cbdata); + + return cbdata; + } + + // Creates an object to be made available to the static getter/setter + // callback wrapper, used to retrieve the native getter/setter callback + // function and data pointer. + static v8::Local CreateAccessorCallbackData(napi_env env, + napi_callback getter, + napi_callback setter, + void* data) + { + CallbackBundle* bundle = new CallbackBundle(); + bundle->function_or_getter = getter; + bundle->setter = setter; + bundle->cb_data = data; + bundle->env = env; + v8::Local cbdata = v8::External::New(env->isolate, bundle); + bundle->BindLifecycleTo(env->isolate, cbdata); - auto val = obj->GetPrivate(context, NAPI_PRIVATE_KEY(context, wrapper)).ToLocalChecked(); - RETURN_STATUS_IF_FALSE(env, val->IsExternal(), napi_invalid_arg); - Reference* reference = - static_cast(val.As()->Value()); + return cbdata; + } - if (result) { - *result = reference->Data(); + static void DeleteEnv(napi_env env, void* data, void* hint) + { + delete env; } - if (action == RemoveWrap) { - CHECK(obj->DeletePrivate(context, NAPI_PRIVATE_KEY(context, wrapper)).FromJust()); - Reference::Delete(reference); + static uv_loop_t* GetCurrentEventLoop(v8::Isolate* isolate) + { + v8::HandleScope handle_scope(isolate); + v8::Local context = isolate->GetCurrentContext(); + if (context.IsEmpty()) + return nullptr; + node::Environment* env = node::Environment::GetCurrent(context); + if (env == nullptr) + return nullptr; + return env->event_loop(); } - return GET_RETURN_STATUS(env); -} + static inline napi_env NewEnv(v8::Local context) + { + napi_env result; + v8::Isolate* isolate = context->GetIsolate(); -static -napi_status ConcludeDeferred(napi_env env, - napi_deferred deferred, - napi_value result, - bool is_resolved) -{ - NAPI_PREAMBLE(env); - CHECK_ARG(env, result); + result = new napi_env__(isolate, GetCurrentEventLoop(isolate)); + // TODO(addaleax): There was previously code that tried to delete the + // napi_env when its v8::Context was garbage collected; + // However, as long as N-API addons using this napi_env are in place, + // the Context needs to be accessible and alive. + // Ideally, we'd want an on-addon-unload hook that takes care of this + // once all N-API addons using this napi_env are unloaded. + // For now, a per-Environment cleanup hook is the best we can do. +// result->node_env()->AddCleanupHook( +// [](void* arg) { +// static_cast(arg)->Unref(); +// }, +// static_cast(result)); + + return result; + } + +// static napi_env GetEnv(v8::Local context) +// { +// napi_env result; +// +// v8::Isolate* isolate = context->GetIsolate(); +// v8::Local global = context->Global(); +// +// // In the case of the string for which we grab the private and the value of +// // the private on the global object we can call .ToLocalChecked() directly +// // because we need to stop hard if either of them is empty. +// // +// // Re https://github.com/nodejs/node/pull/14217#discussion_r128775149 +// v8::Local value = global->GetPrivate(context, NAPI_PRIVATE_KEY(context, env)).ToLocalChecked(); +// +// if (value->IsExternal()) { +// result = static_cast(value.As()->Value()); +// } else { +// result = new napi_env__(isolate, GetCurrentEventLoop(isolate)); +// +// v8::Local external = v8::External::New(isolate, result); +// +// // We must also stop hard if the result of assigning the env to the global +// // is either nothing or false. +// NODE_CHECK(global->SetPrivate(context, NAPI_PRIVATE_KEY(context, env), external).FromJust()); +// +// // Create a self-destructing reference to external that will get rid of the +// // napi_env when external goes out of scope. +// Reference::New(result, external, 0, true, DeleteEnv, nullptr, nullptr); +// } +// +// return result; +// } - v8::Local context = env->isolate->GetCurrentContext(); - node::Persistent* deferred_ref = - NodePersistentFromJsDeferred(deferred); - v8::Local v8_deferred = - v8::Local::New(env->isolate, *deferred_ref); + enum UnwrapAction { + KeepWrap, + RemoveWrap + }; - auto v8_resolver = v8::Local::Cast(v8_deferred); + static napi_status Unwrap(napi_env env, napi_value js_object, void** result, UnwrapAction action) + { + NAPI_PREAMBLE(env); + NODE_CHECK_ARG(env, js_object); + if (action == KeepWrap) { + NODE_CHECK_ARG(env, result); + } - v8::Maybe success = is_resolved ? - v8_resolver->Resolve(context, v8impl::V8LocalValueFromJsValue(result)) : - v8_resolver->Reject(context, v8impl::V8LocalValueFromJsValue(result)); + v8::Isolate* isolate = env->isolate; + v8::Local context = isolate->GetCurrentContext(); - delete deferred_ref; + v8::Local value = v8impl::V8LocalValueFromJsValue(js_object); + RETURN_STATUS_IF_FALSE(env, value->IsObject(), napi_invalid_arg); + v8::Local obj = value.As(); - RETURN_STATUS_IF_FALSE(env, success.FromMaybe(false), napi_generic_failure); + auto val = obj->GetPrivate(context, NAPI_PRIVATE_KEY(context, wrapper)).ToLocalChecked(); + RETURN_STATUS_IF_FALSE(env, val->IsExternal(), napi_invalid_arg); + Reference* reference = static_cast(val.As()->Value()); - return GET_RETURN_STATUS(env); -} + if (result) { + *result = reference->Data(); + } -class ThreadSafeFunction : public node::AsyncResource { -public: - ThreadSafeFunction(v8::Local func, - v8::Local resource, - v8::Local name, - v8::Local v8_context, - size_t thread_count_, - void* context_, - size_t max_queue_size_, - napi_env env_, - void* finalize_data_, - napi_finalize finalize_cb_, - napi_threadsafe_function_call_js call_js_cb_) : - AsyncResource(env_->isolate, resource, (*v8::String::Utf8Value(name)), -1), - - m_thread_count(thread_count_), - m_is_closing(false), - m_context(context_), - m_max_queue_size(max_queue_size_), - m_env(env_), - m_finalize_data(finalize_data_), - m_finalize_cb(finalize_cb_), - m_call_js_cb(call_js_cb_ == nullptr ? CallJs : call_js_cb_), - handles_closing(false) - { - m_refFunction.Reset(m_env->isolate, func); - m_v8_context.Reset(m_env->isolate, v8_context); - node::AddEnvironmentCleanupHook(m_env->isolate, Cleanup, this); + if (action == RemoveWrap) { + NODE_CHECK(obj->DeletePrivate(context, NAPI_PRIVATE_KEY(context, wrapper)).FromJust()); + Reference::Delete(reference); + } + + return GET_RETURN_STATUS(env); } - ~ThreadSafeFunction() + static napi_status ConcludeDeferred(napi_env env, + napi_deferred deferred, + napi_value result, + bool is_resolved) { - m_v8_context.Reset(); - node::RemoveEnvironmentCleanupHook(m_env->isolate, Cleanup, this); - } + NAPI_PREAMBLE(env); + NODE_CHECK_ARG(env, result); + + v8::Local context = env->isolate->GetCurrentContext(); + node::Persistent* deferred_ref = NodePersistentFromJsDeferred(deferred); + v8::Local v8_deferred = v8::Local::New(env->isolate, *deferred_ref); + + auto v8_resolver = v8::Local::Cast(v8_deferred); + + v8::Maybe success = is_resolved ? v8_resolver->Resolve(context, v8impl::V8LocalValueFromJsValue(result)) : v8_resolver->Reject(context, v8impl::V8LocalValueFromJsValue(result)); + + delete deferred_ref; + + RETURN_STATUS_IF_FALSE(env, success.FromMaybe(false), napi_generic_failure); + + return GET_RETURN_STATUS(env); + } + + class ThreadSafeFunction : public node::AsyncResource { + public: + ThreadSafeFunction(v8::Local func, + v8::Local resource, + v8::Local name, + v8::Local v8_context, + size_t thread_count_, + void* context_, + size_t max_queue_size_, + napi_env env_, + void* finalize_data_, + napi_finalize finalize_cb_, + napi_threadsafe_function_call_js call_js_cb_) + : AsyncResource(env_->isolate, resource, (*v8::String::Utf8Value(name)), -1) + , + + m_thread_count(thread_count_) + , m_is_closing(false) + , m_context(context_) + , m_max_queue_size(max_queue_size_) + , m_env(env_) + , m_finalize_data(finalize_data_) + , m_finalize_cb(finalize_cb_) + , m_call_js_cb(call_js_cb_ == nullptr ? CallJs : call_js_cb_) + , handles_closing(false) + { + m_refFunction.Reset(m_env->isolate, func); + m_v8_context.Reset(m_env->isolate, v8_context); + node::AddEnvironmentCleanupHook(m_env->isolate, Cleanup, this); + } + + ~ThreadSafeFunction() + { + m_v8_context.Reset(); + node::RemoveEnvironmentCleanupHook(m_env->isolate, Cleanup, this); + } - // These methods can be called from any thread. + // These methods can be called from any thread. - napi_status Push(void* data, napi_threadsafe_function_call_mode mode) - { - node::Mutex::ScopedLock lock(this->m_mutex); + napi_status Push(void* data, napi_threadsafe_function_call_mode mode) + { + node::Mutex::ScopedLock lock(this->m_mutex); - while (m_queue.size() >= m_max_queue_size && - m_max_queue_size > 0 && - !m_is_closing) { - if (mode == napi_tsfn_nonblocking) { - return napi_queue_full; + while (m_queue.size() >= m_max_queue_size && m_max_queue_size > 0 && !m_is_closing) { + if (mode == napi_tsfn_nonblocking) { + return napi_queue_full; + } + m_cond->Wait(lock); } - m_cond->Wait(lock); - } - if (m_is_closing) { - if (m_thread_count == 0) { - return napi_invalid_arg; + if (m_is_closing) { + if (m_thread_count == 0) { + return napi_invalid_arg; + } else { + m_thread_count--; + return napi_closing; + } } else { - m_thread_count--; - return napi_closing; - } - } else { - if (uv_async_send(&m_async) != 0) { - return napi_generic_failure; + if (uv_async_send(&m_async) != 0) { + return napi_generic_failure; + } + m_queue.push(data); + return napi_ok; } - m_queue.push(data); - return napi_ok; } - } - napi_status Acquire() - { - node::Mutex::ScopedLock lock(this->m_mutex); + napi_status Acquire() + { + node::Mutex::ScopedLock lock(this->m_mutex); - if (m_is_closing) { - return napi_closing; + if (m_is_closing) { + return napi_closing; + } + + m_thread_count++; + + return napi_ok; } - m_thread_count++; + napi_status Release(napi_threadsafe_function_release_mode mode) + { + node::Mutex::ScopedLock lock(this->m_mutex); + + if (m_thread_count == 0) { + return napi_invalid_arg; + } - return napi_ok; - } + m_thread_count--; - napi_status Release(napi_threadsafe_function_release_mode mode) - { - node::Mutex::ScopedLock lock(this->m_mutex); + if (m_thread_count == 0 || mode == napi_tsfn_abort) { + if (!m_is_closing) { + m_is_closing = (mode == napi_tsfn_abort); + if (m_is_closing && m_max_queue_size > 0) { + m_cond->Signal(lock); + } + if (uv_async_send(&m_async) != 0) { + return napi_generic_failure; + } + } + } - if (m_thread_count == 0) { - return napi_invalid_arg; + return napi_ok; } - m_thread_count--; - - if (m_thread_count == 0 || mode == napi_tsfn_abort) { - if (!m_is_closing) { - m_is_closing = (mode == napi_tsfn_abort); - if (m_is_closing && m_max_queue_size > 0) { - m_cond->Signal(lock); - } - if (uv_async_send(&m_async) != 0) { - return napi_generic_failure; - } + void EmptyQueueAndDelete() + { + for (; !m_queue.empty(); m_queue.pop()) { + m_call_js_cb(nullptr, nullptr, m_context, m_queue.front()); } + delete this; } - return napi_ok; - } + // These methods must only be called from the loop thread. - void EmptyQueueAndDelete() - { - for (; !m_queue.empty(); m_queue.pop()) { - m_call_js_cb(nullptr, nullptr, m_context, m_queue.front()); - } - delete this; - } + napi_status Init() + { + ThreadSafeFunction* ts_fn = this; - // These methods must only be called from the loop thread. + if (uv_async_init(m_env->loop, &m_async, AsyncCb) == 0) { + if (m_max_queue_size > 0) { + m_cond.reset(new node::ConditionVariable); + } + if ((m_max_queue_size == 0 || m_cond.get() != nullptr) && uv_idle_init(m_env->loop, &m_idle) == 0) { + return napi_ok; + } - napi_status Init() - { - ThreadSafeFunction* ts_fn = this; + NodeEnv()->CloseHandle(reinterpret_cast(&m_async), [](uv_handle_t* handle) -> void { + ThreadSafeFunction* ts_fn = node::ContainerOf(&ThreadSafeFunction::m_async, reinterpret_cast(handle)); + delete ts_fn; + }); - if (uv_async_init(m_env->loop, &m_async, AsyncCb) == 0) { - if (m_max_queue_size > 0) { - m_cond.reset(new node::ConditionVariable); - } - if ((m_max_queue_size == 0 || m_cond.get() != nullptr) && - uv_idle_init(m_env->loop, &m_idle) == 0) { - return napi_ok; + // Prevent the thread-safe function from being deleted here, because + // the callback above will delete it. + ts_fn = nullptr; } - NodeEnv()->CloseHandle(reinterpret_cast(&m_async), [](uv_handle_t* handle) -> void { - ThreadSafeFunction* ts_fn = node::ContainerOf(&ThreadSafeFunction::m_async, reinterpret_cast(handle)); - delete ts_fn; - }); + delete ts_fn; - // Prevent the thread-safe function from being deleted here, because - // the callback above will delete it. - ts_fn = nullptr; + return napi_generic_failure; } - delete ts_fn; - - return napi_generic_failure; - } - - napi_status Unref() - { - uv_unref(reinterpret_cast(&m_async)); - uv_unref(reinterpret_cast(&m_idle)); - - return napi_ok; - } + napi_status Unref() + { + uv_unref(reinterpret_cast(&m_async)); + uv_unref(reinterpret_cast(&m_idle)); - napi_status Ref() - { - uv_ref(reinterpret_cast(&m_async)); - uv_ref(reinterpret_cast(&m_idle)); + return napi_ok; + } - return napi_ok; - } + napi_status Ref() + { + uv_ref(reinterpret_cast(&m_async)); + uv_ref(reinterpret_cast(&m_idle)); - void DispatchOne() - { - void* data = nullptr; - bool popped_value = false; - bool idle_stop_failed = false; + return napi_ok; + } + void DispatchOne() { - node::Mutex::ScopedLock lock(this->m_mutex); - if (m_is_closing) { - CloseHandlesAndMaybeDelete(); - } else { - size_t size = m_queue.size(); - if (size > 0) { - data = m_queue.front(); - m_queue.pop(); - popped_value = true; - if (size == m_max_queue_size && m_max_queue_size > 0) { - m_cond->Signal(lock); - } - size--; - } - - if (size == 0) { - if (m_thread_count == 0) { - m_is_closing = true; - if (m_max_queue_size > 0) { + void* data = nullptr; + bool popped_value = false; + bool idle_stop_failed = false; + + { + node::Mutex::ScopedLock lock(this->m_mutex); + if (m_is_closing) { + CloseHandlesAndMaybeDelete(); + } else { + size_t size = m_queue.size(); + if (size > 0) { + data = m_queue.front(); + m_queue.pop(); + popped_value = true; + if (size == m_max_queue_size && m_max_queue_size > 0) { m_cond->Signal(lock); } - CloseHandlesAndMaybeDelete(); - } else { - if (uv_idle_stop(&m_idle) != 0) { - idle_stop_failed = true; + size--; + } + + if (size == 0) { + if (m_thread_count == 0) { + m_is_closing = true; + if (m_max_queue_size > 0) { + m_cond->Signal(lock); + } + CloseHandlesAndMaybeDelete(); + } else { + if (uv_idle_stop(&m_idle) != 0) { + idle_stop_failed = true; + } } } } } + + if (popped_value || idle_stop_failed) { + v8::HandleScope scope(m_env->isolate); + v8::Local v8_context = v8::Local::New(m_env->isolate, m_v8_context); + v8::Context::Scope context_scope(v8_context); + CallbackScope cb_scope(this); + + if (idle_stop_failed) { + NODE_CHECK(napi_throw_error(m_env, "ERR_NAPI_TSFN_STOP_IDLE_LOOP", "Failed to stop the idle loop") == napi_ok); + } else { + v8::Local js_cb = v8::Local::New(m_env->isolate, m_refFunction); + m_call_js_cb(m_env, v8impl::JsValueFromV8LocalValue(js_cb), m_context, data); + } + } } - if (popped_value || idle_stop_failed) { + node::Environment* NodeEnv() + { + // Grabbing the Node.js environment requires a handle scope because it + // looks up fields on the current context. v8::HandleScope scope(m_env->isolate); - v8::Local v8_context = v8::Local::New(m_env->isolate, m_v8_context); - v8::Context::Scope context_scope(v8_context); - CallbackScope cb_scope(this); + node::Environment* node_env = node::Environment::GetCurrent(m_env->isolate); + //NODE_CHECK_NOT_NULL(node_env); + return node_env; + } - if (idle_stop_failed) { - CHECK(napi_throw_error(m_env, "ERR_NAPI_TSFN_STOP_IDLE_LOOP", "Failed to stop the idle loop") == napi_ok); - } else { - v8::Local js_cb = v8::Local::New(m_env->isolate, m_refFunction); - m_call_js_cb(m_env, v8impl::JsValueFromV8LocalValue(js_cb), m_context, data); + void MaybeStartIdle() + { + if (uv_idle_start(&m_idle, IdleCb) != 0) { + v8::HandleScope scope(m_env->isolate); + CallbackScope cb_scope(this); + NODE_CHECK(napi_throw_error(m_env, + "ERR_NAPI_TSFN_START_IDLE_LOOP", + "Failed to start the idle loop") + == napi_ok); } } - } - - node::Environment* NodeEnv() - { - // Grabbing the Node.js environment requires a handle scope because it - // looks up fields on the current context. - v8::HandleScope scope(m_env->isolate); - node::Environment* node_env = node::Environment::GetCurrent(m_env->isolate); - //CHECK_NOT_NULL(node_env); - return node_env; - } - void MaybeStartIdle() - { - if (uv_idle_start(&m_idle, IdleCb) != 0) { + void Finalize() + { v8::HandleScope scope(m_env->isolate); - CallbackScope cb_scope(this); - CHECK(napi_throw_error(m_env, - "ERR_NAPI_TSFN_START_IDLE_LOOP", - "Failed to start the idle loop") == napi_ok); + if (m_finalize_cb) { + CallbackScope cb_scope(this); + m_finalize_cb(m_env, m_finalize_data, m_context); + } + EmptyQueueAndDelete(); } - } - void Finalize() - { - v8::HandleScope scope(m_env->isolate); - if (m_finalize_cb) { - CallbackScope cb_scope(this); - m_finalize_cb(m_env, m_finalize_data, m_context); + inline void* Context() + { + return m_context; } - EmptyQueueAndDelete(); - } - - inline void* Context() - { - return m_context; - } - void CloseHandlesAndMaybeDelete(bool set_closing = false) - { - if (set_closing) { - node::Mutex::ScopedLock lock(this->m_mutex); - m_is_closing = true; - if (m_max_queue_size > 0) { - m_cond->Signal(lock); + void CloseHandlesAndMaybeDelete(bool set_closing = false) + { + if (set_closing) { + node::Mutex::ScopedLock lock(this->m_mutex); + m_is_closing = true; + if (m_max_queue_size > 0) { + m_cond->Signal(lock); + } } - } - if (handles_closing) { - return; - } - handles_closing = true; - NodeEnv()->CloseHandle(reinterpret_cast(&m_async), [](uv_handle_t* handle) -> void { - ThreadSafeFunction* ts_fn = node::ContainerOf(&ThreadSafeFunction::m_async, reinterpret_cast(handle)); + if (handles_closing) { + return; + } + handles_closing = true; + NodeEnv()->CloseHandle(reinterpret_cast(&m_async), [](uv_handle_t* handle) -> void { + ThreadSafeFunction* ts_fn = node::ContainerOf(&ThreadSafeFunction::m_async, reinterpret_cast(handle)); - ts_fn->NodeEnv()->CloseHandle(reinterpret_cast(&ts_fn->m_idle), [](uv_handle_t* handle) -> void { - ThreadSafeFunction* ts_fn = node::ContainerOf(&ThreadSafeFunction::m_idle, reinterpret_cast(handle)); - ts_fn->Finalize(); + ts_fn->NodeEnv()->CloseHandle(reinterpret_cast(&ts_fn->m_idle), [](uv_handle_t* handle) -> void { + ThreadSafeFunction* ts_fn = node::ContainerOf(&ThreadSafeFunction::m_idle, reinterpret_cast(handle)); + ts_fn->Finalize(); + }); }); - }); - } - - // Default way of calling into JavaScript. Used when ThreadSafeFunction is - // without a call_js_cb_. - static void CallJs(napi_env env, napi_value cb, void* context, void* data) - { - if (!(env == nullptr || cb == nullptr)) { - napi_value recv; - napi_status status; + } - status = napi_get_undefined(env, &recv); - if (status != napi_ok) { - napi_throw_error(env, "ERR_NAPI_TSFN_GET_UNDEFINED", "Failed to retrieve undefined value"); - return; - } + // Default way of calling into JavaScript. Used when ThreadSafeFunction is + // without a call_js_cb_. + static void CallJs(napi_env env, napi_value cb, void* context, void* data) + { + if (!(env == nullptr || cb == nullptr)) { + napi_value recv; + napi_status status; + + status = napi_get_undefined(env, &recv); + if (status != napi_ok) { + napi_throw_error(env, "ERR_NAPI_TSFN_GET_UNDEFINED", "Failed to retrieve undefined value"); + return; + } - status = napi_call_function(env, recv, cb, 0, nullptr, nullptr); - if (status != napi_ok && status != napi_pending_exception) { - napi_throw_error(env, "ERR_NAPI_TSFN_CALL_JS", "Failed to call JS callback"); - return; + status = napi_call_function(env, recv, cb, 0, nullptr, nullptr); + if (status != napi_ok && status != napi_pending_exception) { + napi_throw_error(env, "ERR_NAPI_TSFN_CALL_JS", "Failed to call JS callback"); + return; + } } } - } - static void IdleCb(uv_idle_t* idle) - { - ThreadSafeFunction* ts_fn = node::ContainerOf(&ThreadSafeFunction::m_idle, idle); - ts_fn->DispatchOne(); - } + static void IdleCb(uv_idle_t* idle) + { + ThreadSafeFunction* ts_fn = node::ContainerOf(&ThreadSafeFunction::m_idle, idle); + ts_fn->DispatchOne(); + } - static void AsyncCb(uv_async_t* async) - { - ThreadSafeFunction* ts_fn = node::ContainerOf(&ThreadSafeFunction::m_async, async); - ts_fn->MaybeStartIdle(); - } + static void AsyncCb(uv_async_t* async) + { + ThreadSafeFunction* ts_fn = node::ContainerOf(&ThreadSafeFunction::m_async, async); + ts_fn->MaybeStartIdle(); + } - static void Cleanup(void* data) - { - reinterpret_cast(data)->CloseHandlesAndMaybeDelete(true); - } + static void Cleanup(void* data) + { + reinterpret_cast(data)->CloseHandlesAndMaybeDelete(true); + } -private: - // These are variables protected by the mutex. - node::Mutex m_mutex; - std::unique_ptr m_cond; - std::queue m_queue; - uv_async_t m_async; - uv_idle_t m_idle; - size_t m_thread_count; - bool m_is_closing; - - // These are variables set once, upon creation, and then never again, which - // means we don't need the mutex to read them. - void* m_context; - size_t m_max_queue_size; - - // These are variables accessed only from the loop thread. - node::Persistent m_refFunction; - node::Persistent m_v8_context; - napi_env m_env; - void* m_finalize_data; - napi_finalize m_finalize_cb; - napi_threadsafe_function_call_js m_call_js_cb; - bool handles_closing; -}; + private: + // These are variables protected by the mutex. + node::Mutex m_mutex; + std::unique_ptr m_cond; + std::queue m_queue; + uv_async_t m_async; + uv_idle_t m_idle; + size_t m_thread_count; + bool m_is_closing; + + // These are variables set once, upon creation, and then never again, which + // means we don't need the mutex to read them. + void* m_context; + size_t m_max_queue_size; + + // These are variables accessed only from the loop thread. + node::Persistent m_refFunction; + node::Persistent m_v8_context; + napi_env m_env; + void* m_finalize_data; + napi_finalize m_finalize_cb; + napi_threadsafe_function_call_js m_call_js_cb; + bool handles_closing; + }; -enum WrapType { - retrievable, - anonymous -}; + enum WrapType { + retrievable, + anonymous + }; -template static inline -napi_status Wrap(napi_env env, - napi_value js_object, - void* native_object, - napi_finalize finalize_cb, - void* finalize_hint, - napi_ref* result) -{ - NAPI_PREAMBLE(env); - CHECK_ARG(env, js_object); + template + static inline napi_status Wrap(napi_env env, + napi_value js_object, + void* native_object, + napi_finalize finalize_cb, + void* finalize_hint, + napi_ref* result) + { + NAPI_PREAMBLE(env); + NODE_CHECK_ARG(env, js_object); - v8::Isolate* isolate = env->isolate; - v8::Local context = isolate->GetCurrentContext(); + v8::Isolate* isolate = env->isolate; + v8::Local context = isolate->GetCurrentContext(); - v8::Local value = v8impl::V8LocalValueFromJsValue(js_object); - RETURN_STATUS_IF_FALSE(env, value->IsObject(), napi_invalid_arg); - v8::Local obj = value.As(); + v8::Local value = v8impl::V8LocalValueFromJsValue(js_object); + RETURN_STATUS_IF_FALSE(env, value->IsObject(), napi_invalid_arg); + v8::Local obj = value.As(); + + if (wrap_type == retrievable) { + // If we've already wrapped this object, we error out. + RETURN_STATUS_IF_FALSE(env, + !obj->HasPrivate(context, NAPI_PRIVATE_KEY(context, wrapper)) + .FromJust(), + napi_invalid_arg); + } else if (wrap_type == anonymous) { + // If no finalize callback is provided, we error out. + NODE_CHECK_ARG(env, finalize_cb); + } - if (wrap_type == retrievable) { - // If we've already wrapped this object, we error out. - RETURN_STATUS_IF_FALSE(env, - !obj->HasPrivate(context, NAPI_PRIVATE_KEY(context, wrapper)) - .FromJust(), - napi_invalid_arg); - } else if (wrap_type == anonymous) { - // If no finalize callback is provided, we error out. - CHECK_ARG(env, finalize_cb); - } + v8impl::Reference* reference = nullptr; + if (result != nullptr) { + // The returned reference should be deleted via napi_delete_reference() + // ONLY in response to the finalize callback invocation. (If it is deleted + // before then, then the finalize callback will never be invoked.) + // Therefore a finalize callback is required when returning a reference. + NODE_CHECK_ARG(env, finalize_cb); + reference = v8impl::Reference::New( + env, obj, 0, false, finalize_cb, native_object, finalize_hint); + *result = reinterpret_cast(reference); + } else { + // Create a self-deleting reference. + reference = v8impl::Reference::New(env, obj, 0, true, finalize_cb, + native_object, finalize_cb == nullptr ? nullptr : finalize_hint); + } - v8impl::Reference* reference = nullptr; - if (result != nullptr) { - // The returned reference should be deleted via napi_delete_reference() - // ONLY in response to the finalize callback invocation. (If it is deleted - // before then, then the finalize callback will never be invoked.) - // Therefore a finalize callback is required when returning a reference. - CHECK_ARG(env, finalize_cb); - reference = v8impl::Reference::New( - env, obj, 0, false, finalize_cb, native_object, finalize_hint); - *result = reinterpret_cast(reference); - } else { - // Create a self-deleting reference. - reference = v8impl::Reference::New(env, obj, 0, true, finalize_cb, - native_object, finalize_cb == nullptr ? nullptr : finalize_hint); - } + if (wrap_type == retrievable) { + NODE_CHECK(obj->SetPrivate(context, NAPI_PRIVATE_KEY(context, wrapper), + v8::External::New(isolate, reference)) + .FromJust()); + } - if (wrap_type == retrievable) { - CHECK(obj->SetPrivate(context, NAPI_PRIVATE_KEY(context, wrapper), - v8::External::New(isolate, reference)).FromJust()); + return GET_RETURN_STATUS(env); } - return GET_RETURN_STATUS(env); -} - -} // end of namespace v8impl +} // end of namespace v8impl - // Intercepts the Node-V8 module registration callback. Converts parameters - // to NAPI equivalents and then calls the registration callback specified - // by the NAPI module. +// Intercepts the Node-V8 module registration callback. Converts parameters +// to NAPI equivalents and then calls the registration callback specified +// by the NAPI module. void napi_module_register_cb(v8::Local exports, v8::Local module, v8::Local context, @@ -1684,8 +1713,9 @@ void napi_module_register_cb(v8::Local exports, napi_module_register_by_symbol(exports, module, context, static_cast(priv)->nm_register_func); } -} // end of anonymous namespace +} // end of anonymous namespace +// https://github.com/nodejs/node/commit/5030e81ce305cc6bc5a1e17dd0ef8a9c761f035b void napi_module_register_by_symbol(v8::Local exports, v8::Local module, v8::Local context, @@ -1693,14 +1723,14 @@ void napi_module_register_by_symbol(v8::Local exports, { if (init == nullptr) { node::Environment* node_env = node::Environment::GetCurrent(context); - //CHECK_NOT_NULL(node_env); + //NODE_CHECK_NOT_NULL(node_env); node_env->ThrowError("Module has no declared entry point."); return; } // Create a new napi_env for this module or reference one if a pre-existing // one is found. - napi_env env = v8impl::GetEnv(context); + napi_env env = v8impl::NewEnv(context); napi_value _exports; NAPI_CALL_INTO_MODULE_THROW(env, @@ -1709,8 +1739,7 @@ void napi_module_register_by_symbol(v8::Local exports, // If register function returned a non-null exports object different from // the exports object we passed it, set that as the "exports" property of // the module. - if (_exports != nullptr && - _exports != v8impl::JsValueFromV8LocalValue(exports)) { + if (_exports != nullptr && _exports != v8impl::JsValueFromV8LocalValue(exports)) { napi_value _module = v8impl::JsValueFromV8LocalValue(module); napi_set_named_property(env, _module, "exports", _exports); } @@ -1719,7 +1748,7 @@ void napi_module_register_by_symbol(v8::Local exports, // Registers a NAPI module. void napi_module_register(napi_module* mod) { - node::node_module* nm = new node::node_module{ + node::node_module* nm = new node::node_module { -1, mod->nm_flags, nullptr, @@ -1727,54 +1756,54 @@ void napi_module_register(napi_module* mod) nullptr, napi_module_register_cb, mod->nm_modname, - mod, // priv + mod, // priv nullptr, }; node::node_module_register(nm); } napi_status napi_add_env_cleanup_hook(napi_env env, - void(*fun)(void* arg), + void (*fun)(void* arg), void* arg) { - CHECK_ENV(env); - CHECK_ARG(env, fun); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, fun); node::AddEnvironmentCleanupHook(env->isolate, fun, arg); return napi_ok; } napi_status napi_remove_env_cleanup_hook(napi_env env, - void(*fun)(void* arg), + void (*fun)(void* arg), void* arg) { - CHECK_ENV(env); - CHECK_ARG(env, fun); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, fun); node::RemoveEnvironmentCleanupHook(env->isolate, fun, arg); return napi_ok; } // Warning: Keep in-sync with napi_status enum -static -const char* error_messages[] = { nullptr, -"Invalid argument", -"An object was expected", -"A string was expected", -"A string or symbol was expected", -"A function was expected", -"A number was expected", -"A boolean was expected", -"An array was expected", -"Unknown failure", -"An exception is pending", -"The async work item was cancelled", -"napi_escape_handle already called on scope", -"Invalid handle scope usage", -"Invalid callback scope usage", -"Thread-safe function queue is full", -"Thread-safe function handle is closing", -"A bigint was expected", +static const char* error_messages[] = { + nullptr, + "Invalid argument", + "An object was expected", + "A string was expected", + "A string or symbol was expected", + "A function was expected", + "A number was expected", + "A boolean was expected", + "An array was expected", + "Unknown failure", + "An exception is pending", + "The async work item was cancelled", + "napi_escape_handle already called on scope", + "Invalid handle scope usage", + "Invalid callback scope usage", + "Thread-safe function queue is full", + "Thread-safe function handle is closing", + "A bigint was expected", }; static inline napi_status napi_clear_last_error(napi_env env) @@ -1787,8 +1816,7 @@ static inline napi_status napi_clear_last_error(napi_env env) return napi_ok; } -static inline -napi_status napi_set_last_error(napi_env env, napi_status error_code, +static inline napi_status napi_set_last_error(napi_env env, napi_status error_code, uint32_t engine_error_code, void* engine_reserved) { @@ -1801,21 +1829,20 @@ napi_status napi_set_last_error(napi_env env, napi_status error_code, napi_status napi_get_last_error_info(napi_env env, const napi_extended_error_info** result) { - CHECK_ENV(env); - CHECK_ARG(env, result); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, result); - // you must update this assert to reference the last message + // you must update this NODE_ASSERT to reference the last message // in the napi_status enum each time a new error message is added. // We don't have a napi_status_last as this would result in an ABI // change each time a message was added. DebugBreak(); //static_assert(node::arraysize(error_messages) == napi_bigint_expected + 1, "Count of error messages must match count of error values"); - CHECK_LE(env->last_error.error_code, napi_callback_scope_mismatch); + NODE_CHECK_LE(env->last_error.error_code, napi_callback_scope_mismatch); // Wait until someone requests the last error information to fetch the error // message string - env->last_error.error_message = - error_messages[env->last_error.error_code]; + env->last_error.error_message = error_messages[env->last_error.error_code]; *result = &(env->last_error); return napi_ok; @@ -1824,7 +1851,7 @@ napi_status napi_get_last_error_info(napi_env env, napi_status napi_fatal_exception(napi_env env, napi_value err) { NAPI_PREAMBLE(env); - CHECK_ARG(env, err); + NODE_CHECK_ARG(env, err); v8::Local local_err = v8impl::V8LocalValueFromJsValue(err); v8impl::trigger_fatal_exception(env, local_err); @@ -1867,29 +1894,27 @@ napi_status napi_create_function(napi_env env, napi_value* result) { NAPI_PREAMBLE(env); - CHECK_ARG(env, result); - CHECK_ARG(env, cb); + NODE_CHECK_ARG(env, result); + NODE_CHECK_ARG(env, cb); v8::Isolate* isolate = env->isolate; v8::Local return_value; v8::EscapableHandleScope scope(isolate); - v8::Local cbdata = - v8impl::CreateFunctionCallbackData(env, cb, callback_data); + v8::Local cbdata = v8impl::CreateFunctionCallbackData(env, cb, callback_data); RETURN_STATUS_IF_FALSE(env, !cbdata.IsEmpty(), napi_generic_failure); v8::Local context = isolate->GetCurrentContext(); - v8::MaybeLocal maybe_function = - v8::Function::New(context, - v8impl::FunctionCallbackWrapper::Invoke, - cbdata); - CHECK_MAYBE_EMPTY(env, maybe_function, napi_generic_failure); + v8::MaybeLocal maybe_function = v8::Function::New(context, + v8impl::FunctionCallbackWrapper::Invoke, + cbdata); + NODE_CHECK_MAYBE_EMPTY(env, maybe_function, napi_generic_failure); return_value = scope.Escape(maybe_function.ToLocalChecked()); if (utf8name != nullptr) { v8::Local name_string; - CHECK_NEW_FROM_UTF8_LEN(env, name_string, utf8name, length); + NODE_CHECK_NEW_FROM_UTF8_LEN(env, name_string, utf8name, length); return_value->SetName(name_string); } @@ -1908,14 +1933,13 @@ napi_status napi_define_class(napi_env env, napi_value* result) { NAPI_PREAMBLE(env); - CHECK_ARG(env, result); - CHECK_ARG(env, constructor); + NODE_CHECK_ARG(env, result); + NODE_CHECK_ARG(env, constructor); v8::Isolate* isolate = env->isolate; v8::EscapableHandleScope scope(isolate); - v8::Local cbdata = - v8impl::CreateFunctionCallbackData(env, constructor, callback_data); + v8::Local cbdata = v8impl::CreateFunctionCallbackData(env, constructor, callback_data); RETURN_STATUS_IF_FALSE(env, !cbdata.IsEmpty(), napi_generic_failure); @@ -1923,7 +1947,7 @@ napi_status napi_define_class(napi_env env, isolate, v8impl::FunctionCallbackWrapper::Invoke, cbdata); v8::Local name_string; - CHECK_NEW_FROM_UTF8_LEN(env, name_string, utf8name, length); + NODE_CHECK_NEW_FROM_UTF8_LEN(env, name_string, utf8name, length); tpl->SetClassName(name_string); size_t static_property_count = 0; @@ -1937,15 +1961,13 @@ napi_status napi_define_class(napi_env env, } v8::Local property_name; - napi_status status = - v8impl::V8NameFromPropertyDescriptor(env, p, &property_name); + napi_status status = v8impl::V8NameFromPropertyDescriptor(env, p, &property_name); if (status != napi_ok) { return napi_set_last_error(env, status); } - v8::PropertyAttribute attributes = - v8impl::V8PropertyAttributesFromDescriptor(p); + v8::PropertyAttribute attributes = v8impl::V8PropertyAttributesFromDescriptor(p); // This code is similar to that in napi_define_properties(); the // difference is it applies to a template instead of an object. @@ -1961,16 +1983,14 @@ napi_status napi_define_class(napi_env env, v8::AccessControl::DEFAULT, attributes); } else if (p->method != nullptr) { - v8::Local cbdata = - v8impl::CreateFunctionCallbackData(env, p->method, p->data); + v8::Local cbdata = v8impl::CreateFunctionCallbackData(env, p->method, p->data); RETURN_STATUS_IF_FALSE(env, !cbdata.IsEmpty(), napi_generic_failure); - v8::Local t = - v8::FunctionTemplate::New(isolate, - v8impl::FunctionCallbackWrapper::Invoke, - cbdata, - v8::Signature::New(isolate, tpl)); + v8::Local t = v8::FunctionTemplate::New(isolate, + v8impl::FunctionCallbackWrapper::Invoke, + cbdata, + v8::Signature::New(isolate, tpl)); tpl->PrototypeTemplate()->Set(property_name, t, attributes); } else { @@ -1994,12 +2014,12 @@ napi_status napi_define_class(napi_env env, } } - napi_status status = - napi_define_properties(env, - *result, - static_descriptors.size(), - static_descriptors.data()); - if (status != napi_ok) return status; + napi_status status = napi_define_properties(env, + *result, + static_descriptors.size(), + static_descriptors.data()); + if (status != napi_ok) + return status; } return GET_RETURN_STATUS(env); @@ -2010,16 +2030,16 @@ napi_status napi_get_property_names(napi_env env, napi_value* result) { NAPI_PREAMBLE(env); - CHECK_ARG(env, result); + NODE_CHECK_ARG(env, result); v8::Isolate* isolate = env->isolate; v8::Local context = isolate->GetCurrentContext(); v8::Local obj; - CHECK_TO_OBJECT(env, context, obj, object); + NODE_CHECK_TO_OBJECT(env, context, obj, object); auto maybe_propertynames = obj->GetPropertyNames(context); - CHECK_MAYBE_EMPTY(env, maybe_propertynames, napi_generic_failure); + NODE_CHECK_MAYBE_EMPTY(env, maybe_propertynames, napi_generic_failure); *result = v8impl::JsValueFromV8LocalValue( maybe_propertynames.ToLocalChecked()); @@ -2032,14 +2052,14 @@ napi_status napi_set_property(napi_env env, napi_value value) { NAPI_PREAMBLE(env); - CHECK_ARG(env, key); - CHECK_ARG(env, value); + NODE_CHECK_ARG(env, key); + NODE_CHECK_ARG(env, value); v8::Isolate* isolate = env->isolate; v8::Local context = isolate->GetCurrentContext(); v8::Local obj; - CHECK_TO_OBJECT(env, context, obj, object); + NODE_CHECK_TO_OBJECT(env, context, obj, object); v8::Local k = v8impl::V8LocalValueFromJsValue(key); v8::Local val = v8impl::V8LocalValueFromJsValue(value); @@ -2056,19 +2076,19 @@ napi_status napi_has_property(napi_env env, bool* result) { NAPI_PREAMBLE(env); - CHECK_ARG(env, result); - CHECK_ARG(env, key); + NODE_CHECK_ARG(env, result); + NODE_CHECK_ARG(env, key); v8::Isolate* isolate = env->isolate; v8::Local context = isolate->GetCurrentContext(); v8::Local obj; - CHECK_TO_OBJECT(env, context, obj, object); + NODE_CHECK_TO_OBJECT(env, context, obj, object); v8::Local k = v8impl::V8LocalValueFromJsValue(key); v8::Maybe has_maybe = obj->Has(context, k); - CHECK_MAYBE_NOTHING(env, has_maybe, napi_generic_failure); + NODE_CHECK_MAYBE_NOTHING(env, has_maybe, napi_generic_failure); *result = has_maybe.FromMaybe(false); return GET_RETURN_STATUS(env); @@ -2080,19 +2100,19 @@ napi_status napi_get_property(napi_env env, napi_value* result) { NAPI_PREAMBLE(env); - CHECK_ARG(env, key); - CHECK_ARG(env, result); + NODE_CHECK_ARG(env, key); + NODE_CHECK_ARG(env, result); v8::Isolate* isolate = env->isolate; v8::Local context = isolate->GetCurrentContext(); v8::Local k = v8impl::V8LocalValueFromJsValue(key); v8::Local obj; - CHECK_TO_OBJECT(env, context, obj, object); + NODE_CHECK_TO_OBJECT(env, context, obj, object); auto get_maybe = obj->Get(context, k); - CHECK_MAYBE_EMPTY(env, get_maybe, napi_generic_failure); + NODE_CHECK_MAYBE_EMPTY(env, get_maybe, napi_generic_failure); v8::Local val = get_maybe.ToLocalChecked(); *result = v8impl::JsValueFromV8LocalValue(val); @@ -2105,16 +2125,16 @@ napi_status napi_delete_property(napi_env env, bool* result) { NAPI_PREAMBLE(env); - CHECK_ARG(env, key); + NODE_CHECK_ARG(env, key); v8::Isolate* isolate = env->isolate; v8::Local context = isolate->GetCurrentContext(); v8::Local k = v8impl::V8LocalValueFromJsValue(key); v8::Local obj; - CHECK_TO_OBJECT(env, context, obj, object); + NODE_CHECK_TO_OBJECT(env, context, obj, object); v8::Maybe delete_maybe = obj->Delete(context, k); - CHECK_MAYBE_NOTHING(env, delete_maybe, napi_generic_failure); + NODE_CHECK_MAYBE_NOTHING(env, delete_maybe, napi_generic_failure); if (result != nullptr) *result = delete_maybe.FromMaybe(false); @@ -2128,17 +2148,17 @@ napi_status napi_has_own_property(napi_env env, bool* result) { NAPI_PREAMBLE(env); - CHECK_ARG(env, key); + NODE_CHECK_ARG(env, key); v8::Isolate* isolate = env->isolate; v8::Local context = isolate->GetCurrentContext(); v8::Local obj; - CHECK_TO_OBJECT(env, context, obj, object); + NODE_CHECK_TO_OBJECT(env, context, obj, object); v8::Local k = v8impl::V8LocalValueFromJsValue(key); RETURN_STATUS_IF_FALSE(env, k->IsName(), napi_name_expected); v8::Maybe has_maybe = obj->HasOwnProperty(context, k.As()); - CHECK_MAYBE_NOTHING(env, has_maybe, napi_generic_failure); + NODE_CHECK_MAYBE_NOTHING(env, has_maybe, napi_generic_failure); *result = has_maybe.FromMaybe(false); return GET_RETURN_STATUS(env); @@ -2150,16 +2170,16 @@ napi_status napi_set_named_property(napi_env env, napi_value value) { NAPI_PREAMBLE(env); - CHECK_ARG(env, value); + NODE_CHECK_ARG(env, value); v8::Isolate* isolate = env->isolate; v8::Local context = isolate->GetCurrentContext(); v8::Local obj; - CHECK_TO_OBJECT(env, context, obj, object); + NODE_CHECK_TO_OBJECT(env, context, obj, object); v8::Local key; - CHECK_NEW_FROM_UTF8(env, key, utf8name); + NODE_CHECK_NEW_FROM_UTF8(env, key, utf8name); v8::Local val = v8impl::V8LocalValueFromJsValue(value); @@ -2175,20 +2195,20 @@ napi_status napi_has_named_property(napi_env env, bool* result) { NAPI_PREAMBLE(env); - CHECK_ARG(env, result); + NODE_CHECK_ARG(env, result); v8::Isolate* isolate = env->isolate; v8::Local context = isolate->GetCurrentContext(); v8::Local obj; - CHECK_TO_OBJECT(env, context, obj, object); + NODE_CHECK_TO_OBJECT(env, context, obj, object); v8::Local key; - CHECK_NEW_FROM_UTF8(env, key, utf8name); + NODE_CHECK_NEW_FROM_UTF8(env, key, utf8name); v8::Maybe has_maybe = obj->Has(context, key); - CHECK_MAYBE_NOTHING(env, has_maybe, napi_generic_failure); + NODE_CHECK_MAYBE_NOTHING(env, has_maybe, napi_generic_failure); *result = has_maybe.FromMaybe(false); return GET_RETURN_STATUS(env); @@ -2200,21 +2220,21 @@ napi_status napi_get_named_property(napi_env env, napi_value* result) { NAPI_PREAMBLE(env); - CHECK_ARG(env, result); + NODE_CHECK_ARG(env, result); v8::Isolate* isolate = env->isolate; v8::Local context = isolate->GetCurrentContext(); v8::Local key; - CHECK_NEW_FROM_UTF8(env, key, utf8name); + NODE_CHECK_NEW_FROM_UTF8(env, key, utf8name); v8::Local obj; - CHECK_TO_OBJECT(env, context, obj, object); + NODE_CHECK_TO_OBJECT(env, context, obj, object); auto get_maybe = obj->Get(context, key); - CHECK_MAYBE_EMPTY(env, get_maybe, napi_generic_failure); + NODE_CHECK_MAYBE_EMPTY(env, get_maybe, napi_generic_failure); v8::Local val = get_maybe.ToLocalChecked(); *result = v8impl::JsValueFromV8LocalValue(val); @@ -2227,13 +2247,13 @@ napi_status napi_set_element(napi_env env, napi_value value) { NAPI_PREAMBLE(env); - CHECK_ARG(env, value); + NODE_CHECK_ARG(env, value); v8::Isolate* isolate = env->isolate; v8::Local context = isolate->GetCurrentContext(); v8::Local obj; - CHECK_TO_OBJECT(env, context, obj, object); + NODE_CHECK_TO_OBJECT(env, context, obj, object); v8::Local val = v8impl::V8LocalValueFromJsValue(value); auto set_maybe = obj->Set(context, index, val); @@ -2249,17 +2269,17 @@ napi_status napi_has_element(napi_env env, bool* result) { NAPI_PREAMBLE(env); - CHECK_ARG(env, result); + NODE_CHECK_ARG(env, result); v8::Isolate* isolate = env->isolate; v8::Local context = isolate->GetCurrentContext(); v8::Local obj; - CHECK_TO_OBJECT(env, context, obj, object); + NODE_CHECK_TO_OBJECT(env, context, obj, object); v8::Maybe has_maybe = obj->Has(context, index); - CHECK_MAYBE_NOTHING(env, has_maybe, napi_generic_failure); + NODE_CHECK_MAYBE_NOTHING(env, has_maybe, napi_generic_failure); *result = has_maybe.FromMaybe(false); return GET_RETURN_STATUS(env); @@ -2271,17 +2291,17 @@ napi_status napi_get_element(napi_env env, napi_value* result) { NAPI_PREAMBLE(env); - CHECK_ARG(env, result); + NODE_CHECK_ARG(env, result); v8::Isolate* isolate = env->isolate; v8::Local context = isolate->GetCurrentContext(); v8::Local obj; - CHECK_TO_OBJECT(env, context, obj, object); + NODE_CHECK_TO_OBJECT(env, context, obj, object); auto get_maybe = obj->Get(context, index); - CHECK_MAYBE_EMPTY(env, get_maybe, napi_generic_failure); + NODE_CHECK_MAYBE_EMPTY(env, get_maybe, napi_generic_failure); *result = v8impl::JsValueFromV8LocalValue(get_maybe.ToLocalChecked()); return GET_RETURN_STATUS(env); @@ -2298,9 +2318,9 @@ napi_status napi_delete_element(napi_env env, v8::Local context = isolate->GetCurrentContext(); v8::Local obj; - CHECK_TO_OBJECT(env, context, obj, object); + NODE_CHECK_TO_OBJECT(env, context, obj, object); v8::Maybe delete_maybe = obj->Delete(context, index); - CHECK_MAYBE_NOTHING(env, delete_maybe, napi_generic_failure); + NODE_CHECK_MAYBE_NOTHING(env, delete_maybe, napi_generic_failure); if (result != nullptr) *result = delete_maybe.FromMaybe(false); @@ -2315,28 +2335,26 @@ napi_status napi_define_properties(napi_env env, { NAPI_PREAMBLE(env); if (property_count > 0) { - CHECK_ARG(env, properties); + NODE_CHECK_ARG(env, properties); } v8::Isolate* isolate = env->isolate; v8::Local context = isolate->GetCurrentContext(); v8::Local obj; - CHECK_TO_OBJECT(env, context, obj, object); + NODE_CHECK_TO_OBJECT(env, context, obj, object); for (size_t i = 0; i < property_count; i++) { const napi_property_descriptor* p = &properties[i]; v8::Local property_name; - napi_status status = - v8impl::V8NameFromPropertyDescriptor(env, p, &property_name); + napi_status status = v8impl::V8NameFromPropertyDescriptor(env, p, &property_name); if (status != napi_ok) { return napi_set_last_error(env, status); } - v8::PropertyAttribute attributes = - v8impl::V8PropertyAttributesFromDescriptor(p); + v8::PropertyAttribute attributes = v8impl::V8PropertyAttributesFromDescriptor(p); if (p->getter != nullptr || p->setter != nullptr) { v8::Local cbdata = v8impl::CreateAccessorCallbackData( @@ -2358,17 +2376,15 @@ napi_status napi_define_properties(napi_env env, return napi_set_last_error(env, napi_invalid_arg); } } else if (p->method != nullptr) { - v8::Local cbdata = - v8impl::CreateFunctionCallbackData(env, p->method, p->data); + v8::Local cbdata = v8impl::CreateFunctionCallbackData(env, p->method, p->data); - CHECK_MAYBE_EMPTY(env, cbdata, napi_generic_failure); + NODE_CHECK_MAYBE_EMPTY(env, cbdata, napi_generic_failure); - v8::MaybeLocal maybe_fn = - v8::Function::New(context, - v8impl::FunctionCallbackWrapper::Invoke, - cbdata); + v8::MaybeLocal maybe_fn = v8::Function::New(context, + v8impl::FunctionCallbackWrapper::Invoke, + cbdata); - CHECK_MAYBE_EMPTY(env, maybe_fn, napi_generic_failure); + NODE_CHECK_MAYBE_EMPTY(env, maybe_fn, napi_generic_failure); auto define_maybe = obj->DefineOwnProperty( context, property_name, maybe_fn.ToLocalChecked(), attributes); @@ -2379,8 +2395,7 @@ napi_status napi_define_properties(napi_env env, } else { v8::Local value = v8impl::V8LocalValueFromJsValue(p->value); - auto define_maybe = - obj->DefineOwnProperty(context, property_name, value, attributes); + auto define_maybe = obj->DefineOwnProperty(context, property_name, value, attributes); if (!define_maybe.FromMaybe(false)) { return napi_set_last_error(env, napi_invalid_arg); @@ -2393,9 +2408,9 @@ napi_status napi_define_properties(napi_env env, napi_status napi_is_array(napi_env env, napi_value value, bool* result) { - CHECK_ENV(env); - CHECK_ARG(env, value); - CHECK_ARG(env, result); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, value); + NODE_CHECK_ARG(env, result); v8::Local val = v8impl::V8LocalValueFromJsValue(value); @@ -2408,8 +2423,8 @@ napi_status napi_get_array_length(napi_env env, uint32_t* result) { NAPI_PREAMBLE(env); - CHECK_ARG(env, value); - CHECK_ARG(env, result); + NODE_CHECK_ARG(env, value); + NODE_CHECK_ARG(env, result); v8::Local val = v8impl::V8LocalValueFromJsValue(value); RETURN_STATUS_IF_FALSE(env, val->IsArray(), napi_array_expected); @@ -2426,9 +2441,9 @@ napi_status napi_strict_equals(napi_env env, bool* result) { NAPI_PREAMBLE(env); - CHECK_ARG(env, lhs); - CHECK_ARG(env, rhs); - CHECK_ARG(env, result); + NODE_CHECK_ARG(env, lhs); + NODE_CHECK_ARG(env, rhs); + NODE_CHECK_ARG(env, result); v8::Local a = v8impl::V8LocalValueFromJsValue(lhs); v8::Local b = v8impl::V8LocalValueFromJsValue(rhs); @@ -2442,13 +2457,13 @@ napi_status napi_get_prototype(napi_env env, napi_value* result) { NAPI_PREAMBLE(env); - CHECK_ARG(env, result); + NODE_CHECK_ARG(env, result); v8::Isolate* isolate = env->isolate; v8::Local context = isolate->GetCurrentContext(); v8::Local obj; - CHECK_TO_OBJECT(env, context, obj, object); + NODE_CHECK_TO_OBJECT(env, context, obj, object); v8::Local val = obj->GetPrototype(); *result = v8impl::JsValueFromV8LocalValue(val); @@ -2457,8 +2472,8 @@ napi_status napi_get_prototype(napi_env env, napi_status napi_create_object(napi_env env, napi_value* result) { - CHECK_ENV(env); - CHECK_ARG(env, result); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, result); *result = v8impl::JsValueFromV8LocalValue( v8::Object::New(env->isolate)); @@ -2468,8 +2483,8 @@ napi_status napi_create_object(napi_env env, napi_value* result) napi_status napi_create_array(napi_env env, napi_value* result) { - CHECK_ENV(env); - CHECK_ARG(env, result); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, result); *result = v8impl::JsValueFromV8LocalValue( v8::Array::New(env->isolate)); @@ -2481,8 +2496,8 @@ napi_status napi_create_array_with_length(napi_env env, size_t length, napi_value* result) { - CHECK_ENV(env); - CHECK_ARG(env, result); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, result); *result = v8impl::JsValueFromV8LocalValue( v8::Array::New(env->isolate, length)); @@ -2495,16 +2510,15 @@ napi_status napi_create_string_latin1(napi_env env, size_t length, napi_value* result) { - CHECK_ENV(env); - CHECK_ARG(env, result); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, result); auto isolate = env->isolate; - auto str_maybe = - v8::String::NewFromOneByte(isolate, - reinterpret_cast(str), - v8::NewStringType::kInternalized, - length); - CHECK_MAYBE_EMPTY(env, str_maybe, napi_generic_failure); + auto str_maybe = v8::String::NewFromOneByte(isolate, + reinterpret_cast(str), + v8::NewStringType::kInternalized, + length); + NODE_CHECK_MAYBE_EMPTY(env, str_maybe, napi_generic_failure); *result = v8impl::JsValueFromV8LocalValue(str_maybe.ToLocalChecked()); return napi_clear_last_error(env); @@ -2515,11 +2529,11 @@ napi_status napi_create_string_utf8(napi_env env, size_t length, napi_value* result) { - CHECK_ENV(env); - CHECK_ARG(env, result); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, result); v8::Local s; - CHECK_NEW_FROM_UTF8_LEN(env, s, str, length); + NODE_CHECK_NEW_FROM_UTF8_LEN(env, s, str, length); *result = v8impl::JsValueFromV8LocalValue(s); return napi_clear_last_error(env); @@ -2530,16 +2544,15 @@ napi_status napi_create_string_utf16(napi_env env, size_t length, napi_value* result) { - CHECK_ENV(env); - CHECK_ARG(env, result); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, result); auto isolate = env->isolate; - auto str_maybe = - v8::String::NewFromTwoByte(isolate, - reinterpret_cast(str), - v8::NewStringType::kInternalized, - length); - CHECK_MAYBE_EMPTY(env, str_maybe, napi_generic_failure); + auto str_maybe = v8::String::NewFromTwoByte(isolate, + reinterpret_cast(str), + v8::NewStringType::kInternalized, + length); + NODE_CHECK_MAYBE_EMPTY(env, str_maybe, napi_generic_failure); *result = v8impl::JsValueFromV8LocalValue(str_maybe.ToLocalChecked()); return napi_clear_last_error(env); @@ -2549,8 +2562,8 @@ napi_status napi_create_double(napi_env env, double value, napi_value* result) { - CHECK_ENV(env); - CHECK_ARG(env, result); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, result); *result = v8impl::JsValueFromV8LocalValue( v8::Number::New(env->isolate, value)); @@ -2562,8 +2575,8 @@ napi_status napi_create_int32(napi_env env, int32_t value, napi_value* result) { - CHECK_ENV(env); - CHECK_ARG(env, result); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, result); *result = v8impl::JsValueFromV8LocalValue(v8::Integer::New(env->isolate, value)); @@ -2574,8 +2587,8 @@ napi_status napi_create_uint32(napi_env env, uint32_t value, napi_value* result) { - CHECK_ENV(env); - CHECK_ARG(env, result); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, result); *result = v8impl::JsValueFromV8LocalValue( v8::Integer::NewFromUnsigned(env->isolate, value)); @@ -2587,8 +2600,8 @@ napi_status napi_create_int64(napi_env env, int64_t value, napi_value* result) { - CHECK_ENV(env); - CHECK_ARG(env, result); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, result); *result = v8impl::JsValueFromV8LocalValue( v8::Number::New(env->isolate, static_cast(value))); @@ -2600,8 +2613,8 @@ napi_status napi_create_bigint_int64(napi_env env, int64_t value, napi_value* result) { - CHECK_ENV(env); - CHECK_ARG(env, result); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, result); //*result = v8impl::JsValueFromV8LocalValue(v8::BigInt::New(env->isolate, value)); DebugBreak(); @@ -2613,10 +2626,10 @@ napi_status napi_create_bigint_uint64(napi_env env, uint64_t value, napi_value* result) { -// CHECK_ENV(env); -// CHECK_ARG(env, result); -// -// *result = v8impl::JsValueFromV8LocalValue(v8::BigInt::NewFromUnsigned(env->isolate, value)); + // NODE_CHECK_ENV(env); + // NODE_CHECK_ARG(env, result); + // + // *result = v8impl::JsValueFromV8LocalValue(v8::BigInt::NewFromUnsigned(env->isolate, value)); DebugBreak(); return napi_clear_last_error(env); } @@ -2627,35 +2640,35 @@ napi_status napi_create_bigint_words(napi_env env, const uint64_t* words, napi_value* result) { -// NAPI_PREAMBLE(env); -// CHECK_ARG(env, words); -// CHECK_ARG(env, result); -// -// v8::Local context = env->isolate->GetCurrentContext(); -// -// if (word_count > INT_MAX) { -// napi_throw_range_error(env, nullptr, "Maximum BigInt size exceeded"); -// return napi_set_last_error(env, napi_pending_exception); -// } -// -// v8::MaybeLocal b = v8::BigInt::NewFromWords( -// context, sign_bit, word_count, words); -// -// if (try_catch.HasCaught()) { -// return napi_set_last_error(env, napi_pending_exception); -// } else { -// CHECK_MAYBE_EMPTY(env, b, napi_generic_failure); -// *result = v8impl::JsValueFromV8LocalValue(b.ToLocalChecked()); -// return napi_clear_last_error(env); -// } + // NAPI_PREAMBLE(env); + // NODE_CHECK_ARG(env, words); + // NODE_CHECK_ARG(env, result); + // + // v8::Local context = env->isolate->GetCurrentContext(); + // + // if (word_count > INT_MAX) { + // napi_throw_range_error(env, nullptr, "Maximum BigInt size exceeded"); + // return napi_set_last_error(env, napi_pending_exception); + // } + // + // v8::MaybeLocal b = v8::BigInt::NewFromWords( + // context, sign_bit, word_count, words); + // + // if (try_catch.HasCaught()) { + // return napi_set_last_error(env, napi_pending_exception); + // } else { + // NODE_CHECK_MAYBE_EMPTY(env, b, napi_generic_failure); + // *result = v8impl::JsValueFromV8LocalValue(b.ToLocalChecked()); + // return napi_clear_last_error(env); + // } DebugBreak(); return napi_ok; } napi_status napi_get_boolean(napi_env env, bool value, napi_value* result) { - CHECK_ENV(env); - CHECK_ARG(env, result); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, result); v8::Isolate* isolate = env->isolate; @@ -2672,8 +2685,8 @@ napi_status napi_create_symbol(napi_env env, napi_value description, napi_value* result) { - CHECK_ENV(env); - CHECK_ARG(env, result); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, result); v8::Isolate* isolate = env->isolate; @@ -2705,11 +2718,11 @@ static napi_status set_error_code(napi_env env, code_value = v8impl::V8LocalValueFromJsValue(code); RETURN_STATUS_IF_FALSE(env, code_value->IsString(), napi_string_expected); } else { - CHECK_NEW_FROM_UTF8(env, code_value, code_cstring); + NODE_CHECK_NEW_FROM_UTF8(env, code_value, code_cstring); } v8::Local code_key; - CHECK_NEW_FROM_UTF8(env, code_key, "code"); + NODE_CHECK_NEW_FROM_UTF8(env, code_key, "code"); v8::Maybe set_maybe = err_object->Set(context, code_key, code_value); RETURN_STATUS_IF_FALSE(env, @@ -2719,9 +2732,9 @@ static napi_status set_error_code(napi_env env, // now update the name to be "name [code]" where name is the // original name and code is the code associated with the Error v8::Local name_string; - CHECK_NEW_FROM_UTF8(env, name_string, ""); + NODE_CHECK_NEW_FROM_UTF8(env, name_string, ""); v8::Local name_key; - CHECK_NEW_FROM_UTF8(env, name_key, "name"); + NODE_CHECK_NEW_FROM_UTF8(env, name_key, "name"); auto maybe_name = err_object->Get(context, name_key); if (!maybe_name.IsEmpty()) { @@ -2747,17 +2760,17 @@ napi_status napi_create_error(napi_env env, napi_value msg, napi_value* result) { - CHECK_ENV(env); - CHECK_ARG(env, msg); - CHECK_ARG(env, result); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, msg); + NODE_CHECK_ARG(env, result); v8::Local message_value = v8impl::V8LocalValueFromJsValue(msg); RETURN_STATUS_IF_FALSE(env, message_value->IsString(), napi_string_expected); - v8::Local error_obj = - v8::Exception::Error(message_value.As()); + v8::Local error_obj = v8::Exception::Error(message_value.As()); napi_status status = set_error_code(env, error_obj, code, nullptr); - if (status != napi_ok) return status; + if (status != napi_ok) + return status; *result = v8impl::JsValueFromV8LocalValue(error_obj); @@ -2769,17 +2782,17 @@ napi_status napi_create_type_error(napi_env env, napi_value msg, napi_value* result) { - CHECK_ENV(env); - CHECK_ARG(env, msg); - CHECK_ARG(env, result); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, msg); + NODE_CHECK_ARG(env, result); v8::Local message_value = v8impl::V8LocalValueFromJsValue(msg); RETURN_STATUS_IF_FALSE(env, message_value->IsString(), napi_string_expected); - v8::Local error_obj = - v8::Exception::TypeError(message_value.As()); + v8::Local error_obj = v8::Exception::TypeError(message_value.As()); napi_status status = set_error_code(env, error_obj, code, nullptr); - if (status != napi_ok) return status; + if (status != napi_ok) + return status; *result = v8impl::JsValueFromV8LocalValue(error_obj); @@ -2791,17 +2804,17 @@ napi_status napi_create_range_error(napi_env env, napi_value msg, napi_value* result) { - CHECK_ENV(env); - CHECK_ARG(env, msg); - CHECK_ARG(env, result); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, msg); + NODE_CHECK_ARG(env, result); v8::Local message_value = v8impl::V8LocalValueFromJsValue(msg); RETURN_STATUS_IF_FALSE(env, message_value->IsString(), napi_string_expected); - v8::Local error_obj = - v8::Exception::RangeError(message_value.As()); + v8::Local error_obj = v8::Exception::RangeError(message_value.As()); napi_status status = set_error_code(env, error_obj, code, nullptr); - if (status != napi_ok) return status; + if (status != napi_ok) + return status; *result = v8impl::JsValueFromV8LocalValue(error_obj); @@ -2814,16 +2827,16 @@ napi_status napi_typeof(napi_env env, { // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw // JS exceptions. - CHECK_ENV(env); - CHECK_ARG(env, value); - CHECK_ARG(env, result); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, value); + NODE_CHECK_ARG(env, result); v8::Local v = v8impl::V8LocalValueFromJsValue(value); if (v->IsNumber()) { *result = napi_number; -// } else if (v->IsBigInt()) { -// *result = napi_bigint; + // } else if (v->IsBigInt()) { + // *result = napi_bigint; } else if (v->IsString()) { *result = napi_string; } else if (v->IsFunction()) { @@ -2854,8 +2867,8 @@ napi_status napi_typeof(napi_env env, napi_status napi_get_undefined(napi_env env, napi_value* result) { - CHECK_ENV(env); - CHECK_ARG(env, result); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, result); *result = v8impl::JsValueFromV8LocalValue( v8::Undefined(env->isolate)); @@ -2865,8 +2878,8 @@ napi_status napi_get_undefined(napi_env env, napi_value* result) napi_status napi_get_null(napi_env env, napi_value* result) { - CHECK_ENV(env); - CHECK_ARG(env, result); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, result); *result = v8impl::JsValueFromV8LocalValue( v8::Null(env->isolate)); @@ -2876,22 +2889,21 @@ napi_status napi_get_null(napi_env env, napi_value* result) // Gets all callback info in a single call. (Ugly, but faster.) napi_status napi_get_cb_info( - napi_env env, // [in] NAPI environment handle - napi_callback_info cbinfo, // [in] Opaque callback-info handle - size_t* argc, // [in-out] Specifies the size of the provided argv array - // and receives the actual count of args. - napi_value* argv, // [out] Array of values - napi_value* this_arg, // [out] Receives the JS 'this' arg for the call + napi_env env, // [in] NAPI environment handle + napi_callback_info cbinfo, // [in] Opaque callback-info handle + size_t* argc, // [in-out] Specifies the size of the provided argv array + // and receives the actual count of args. + napi_value* argv, // [out] Array of values + napi_value* this_arg, // [out] Receives the JS 'this' arg for the call void** data) -{ // [out] Receives the data pointer for the callback. - CHECK_ENV(env); - CHECK_ARG(env, cbinfo); +{ // [out] Receives the data pointer for the callback. + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, cbinfo); - v8impl::CallbackWrapper* info = - reinterpret_cast(cbinfo); + v8impl::CallbackWrapper* info = reinterpret_cast(cbinfo); if (argv != nullptr) { - CHECK_ARG(env, argc); + NODE_CHECK_ARG(env, argc); info->Args(argv, *argc); } if (argc != nullptr) { @@ -2911,12 +2923,11 @@ napi_status napi_get_new_target(napi_env env, napi_callback_info cbinfo, napi_value* result) { - CHECK_ENV(env); - CHECK_ARG(env, cbinfo); - CHECK_ARG(env, result); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, cbinfo); + NODE_CHECK_ARG(env, result); - v8impl::CallbackWrapper* info = - reinterpret_cast(cbinfo); + v8impl::CallbackWrapper* info = reinterpret_cast(cbinfo); *result = info->GetNewTarget(); return napi_clear_last_error(env); @@ -2930,9 +2941,9 @@ napi_status napi_call_function(napi_env env, napi_value* result) { NAPI_PREAMBLE(env); - CHECK_ARG(env, recv); + NODE_CHECK_ARG(env, recv); if (argc > 0) { - CHECK_ARG(env, argv); + NODE_CHECK_ARG(env, argv); } v8::Isolate* isolate = env->isolate; @@ -2941,7 +2952,7 @@ napi_status napi_call_function(napi_env env, v8::Local v8recv = v8impl::V8LocalValueFromJsValue(recv); v8::Local v8func; - CHECK_TO_FUNCTION(env, v8func, func); + NODE_CHECK_TO_FUNCTION(env, v8func, func); auto maybe = v8func->Call(context, v8recv, argc, reinterpret_cast*>(const_cast(argv))); @@ -2950,7 +2961,7 @@ napi_status napi_call_function(napi_env env, return napi_set_last_error(env, napi_pending_exception); } else { if (result != nullptr) { - CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure); + NODE_CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure); *result = v8impl::JsValueFromV8LocalValue(maybe.ToLocalChecked()); } return napi_clear_last_error(env); @@ -2959,8 +2970,8 @@ napi_status napi_call_function(napi_env env, napi_status napi_get_global(napi_env env, napi_value* result) { - CHECK_ENV(env); - CHECK_ARG(env, result); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, result); v8::Isolate* isolate = env->isolate; // TODO(ianhall): what if we need the global object from a different @@ -2975,7 +2986,7 @@ napi_status napi_get_global(napi_env env, napi_value* result) napi_status napi_throw(napi_env env, napi_value error) { NAPI_PREAMBLE(env); - CHECK_ARG(env, error); + NODE_CHECK_ARG(env, error); v8::Isolate* isolate = env->isolate; @@ -2993,11 +3004,12 @@ napi_status napi_throw_error(napi_env env, v8::Isolate* isolate = env->isolate; v8::Local str; - CHECK_NEW_FROM_UTF8(env, str, msg); + NODE_CHECK_NEW_FROM_UTF8(env, str, msg); v8::Local error_obj = v8::Exception::Error(str); napi_status status = set_error_code(env, error_obj, nullptr, code); - if (status != napi_ok) return status; + if (status != napi_ok) + return status; isolate->ThrowException(error_obj); // any VM calls after this point and before returning @@ -3013,11 +3025,12 @@ napi_status napi_throw_type_error(napi_env env, v8::Isolate* isolate = env->isolate; v8::Local str; - CHECK_NEW_FROM_UTF8(env, str, msg); + NODE_CHECK_NEW_FROM_UTF8(env, str, msg); v8::Local error_obj = v8::Exception::TypeError(str); napi_status status = set_error_code(env, error_obj, nullptr, code); - if (status != napi_ok) return status; + if (status != napi_ok) + return status; isolate->ThrowException(error_obj); // any VM calls after this point and before returning @@ -3033,11 +3046,12 @@ napi_status napi_throw_range_error(napi_env env, v8::Isolate* isolate = env->isolate; v8::Local str; - CHECK_NEW_FROM_UTF8(env, str, msg); + NODE_CHECK_NEW_FROM_UTF8(env, str, msg); v8::Local error_obj = v8::Exception::RangeError(str); napi_status status = set_error_code(env, error_obj, nullptr, code); - if (status != napi_ok) return status; + if (status != napi_ok) + return status; isolate->ThrowException(error_obj); // any VM calls after this point and before returning @@ -3049,9 +3063,9 @@ napi_status napi_is_error(napi_env env, napi_value value, bool* result) { // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot // throw JS exceptions. - CHECK_ENV(env); - CHECK_ARG(env, value); - CHECK_ARG(env, result); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, value); + NODE_CHECK_ARG(env, result); v8::Local val = v8impl::V8LocalValueFromJsValue(value); *result = val->IsNativeError(); @@ -3065,9 +3079,9 @@ napi_status napi_get_value_double(napi_env env, { // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw // JS exceptions. - CHECK_ENV(env); - CHECK_ARG(env, value); - CHECK_ARG(env, result); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, value); + NODE_CHECK_ARG(env, result); v8::Local val = v8impl::V8LocalValueFromJsValue(value); RETURN_STATUS_IF_FALSE(env, val->IsNumber(), napi_number_expected); @@ -3083,9 +3097,9 @@ napi_status napi_get_value_int32(napi_env env, { // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw // JS exceptions. - CHECK_ENV(env); - CHECK_ARG(env, value); - CHECK_ARG(env, result); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, value); + NODE_CHECK_ARG(env, result); v8::Local val = v8impl::V8LocalValueFromJsValue(value); @@ -3108,9 +3122,9 @@ napi_status napi_get_value_uint32(napi_env env, { // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw // JS exceptions. - CHECK_ENV(env); - CHECK_ARG(env, value); - CHECK_ARG(env, result); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, value); + NODE_CHECK_ARG(env, result); v8::Local val = v8impl::V8LocalValueFromJsValue(value); @@ -3133,9 +3147,9 @@ napi_status napi_get_value_int64(napi_env env, { // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw // JS exceptions. - CHECK_ENV(env); - CHECK_ARG(env, value); - CHECK_ARG(env, result); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, value); + NODE_CHECK_ARG(env, result); v8::Local val = v8impl::V8LocalValueFromJsValue(value); @@ -3167,18 +3181,18 @@ napi_status napi_get_value_bigint_int64(napi_env env, int64_t* result, bool* lossless) { -// CHECK_ENV(env); -// CHECK_ARG(env, value); -// CHECK_ARG(env, result); -// CHECK_ARG(env, lossless); -// -// v8::Local val = v8impl::V8LocalValueFromJsValue(value); -// -// RETURN_STATUS_IF_FALSE(env, val->IsBigInt(), napi_bigint_expected); -// -// *result = val.As()->Int64Value(lossless); -// -// return napi_clear_last_error(env); + // NODE_CHECK_ENV(env); + // NODE_CHECK_ARG(env, value); + // NODE_CHECK_ARG(env, result); + // NODE_CHECK_ARG(env, lossless); + // + // v8::Local val = v8impl::V8LocalValueFromJsValue(value); + // + // RETURN_STATUS_IF_FALSE(env, val->IsBigInt(), napi_bigint_expected); + // + // *result = val.As()->Int64Value(lossless); + // + // return napi_clear_last_error(env); DebugBreak(); return napi_ok; } @@ -3188,18 +3202,18 @@ napi_status napi_get_value_bigint_uint64(napi_env env, uint64_t* result, bool* lossless) { -// CHECK_ENV(env); -// CHECK_ARG(env, value); -// CHECK_ARG(env, result); -// CHECK_ARG(env, lossless); -// -// v8::Local val = v8impl::V8LocalValueFromJsValue(value); -// -// RETURN_STATUS_IF_FALSE(env, val->IsBigInt(), napi_bigint_expected); -// -// *result = val.As()->Uint64Value(lossless); -// -// return napi_clear_last_error(env); + // NODE_CHECK_ENV(env); + // NODE_CHECK_ARG(env, value); + // NODE_CHECK_ARG(env, result); + // NODE_CHECK_ARG(env, lossless); + // + // v8::Local val = v8impl::V8LocalValueFromJsValue(value); + // + // RETURN_STATUS_IF_FALSE(env, val->IsBigInt(), napi_bigint_expected); + // + // *result = val.As()->Uint64Value(lossless); + // + // return napi_clear_last_error(env); DebugBreak(); return napi_ok; } @@ -3210,29 +3224,29 @@ napi_status napi_get_value_bigint_words(napi_env env, size_t* word_count, uint64_t* words) { -// CHECK_ENV(env); -// CHECK_ARG(env, value); -// CHECK_ARG(env, word_count); -// -// v8::Local val = v8impl::V8LocalValueFromJsValue(value); -// -// RETURN_STATUS_IF_FALSE(env, val->IsBigInt(), napi_bigint_expected); -// -// v8::Local big = val.As(); -// -// int word_count_int = *word_count; -// -// if (sign_bit == nullptr && words == nullptr) { -// word_count_int = big->WordCount(); -// } else { -// CHECK_ARG(env, sign_bit); -// CHECK_ARG(env, words); -// big->ToWordsArray(sign_bit, &word_count_int, words); -// } -// -// *word_count = word_count_int; -// -// return napi_clear_last_error(env); + // NODE_CHECK_ENV(env); + // NODE_CHECK_ARG(env, value); + // NODE_CHECK_ARG(env, word_count); + // + // v8::Local val = v8impl::V8LocalValueFromJsValue(value); + // + // RETURN_STATUS_IF_FALSE(env, val->IsBigInt(), napi_bigint_expected); + // + // v8::Local big = val.As(); + // + // int word_count_int = *word_count; + // + // if (sign_bit == nullptr && words == nullptr) { + // word_count_int = big->WordCount(); + // } else { + // NODE_CHECK_ARG(env, sign_bit); + // NODE_CHECK_ARG(env, words); + // big->ToWordsArray(sign_bit, &word_count_int, words); + // } + // + // *word_count = word_count_int; + // + // return napi_clear_last_error(env); DebugBreak(); return napi_ok; } @@ -3241,9 +3255,9 @@ napi_status napi_get_value_bool(napi_env env, napi_value value, bool* result) { // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw // JS exceptions. - CHECK_ENV(env); - CHECK_ARG(env, value); - CHECK_ARG(env, result); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, value); + NODE_CHECK_ARG(env, result); v8::Local val = v8impl::V8LocalValueFromJsValue(value); RETURN_STATUS_IF_FALSE(env, val->IsBoolean(), napi_boolean_expected); @@ -3267,22 +3281,21 @@ napi_status napi_get_value_string_latin1(napi_env env, size_t bufsize, size_t* result) { - CHECK_ENV(env); - CHECK_ARG(env, value); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, value); v8::Local val = v8impl::V8LocalValueFromJsValue(value); RETURN_STATUS_IF_FALSE(env, val->IsString(), napi_string_expected); if (!buf) { - CHECK_ARG(env, result); + NODE_CHECK_ARG(env, result); *result = val.As()->Length(); } else { - int copied = - val.As()->WriteOneByte( - reinterpret_cast(buf), - 0, - bufsize - 1, - v8::String::NO_NULL_TERMINATION); + int copied = val.As()->WriteOneByte( + reinterpret_cast(buf), + 0, + bufsize - 1, + v8::String::NO_NULL_TERMINATION); buf[copied] = '\0'; if (result != nullptr) { @@ -3307,14 +3320,14 @@ napi_status napi_get_value_string_utf8(napi_env env, size_t bufsize, size_t* result) { - CHECK_ENV(env); - CHECK_ARG(env, value); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, value); v8::Local val = v8impl::V8LocalValueFromJsValue(value); RETURN_STATUS_IF_FALSE(env, val->IsString(), napi_string_expected); if (!buf) { - CHECK_ARG(env, result); + NODE_CHECK_ARG(env, result); *result = val.As()->Utf8Length(); } else { int copied = val.As()->WriteUtf8( @@ -3346,14 +3359,14 @@ napi_status napi_get_value_string_utf16(napi_env env, size_t bufsize, size_t* result) { - CHECK_ENV(env); - CHECK_ARG(env, value); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, value); v8::Local val = v8impl::V8LocalValueFromJsValue(value); RETURN_STATUS_IF_FALSE(env, val->IsString(), napi_string_expected); if (!buf) { - CHECK_ARG(env, result); + NODE_CHECK_ARG(env, result); // V8 assumes UTF-16 length is the same as the number of characters. *result = val.As()->Length(); } else { @@ -3377,13 +3390,13 @@ napi_status napi_coerce_to_object(napi_env env, napi_value* result) { NAPI_PREAMBLE(env); - CHECK_ARG(env, value); - CHECK_ARG(env, result); + NODE_CHECK_ARG(env, value); + NODE_CHECK_ARG(env, result); v8::Isolate* isolate = env->isolate; v8::Local context = isolate->GetCurrentContext(); v8::Local obj; - CHECK_TO_OBJECT(env, context, obj, value); + NODE_CHECK_TO_OBJECT(env, context, obj, value); *result = v8impl::JsValueFromV8LocalValue(obj); return GET_RETURN_STATUS(env); @@ -3394,14 +3407,14 @@ napi_status napi_coerce_to_bool(napi_env env, napi_value* result) { NAPI_PREAMBLE(env); - CHECK_ARG(env, value); - CHECK_ARG(env, result); + NODE_CHECK_ARG(env, value); + NODE_CHECK_ARG(env, result); v8::Isolate* isolate = env->isolate; v8::Local context = isolate->GetCurrentContext(); v8::Local b; - CHECK_TO_BOOL(env, context, b, value); + NODE_CHECK_TO_BOOL(env, context, b, value); *result = v8impl::JsValueFromV8LocalValue(b); return GET_RETURN_STATUS(env); @@ -3412,14 +3425,14 @@ napi_status napi_coerce_to_number(napi_env env, napi_value* result) { NAPI_PREAMBLE(env); - CHECK_ARG(env, value); - CHECK_ARG(env, result); + NODE_CHECK_ARG(env, value); + NODE_CHECK_ARG(env, result); v8::Isolate* isolate = env->isolate; v8::Local context = isolate->GetCurrentContext(); v8::Local num; - CHECK_TO_NUMBER(env, context, num, value); + NODE_CHECK_TO_NUMBER(env, context, num, value); *result = v8impl::JsValueFromV8LocalValue(num); return GET_RETURN_STATUS(env); @@ -3430,14 +3443,14 @@ napi_status napi_coerce_to_string(napi_env env, napi_value* result) { NAPI_PREAMBLE(env); - CHECK_ARG(env, value); - CHECK_ARG(env, result); + NODE_CHECK_ARG(env, value); + NODE_CHECK_ARG(env, result); v8::Isolate* isolate = env->isolate; v8::Local context = isolate->GetCurrentContext(); v8::Local str; - CHECK_TO_STRING(env, context, str, value); + NODE_CHECK_TO_STRING(env, context, str, value); *result = v8impl::JsValueFromV8LocalValue(str); return GET_RETURN_STATUS(env); @@ -3475,7 +3488,7 @@ napi_status napi_create_external(napi_env env, napi_value* result) { NAPI_PREAMBLE(env); - CHECK_ARG(env, result); + NODE_CHECK_ARG(env, result); v8::Isolate* isolate = env->isolate; @@ -3500,9 +3513,9 @@ napi_status napi_get_value_external(napi_env env, napi_value value, void** result) { - CHECK_ENV(env); - CHECK_ARG(env, value); - CHECK_ARG(env, result); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, value); + NODE_CHECK_ARG(env, result); v8::Local val = v8impl::V8LocalValueFromJsValue(value); RETURN_STATUS_IF_FALSE(env, val->IsExternal(), napi_invalid_arg); @@ -3521,9 +3534,9 @@ napi_status napi_create_reference(napi_env env, { // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw // JS exceptions. - CHECK_ENV(env); - CHECK_ARG(env, value); - CHECK_ARG(env, result); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, value); + NODE_CHECK_ARG(env, result); v8::Local v8_value = v8impl::V8LocalValueFromJsValue(value); @@ -3531,8 +3544,7 @@ napi_status napi_create_reference(napi_env env, return napi_set_last_error(env, napi_object_expected); } - v8impl::Reference* reference = - v8impl::Reference::New(env, v8_value, initial_refcount, false); + v8impl::Reference* reference = v8impl::Reference::New(env, v8_value, initial_refcount, false); *result = reinterpret_cast(reference); return napi_clear_last_error(env); @@ -3544,8 +3556,8 @@ napi_status napi_delete_reference(napi_env env, napi_ref ref) { // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw // JS exceptions. - CHECK_ENV(env); - CHECK_ARG(env, ref); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, ref); v8impl::Reference::Delete(reinterpret_cast(ref)); @@ -3561,8 +3573,8 @@ napi_status napi_reference_ref(napi_env env, napi_ref ref, uint32_t* result) { // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw // JS exceptions. - CHECK_ENV(env); - CHECK_ARG(env, ref); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, ref); v8impl::Reference* reference = reinterpret_cast(ref); uint32_t count = reference->Ref(); @@ -3582,8 +3594,8 @@ napi_status napi_reference_unref(napi_env env, napi_ref ref, uint32_t* result) { // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw // JS exceptions. - CHECK_ENV(env); - CHECK_ARG(env, ref); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, ref); v8impl::Reference* reference = reinterpret_cast(ref); @@ -3609,9 +3621,9 @@ napi_status napi_get_reference_value(napi_env env, { // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw // JS exceptions. - CHECK_ENV(env); - CHECK_ARG(env, ref); - CHECK_ARG(env, result); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, ref); + NODE_CHECK_ARG(env, result); v8impl::Reference* reference = reinterpret_cast(ref); *result = v8impl::JsValueFromV8LocalValue(reference->Get()); @@ -3623,8 +3635,8 @@ napi_status napi_open_handle_scope(napi_env env, napi_handle_scope* result) { // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw // JS exceptions. - CHECK_ENV(env); - CHECK_ARG(env, result); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, result); *result = v8impl::JsHandleScopeFromV8HandleScope( new v8impl::HandleScopeWrapper(env->isolate)); @@ -3636,8 +3648,8 @@ napi_status napi_close_handle_scope(napi_env env, napi_handle_scope scope) { // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw // JS exceptions. - CHECK_ENV(env); - CHECK_ARG(env, scope); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, scope); if (env->open_handle_scopes == 0) { return napi_handle_scope_mismatch; } @@ -3653,8 +3665,8 @@ napi_status napi_open_escapable_handle_scope( { // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw // JS exceptions. - CHECK_ENV(env); - CHECK_ARG(env, result); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, result); *result = v8impl::JsEscapableHandleScopeFromV8EscapableHandleScope( new v8impl::EscapableHandleScopeWrapper(env->isolate)); @@ -3668,8 +3680,8 @@ napi_status napi_close_escapable_handle_scope( { // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw // JS exceptions. - CHECK_ENV(env); - CHECK_ARG(env, scope); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, scope); if (env->open_handle_scopes == 0) { return napi_handle_scope_mismatch; } @@ -3686,13 +3698,12 @@ napi_status napi_escape_handle(napi_env env, { // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw // JS exceptions. - CHECK_ENV(env); - CHECK_ARG(env, scope); - CHECK_ARG(env, escapee); - CHECK_ARG(env, result); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, scope); + NODE_CHECK_ARG(env, escapee); + NODE_CHECK_ARG(env, result); - v8impl::EscapableHandleScopeWrapper* s = - v8impl::V8EscapableHandleScopeFromJsEscapableHandleScope(scope); + v8impl::EscapableHandleScopeWrapper* s = v8impl::V8EscapableHandleScopeFromJsEscapableHandleScope(scope); if (!s->escape_called()) { *result = v8impl::JsValueFromV8LocalValue( s->Escape(v8impl::V8LocalValueFromJsValue(escapee))); @@ -3708,16 +3719,15 @@ napi_status napi_open_callback_scope(napi_env env, { // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw // JS exceptions. - CHECK_ENV(env); - CHECK_ARG(env, result); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, result); v8::Local context = env->isolate->GetCurrentContext(); - node::async_context* node_async_context = - reinterpret_cast(async_context_handle); + node::async_context* node_async_context = reinterpret_cast(async_context_handle); v8::Local resource; - CHECK_TO_OBJECT(env, context, resource, resource_object); + NODE_CHECK_TO_OBJECT(env, context, resource, resource_object); *result = v8impl::JsCallbackScopeFromV8CallbackScope( new node::CallbackScope(env->isolate, @@ -3732,8 +3742,8 @@ napi_status napi_close_callback_scope(napi_env env, napi_callback_scope scope) { // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw // JS exceptions. - CHECK_ENV(env); - CHECK_ARG(env, scope); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, scope); if (env->open_callback_scopes == 0) { return napi_callback_scope_mismatch; } @@ -3750,22 +3760,22 @@ napi_status napi_new_instance(napi_env env, napi_value* result) { NAPI_PREAMBLE(env); - CHECK_ARG(env, constructor); + NODE_CHECK_ARG(env, constructor); if (argc > 0) { - CHECK_ARG(env, argv); + NODE_CHECK_ARG(env, argv); } - CHECK_ARG(env, result); + NODE_CHECK_ARG(env, result); v8::Isolate* isolate = env->isolate; v8::Local context = isolate->GetCurrentContext(); v8::Local ctor; - CHECK_TO_FUNCTION(env, ctor, constructor); + NODE_CHECK_TO_FUNCTION(env, ctor, constructor); auto maybe = ctor->NewInstance(context, argc, reinterpret_cast*>(const_cast(argv))); - CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure); + NODE_CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure); *result = v8impl::JsValueFromV8LocalValue(maybe.ToLocalChecked()); return GET_RETURN_STATUS(env); @@ -3777,8 +3787,8 @@ napi_status napi_instanceof(napi_env env, bool* result) { NAPI_PREAMBLE(env); - CHECK_ARG(env, object); - CHECK_ARG(env, result); + NODE_CHECK_ARG(env, object); + NODE_CHECK_ARG(env, result); *result = false; @@ -3786,7 +3796,7 @@ napi_status napi_instanceof(napi_env env, v8::Isolate* isolate = env->isolate; v8::Local context = isolate->GetCurrentContext(); - CHECK_TO_OBJECT(env, context, ctor, constructor); + NODE_CHECK_TO_OBJECT(env, context, ctor, constructor); if (!ctor->IsFunction()) { napi_throw_type_error(env, @@ -3798,10 +3808,10 @@ napi_status napi_instanceof(napi_env env, napi_status status = napi_generic_failure; -// v8::Local val = v8impl::V8LocalValueFromJsValue(object); -// auto maybe_result = val->InstanceOf(context, ctor); -// CHECK_MAYBE_NOTHING(env, maybe_result, status); -// *result = maybe_result.FromJust(); + // v8::Local val = v8impl::V8LocalValueFromJsValue(object); + // auto maybe_result = val->InstanceOf(context, ctor); + // NODE_CHECK_MAYBE_NOTHING(env, maybe_result, status); + // *result = maybe_result.FromJust(); DebugBreak(); return GET_RETURN_STATUS(env); } @@ -3811,22 +3821,22 @@ napi_status napi_async_init(napi_env env, napi_value async_resource_name, napi_async_context* result) { - CHECK_ENV(env); - CHECK_ARG(env, async_resource_name); - CHECK_ARG(env, result); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, async_resource_name); + NODE_CHECK_ARG(env, result); v8::Isolate* isolate = env->isolate; v8::Local context = isolate->GetCurrentContext(); v8::Local v8_resource; if (async_resource != nullptr) { - CHECK_TO_OBJECT(env, context, v8_resource, async_resource); + NODE_CHECK_TO_OBJECT(env, context, v8_resource, async_resource); } else { v8_resource = v8::Object::New(isolate); } v8::Local v8_resource_name; - CHECK_TO_STRING(env, context, v8_resource_name, async_resource_name); + NODE_CHECK_TO_STRING(env, context, v8_resource_name, async_resource_name); // TODO(jasongin): Consider avoiding allocation here by using // a tagged pointer with 2×31 bit fields instead. @@ -3841,12 +3851,11 @@ napi_status napi_async_init(napi_env env, napi_status napi_async_destroy(napi_env env, napi_async_context async_context) { - CHECK_ENV(env); - CHECK_ARG(env, async_context); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, async_context); v8::Isolate* isolate = env->isolate; - node::async_context* node_async_context = - reinterpret_cast(async_context); + node::async_context* node_async_context = reinterpret_cast(async_context); //node::EmitAsyncDestroy(isolate, *node_async_context); DebugBreak(); @@ -3864,19 +3873,19 @@ napi_status napi_make_callback(napi_env env, napi_value* result) { NAPI_PREAMBLE(env); - CHECK_ARG(env, recv); + NODE_CHECK_ARG(env, recv); if (argc > 0) { - CHECK_ARG(env, argv); + NODE_CHECK_ARG(env, argv); } v8::Isolate* isolate = env->isolate; v8::Local context = isolate->GetCurrentContext(); v8::Local v8recv; - CHECK_TO_OBJECT(env, context, v8recv, recv); + NODE_CHECK_TO_OBJECT(env, context, v8recv, recv); v8::Local v8func; - CHECK_TO_FUNCTION(env, v8func, func); + NODE_CHECK_TO_FUNCTION(env, v8func, func); node::async_context* node_async_context = reinterpret_cast(async_context); if (node_async_context == nullptr) { @@ -3888,12 +3897,12 @@ napi_status napi_make_callback(napi_env env, isolate, v8recv, v8func, argc, reinterpret_cast*>(const_cast(argv)) //, *node_async_context - ); + ); if (try_catch.HasCaught()) { return napi_set_last_error(env, napi_pending_exception); } else { - CHECK_MAYBE_EMPTY(env, callback_result, napi_generic_failure); + NODE_CHECK_MAYBE_EMPTY(env, callback_result, napi_generic_failure); if (result != nullptr) { *result = v8impl::JsValueFromV8LocalValue( callback_result.ToLocalChecked()); @@ -3908,8 +3917,8 @@ napi_status napi_is_exception_pending(napi_env env, bool* result) { // NAPI_PREAMBLE is not used here: this function must execute when there is a // pending exception. - CHECK_ENV(env); - CHECK_ARG(env, result); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, result); *result = !env->last_exception.IsEmpty(); return napi_clear_last_error(env); @@ -3920,8 +3929,8 @@ napi_status napi_get_and_clear_last_exception(napi_env env, { // NAPI_PREAMBLE is not used here: this function must execute when there is a // pending exception. - CHECK_ENV(env); - CHECK_ARG(env, result); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, result); if (env->last_exception.IsEmpty()) { return napi_get_undefined(env, result); @@ -3940,11 +3949,11 @@ napi_status napi_create_buffer(napi_env env, napi_value* result) { NAPI_PREAMBLE(env); - CHECK_ARG(env, result); + NODE_CHECK_ARG(env, result); auto maybe = node::Buffer::New(env->isolate, length); - CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure); + NODE_CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure); v8::Local buffer = maybe.ToLocalChecked(); @@ -3965,7 +3974,7 @@ napi_status napi_create_external_buffer(napi_env env, napi_value* result) { NAPI_PREAMBLE(env); - CHECK_ARG(env, result); + NODE_CHECK_ARG(env, result); v8::Isolate* isolate = env->isolate; @@ -3979,7 +3988,7 @@ napi_status napi_create_external_buffer(napi_env env, v8impl::Finalizer::FinalizeBufferCallback, finalizer); - CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure); + NODE_CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure); *result = v8impl::JsValueFromV8LocalValue(maybe.ToLocalChecked()); return GET_RETURN_STATUS(env); @@ -3996,12 +4005,12 @@ napi_status napi_create_buffer_copy(napi_env env, napi_value* result) { NAPI_PREAMBLE(env); - CHECK_ARG(env, result); + NODE_CHECK_ARG(env, result); auto maybe = node::Buffer::Copy(env->isolate, static_cast(data), length); - CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure); + NODE_CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure); v8::Local buffer = maybe.ToLocalChecked(); *result = v8impl::JsValueFromV8LocalValue(buffer); @@ -4015,9 +4024,9 @@ napi_status napi_create_buffer_copy(napi_env env, napi_status napi_is_buffer(napi_env env, napi_value value, bool* result) { - CHECK_ENV(env); - CHECK_ARG(env, value); - CHECK_ARG(env, result); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, value); + NODE_CHECK_ARG(env, result); *result = node::Buffer::HasInstance(v8impl::V8LocalValueFromJsValue(value)); return napi_clear_last_error(env); @@ -4028,8 +4037,8 @@ napi_status napi_get_buffer_info(napi_env env, void** data, size_t* length) { - CHECK_ENV(env); - CHECK_ARG(env, value); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, value); v8::Local buffer = v8impl::V8LocalValueFromJsValue(value); @@ -4045,9 +4054,9 @@ napi_status napi_get_buffer_info(napi_env env, napi_status napi_is_arraybuffer(napi_env env, napi_value value, bool* result) { - CHECK_ENV(env); - CHECK_ARG(env, value); - CHECK_ARG(env, result); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, value); + NODE_CHECK_ARG(env, result); v8::Local val = v8impl::V8LocalValueFromJsValue(value); *result = val->IsArrayBuffer(); @@ -4061,11 +4070,10 @@ napi_status napi_create_arraybuffer(napi_env env, napi_value* result) { NAPI_PREAMBLE(env); - CHECK_ARG(env, result); + NODE_CHECK_ARG(env, result); v8::Isolate* isolate = env->isolate; - v8::Local buffer = - v8::ArrayBuffer::New(isolate, byte_length); + v8::Local buffer = v8::ArrayBuffer::New(isolate, byte_length); // Optionally return a pointer to the buffer's data, to avoid another call to // retrieve it. @@ -4085,11 +4093,10 @@ napi_status napi_create_external_arraybuffer(napi_env env, napi_value* result) { NAPI_PREAMBLE(env); - CHECK_ARG(env, result); + NODE_CHECK_ARG(env, result); v8::Isolate* isolate = env->isolate; - v8::Local buffer = - v8::ArrayBuffer::New(isolate, external_data, byte_length); + v8::Local buffer = v8::ArrayBuffer::New(isolate, external_data, byte_length); if (finalize_cb != nullptr) { // Create a self-deleting weak reference that invokes the finalizer @@ -4112,14 +4119,13 @@ napi_status napi_get_arraybuffer_info(napi_env env, void** data, size_t* byte_length) { - CHECK_ENV(env); - CHECK_ARG(env, arraybuffer); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, arraybuffer); v8::Local value = v8impl::V8LocalValueFromJsValue(arraybuffer); RETURN_STATUS_IF_FALSE(env, value->IsArrayBuffer(), napi_invalid_arg); - v8::ArrayBuffer::Contents contents = - value.As()->GetContents(); + v8::ArrayBuffer::Contents contents = value.As()->GetContents(); if (data != nullptr) { *data = contents.Data(); @@ -4134,9 +4140,9 @@ napi_status napi_get_arraybuffer_info(napi_env env, napi_status napi_is_typedarray(napi_env env, napi_value value, bool* result) { - CHECK_ENV(env); - CHECK_ARG(env, value); - CHECK_ARG(env, result); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, value); + NODE_CHECK_ARG(env, result); v8::Local val = v8impl::V8LocalValueFromJsValue(value); *result = val->IsTypedArray(); @@ -4152,8 +4158,8 @@ napi_status napi_create_typedarray(napi_env env, napi_value* result) { NAPI_PREAMBLE(env); - CHECK_ARG(env, arraybuffer); - CHECK_ARG(env, result); + NODE_CHECK_ARG(env, arraybuffer); + NODE_CHECK_ARG(env, result); v8::Local value = v8impl::V8LocalValueFromJsValue(arraybuffer); RETURN_STATUS_IF_FALSE(env, value->IsArrayBuffer(), napi_invalid_arg); @@ -4198,14 +4204,14 @@ napi_status napi_create_typedarray(napi_env env, CREATE_TYPED_ARRAY( env, Float64Array, 8, buffer, byte_offset, length, typedArray); break; -// case napi_bigint64_array: -// CREATE_TYPED_ARRAY( -// env, BigInt64Array, 8, buffer, byte_offset, length, typedArray); -// break; -// case napi_biguint64_array: -// CREATE_TYPED_ARRAY( -// env, BigUint64Array, 8, buffer, byte_offset, length, typedArray); -// break; + // case napi_bigint64_array: + // CREATE_TYPED_ARRAY( + // env, BigInt64Array, 8, buffer, byte_offset, length, typedArray); + // break; + // case napi_biguint64_array: + // CREATE_TYPED_ARRAY( + // env, BigUint64Array, 8, buffer, byte_offset, length, typedArray); + // break; default: return napi_set_last_error(env, napi_invalid_arg); } @@ -4222,8 +4228,8 @@ napi_status napi_get_typedarray_info(napi_env env, napi_value* arraybuffer, size_t* byte_offset) { - CHECK_ENV(env); - CHECK_ARG(env, typedarray); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, typedarray); v8::Local value = v8impl::V8LocalValueFromJsValue(typedarray); RETURN_STATUS_IF_FALSE(env, value->IsTypedArray(), napi_invalid_arg); @@ -4249,10 +4255,10 @@ napi_status napi_get_typedarray_info(napi_env env, *type = napi_float32_array; } else if (value->IsFloat64Array()) { *type = napi_float64_array; -// } else if (value->IsBigInt64Array()) { -// *type = napi_bigint64_array; -// } else if (value->IsBigUint64Array()) { -// *type = napi_biguint64_array; + // } else if (value->IsBigInt64Array()) { + // *type = napi_bigint64_array; + // } else if (value->IsBigUint64Array()) { + // *type = napi_biguint64_array; } } @@ -4262,8 +4268,7 @@ napi_status napi_get_typedarray_info(napi_env env, v8::Local buffer = array->Buffer(); if (data != nullptr) { - *data = static_cast(buffer->GetContents().Data()) + - array->ByteOffset(); + *data = static_cast(buffer->GetContents().Data()) + array->ByteOffset(); } if (arraybuffer != nullptr) { @@ -4284,8 +4289,8 @@ napi_status napi_create_dataview(napi_env env, napi_value* result) { NAPI_PREAMBLE(env); - CHECK_ARG(env, arraybuffer); - CHECK_ARG(env, result); + NODE_CHECK_ARG(env, arraybuffer); + NODE_CHECK_ARG(env, result); v8::Local value = v8impl::V8LocalValueFromJsValue(arraybuffer); RETURN_STATUS_IF_FALSE(env, value->IsArrayBuffer(), napi_invalid_arg); @@ -4308,9 +4313,9 @@ napi_status napi_create_dataview(napi_env env, napi_status napi_is_dataview(napi_env env, napi_value value, bool* result) { - CHECK_ENV(env); - CHECK_ARG(env, value); - CHECK_ARG(env, result); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, value); + NODE_CHECK_ARG(env, result); v8::Local val = v8impl::V8LocalValueFromJsValue(value); *result = val->IsDataView(); @@ -4325,8 +4330,8 @@ napi_status napi_get_dataview_info(napi_env env, napi_value* arraybuffer, size_t* byte_offset) { - CHECK_ENV(env); - CHECK_ARG(env, dataview); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, dataview); v8::Local value = v8impl::V8LocalValueFromJsValue(dataview); RETURN_STATUS_IF_FALSE(env, value->IsDataView(), napi_invalid_arg); @@ -4339,8 +4344,7 @@ napi_status napi_get_dataview_info(napi_env env, v8::Local buffer = array->Buffer(); if (data != nullptr) { - *data = static_cast(buffer->GetContents().Data()) + - array->ByteOffset(); + *data = static_cast(buffer->GetContents().Data()) + array->ByteOffset(); } if (arraybuffer != nullptr) { @@ -4356,8 +4360,8 @@ napi_status napi_get_dataview_info(napi_env env, napi_status napi_get_version(napi_env env, uint32_t* result) { - CHECK_ENV(env); - CHECK_ARG(env, result); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, result); *result = NAPI_VERSION; return napi_clear_last_error(env); } @@ -4365,8 +4369,8 @@ napi_status napi_get_version(napi_env env, uint32_t* result) napi_status napi_get_node_version(napi_env env, const napi_node_version** result) { - CHECK_ENV(env); - CHECK_ARG(env, result); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, result); static const napi_node_version version = { NODE_MAJOR_VERSION, NODE_MINOR_VERSION, @@ -4381,8 +4385,8 @@ napi_status napi_adjust_external_memory(napi_env env, int64_t change_in_bytes, int64_t* adjusted_value) { - CHECK_ENV(env); - CHECK_ARG(env, adjusted_value); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, adjusted_value); *adjusted_value = env->isolate->AdjustAmountOfExternalAllocatedMemory( change_in_bytes); @@ -4393,110 +4397,110 @@ napi_status napi_adjust_external_memory(napi_env env, namespace { namespace uvimpl { -static napi_status ConvertUVErrorCode(int code) -{ - switch (code) { - case 0: - return napi_ok; - case UV_EINVAL: - return napi_invalid_arg; - case UV_ECANCELED: - return napi_cancelled; - } - - return napi_generic_failure; -} - -// Wrapper around uv_work_t which calls user-provided callbacks. -class Work : public node::AsyncResource, public node::ThreadPoolWork { -private: - explicit Work(napi_env env, - v8::Local async_resource, - v8::Local async_resource_name, - napi_async_execute_callback execute, - napi_async_complete_callback complete = nullptr, - void* data = nullptr) - : AsyncResource(env->isolate, async_resource, *v8::String::Utf8Value(async_resource_name)), - ThreadPoolWork(node::Environment::GetCurrent(env->isolate)), - _env(env), - _data(data), - _execute(execute), - _complete(complete) + static napi_status ConvertUVErrorCode(int code) { + switch (code) { + case 0: + return napi_ok; + case UV_EINVAL: + return napi_invalid_arg; + case UV_ECANCELED: + return napi_cancelled; + } + + return napi_generic_failure; } - virtual ~Work() {} + // Wrapper around uv_work_t which calls user-provided callbacks. + class Work : public node::AsyncResource, public node::ThreadPoolWork { + private: + explicit Work(napi_env env, + v8::Local async_resource, + v8::Local async_resource_name, + napi_async_execute_callback execute, + napi_async_complete_callback complete = nullptr, + void* data = nullptr) + : AsyncResource(env->isolate, async_resource, *v8::String::Utf8Value(async_resource_name)) + , ThreadPoolWork(node::Environment::GetCurrent(env->isolate)) + , _env(env) + , _data(data) + , _execute(execute) + , _complete(complete) + { + } -public: - static Work* New(napi_env env, - v8::Local async_resource, - v8::Local async_resource_name, - napi_async_execute_callback execute, - napi_async_complete_callback complete, - void* data) - { - return new Work(env, async_resource, async_resource_name, - execute, complete, data); - } + virtual ~Work() { } - static void Delete(Work* work) - { - delete work; - } + public: + static Work* New(napi_env env, + v8::Local async_resource, + v8::Local async_resource_name, + napi_async_execute_callback execute, + napi_async_complete_callback complete, + void* data) + { + return new Work(env, async_resource, async_resource_name, + execute, complete, data); + } - void DoThreadPoolWork() override - { - _execute(_env, _data); - } + static void Delete(Work* work) + { + delete work; + } - void AfterThreadPoolWork(int status) override - { - if (_complete == nullptr) - return; - - // Establish a handle scope here so that every callback doesn't have to. - // Also it is needed for the exception-handling below. - v8::HandleScope scope(_env->isolate); - - CallbackScope callback_scope(this); - - // We have to back up the env here because the `NAPI_CALL_INTO_MODULE` macro - // makes use of it after the call into the module completes, but the module - // may have deallocated **this**, and along with it the place where _env is - // stored. - napi_env env = _env; - - NAPI_CALL_INTO_MODULE(env, - _complete(_env, ConvertUVErrorCode(status), _data), - [env](v8::Local local_err) { - // If there was an unhandled exception in the complete callback, - // report it as a fatal exception. (There is no JavaScript on the - // callstack that can possibly handle it.) - v8impl::trigger_fatal_exception(env, local_err); - }); + void DoThreadPoolWork() override + { + _execute(_env, _data); + } - // Note: Don't access `work` after this point because it was - // likely deleted by the complete callback. - } + void AfterThreadPoolWork(int status) override + { + if (_complete == nullptr) + return; -private: - napi_env _env; - void* _data; - napi_async_execute_callback _execute; - napi_async_complete_callback _complete; -}; + // Establish a handle scope here so that every callback doesn't have to. + // Also it is needed for the exception-handling below. + v8::HandleScope scope(_env->isolate); + + CallbackScope callback_scope(this); + + // We have to back up the env here because the `NAPI_CALL_INTO_MODULE` macro + // makes use of it after the call into the module completes, but the module + // may have deallocated **this**, and along with it the place where _env is + // stored. + napi_env env = _env; + + NAPI_CALL_INTO_MODULE(env, + _complete(_env, ConvertUVErrorCode(status), _data), + [env](v8::Local local_err) { + // If there was an unhandled exception in the complete callback, + // report it as a fatal exception. (There is no JavaScript on the + // callstack that can possibly handle it.) + v8impl::trigger_fatal_exception(env, local_err); + }); + + // Note: Don't access `work` after this point because it was + // likely deleted by the complete callback. + } -} // end of namespace uvimpl -} // end of anonymous namespace + private: + napi_env _env; + void* _data; + napi_async_execute_callback _execute; + napi_async_complete_callback _complete; + }; + +} // end of namespace uvimpl +} // end of anonymous namespace -#define CALL_UV(env, condition) \ - do { \ - int result = (condition); \ - napi_status status = uvimpl::ConvertUVErrorCode(result); \ - if (status != napi_ok) { \ - return napi_set_last_error(env, status, result); \ - } \ - } while (0) +#define CALL_UV(env, condition) \ + do { \ + int result = (condition); \ + napi_status status = uvimpl::ConvertUVErrorCode(result); \ + if (status != napi_ok) { \ + return napi_set_last_error(env, status, result); \ + } \ + } while (0) napi_status napi_create_async_work(napi_env env, napi_value async_resource, @@ -4506,25 +4510,24 @@ napi_status napi_create_async_work(napi_env env, void* data, napi_async_work* result) { - CHECK_ENV(env); - CHECK_ARG(env, execute); - CHECK_ARG(env, result); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, execute); + NODE_CHECK_ARG(env, result); v8::Local context = env->isolate->GetCurrentContext(); v8::Local resource; if (async_resource != nullptr) { - CHECK_TO_OBJECT(env, context, resource, async_resource); + NODE_CHECK_TO_OBJECT(env, context, resource, async_resource); } else { resource = v8::Object::New(env->isolate); } v8::Local resource_name; - CHECK_TO_STRING(env, context, resource_name, async_resource_name); + NODE_CHECK_TO_STRING(env, context, resource_name, async_resource_name); - uvimpl::Work* work = - uvimpl::Work::New(env, resource, resource_name, - execute, complete, data); + uvimpl::Work* work = uvimpl::Work::New(env, resource, resource_name, + execute, complete, data); *result = reinterpret_cast(work); @@ -4533,8 +4536,8 @@ napi_status napi_create_async_work(napi_env env, napi_status napi_delete_async_work(napi_env env, napi_async_work work) { - CHECK_ENV(env); - CHECK_ARG(env, work); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, work); uvimpl::Work::Delete(reinterpret_cast(work)); @@ -4543,16 +4546,16 @@ napi_status napi_delete_async_work(napi_env env, napi_async_work work) napi_status napi_get_uv_event_loop(napi_env env, uv_loop_t** loop) { - CHECK_ENV(env); - CHECK_ARG(env, loop); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, loop); *loop = env->loop; return napi_clear_last_error(env); } napi_status napi_queue_async_work(napi_env env, napi_async_work work) { - CHECK_ENV(env); - CHECK_ARG(env, work); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, work); napi_status status; uv_loop_t* event_loop = nullptr; @@ -4569,8 +4572,8 @@ napi_status napi_queue_async_work(napi_env env, napi_async_work work) napi_status napi_cancel_async_work(napi_env env, napi_async_work work) { - CHECK_ENV(env); - CHECK_ARG(env, work); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, work); uvimpl::Work* w = reinterpret_cast(work); @@ -4584,11 +4587,11 @@ napi_status napi_create_promise(napi_env env, napi_value* promise) { NAPI_PREAMBLE(env); - CHECK_ARG(env, deferred); - CHECK_ARG(env, promise); + NODE_CHECK_ARG(env, deferred); + NODE_CHECK_ARG(env, promise); auto maybe = v8::Promise::Resolver::New(env->isolate->GetCurrentContext()); - CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure); + NODE_CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure); auto v8_resolver = maybe.ToLocalChecked(); auto v8_deferred = new node::Persistent(); @@ -4617,9 +4620,9 @@ napi_status napi_is_promise(napi_env env, napi_value promise, bool* is_promise) { - CHECK_ENV(env); - CHECK_ARG(env, promise); - CHECK_ARG(env, is_promise); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, promise); + NODE_CHECK_ARG(env, is_promise); *is_promise = v8impl::V8LocalValueFromJsValue(promise)->IsPromise(); @@ -4631,8 +4634,8 @@ napi_status napi_run_script(napi_env env, napi_value* result) { NAPI_PREAMBLE(env); - CHECK_ARG(env, script); - CHECK_ARG(env, result); + NODE_CHECK_ARG(env, script); + NODE_CHECK_ARG(env, result); v8::Local v8_script = v8impl::V8LocalValueFromJsValue(script); @@ -4644,11 +4647,10 @@ napi_status napi_run_script(napi_env env, auto maybe_script = v8::Script::Compile(context, v8::Local::Cast(v8_script)); - CHECK_MAYBE_EMPTY(env, maybe_script, napi_generic_failure); + NODE_CHECK_MAYBE_EMPTY(env, maybe_script, napi_generic_failure); - auto script_result = - maybe_script.ToLocalChecked()->Run(context); - CHECK_MAYBE_EMPTY(env, script_result, napi_generic_failure); + auto script_result = maybe_script.ToLocalChecked()->Run(context); + NODE_CHECK_MAYBE_EMPTY(env, script_result, napi_generic_failure); *result = v8impl::JsValueFromV8LocalValue(script_result.ToLocalChecked()); return GET_RETURN_STATUS(env); @@ -4666,16 +4668,16 @@ napi_status napi_create_threadsafe_function(napi_env env, napi_threadsafe_function_call_js call_js_cb, napi_threadsafe_function* result) { - CHECK_ENV(env); - CHECK_ARG(env, func); - CHECK_ARG(env, async_resource_name); + NODE_CHECK_ENV(env); + NODE_CHECK_ARG(env, func); + NODE_CHECK_ARG(env, async_resource_name); RETURN_STATUS_IF_FALSE(env, initial_thread_count > 0, napi_invalid_arg); - CHECK_ARG(env, result); + NODE_CHECK_ARG(env, result); napi_status status = napi_ok; v8::Local v8_func; - CHECK_TO_FUNCTION(env, v8_func, func); + NODE_CHECK_TO_FUNCTION(env, v8_func, func); v8::Local v8_context = env->isolate->GetCurrentContext(); @@ -4683,23 +4685,23 @@ napi_status napi_create_threadsafe_function(napi_env env, if (async_resource == nullptr) { v8_resource = v8::Object::New(env->isolate); } else { - CHECK_TO_OBJECT(env, v8_context, v8_resource, async_resource); + NODE_CHECK_TO_OBJECT(env, v8_context, v8_resource, async_resource); } v8::Local v8_name; - CHECK_TO_STRING(env, v8_context, v8_name, async_resource_name); + NODE_CHECK_TO_STRING(env, v8_context, v8_name, async_resource_name); v8impl::ThreadSafeFunction* ts_fn = new v8impl::ThreadSafeFunction(v8_func, - v8_resource, - v8_name, - v8_context, - initial_thread_count, - context, - max_queue_size, - env, - thread_finalize_data, - thread_finalize_cb, - call_js_cb); + v8_resource, + v8_name, + v8_context, + initial_thread_count, + context, + max_queue_size, + env, + thread_finalize_data, + thread_finalize_cb, + call_js_cb); if (ts_fn == nullptr) { status = napi_generic_failure; @@ -4718,8 +4720,8 @@ napi_status napi_get_threadsafe_function_context(napi_threadsafe_function func, void** result) { - CHECK(func != nullptr); - CHECK(result != nullptr); + NODE_CHECK(func != nullptr); + NODE_CHECK(result != nullptr); *result = reinterpret_cast(func)->Context(); return napi_ok; @@ -4730,7 +4732,7 @@ napi_call_threadsafe_function(napi_threadsafe_function func, void* data, napi_threadsafe_function_call_mode is_blocking) { - CHECK(func != nullptr); + NODE_CHECK(func != nullptr); return reinterpret_cast(func)->Push(data, is_blocking); } @@ -4738,7 +4740,7 @@ napi_call_threadsafe_function(napi_threadsafe_function func, napi_status napi_acquire_threadsafe_function(napi_threadsafe_function func) { - CHECK(func != nullptr); + NODE_CHECK(func != nullptr); return reinterpret_cast(func)->Acquire(); } @@ -4746,21 +4748,21 @@ napi_status napi_release_threadsafe_function(napi_threadsafe_function func, napi_threadsafe_function_release_mode mode) { - CHECK(func != nullptr); + NODE_CHECK(func != nullptr); return reinterpret_cast(func)->Release(mode); } napi_status napi_unref_threadsafe_function(napi_env env, napi_threadsafe_function func) { - CHECK(func != nullptr); + NODE_CHECK(func != nullptr); return reinterpret_cast(func)->Unref(); } napi_status napi_ref_threadsafe_function(napi_env env, napi_threadsafe_function func) { - CHECK(func != nullptr); + NODE_CHECK(func != nullptr); return reinterpret_cast(func)->Ref(); } @@ -4773,3 +4775,32 @@ napi_status napi_add_finalizer(napi_env env, { return v8impl::Wrap(env, js_object, native_object, finalize_cb, finalize_hint, result); } + +napi_status napi_set_instance_data(napi_env env, + void* data, + napi_finalize finalize_cb, + void* finalize_hint) +{ + env->instance_data = data; + env->finalize_cb = finalize_cb; + env->finalize_hint = finalize_hint; + +// char* output = (char*)malloc(0x100); +// sprintf(output, "napi_set_instance_data: env:%p data:%p\n", env, data); +// OutputDebugStringA(output); +// free(output); + + return napi_clear_last_error(env); +} + +napi_status napi_get_instance_data(napi_env env, void** data) +{ + *data = env->instance_data; + +// char* output = (char*)malloc(0x100); +// sprintf(output, "napi_get_instance_data: env:%p data:%p\n", env, *data); +// OutputDebugStringA(output); +// free(output); + + return napi_clear_last_error(env); +} \ No newline at end of file diff --git a/node/src/node_api.h b/node/src/node_api.h index e8c1f79de7..b92f737e72 100644 --- a/node/src/node_api.h +++ b/node/src/node_api.h @@ -5,7 +5,7 @@ #include #include "node_api_types.h" -struct uv_loop_s; // Forward declaration. +struct uv_loop_s; // Forward declaration. #ifndef NAPI_VERSION #ifdef NAPI_EXPERIMENTAL @@ -18,26 +18,26 @@ struct uv_loop_s; // Forward declaration. #endif #ifdef _WIN32 - #ifdef BUILDING_NODE_EXTENSION - #ifdef EXTERNAL_NAPI - // Building external N-API, or native module against external N-API - #define NAPI_EXTERN /* nothing */ - #else - // Building native module against node with built-in N-API - #define NAPI_EXTERN __declspec(dllimport) - #endif - #else +#ifdef BUILDING_NODE_EXTENSION +#ifdef EXTERNAL_NAPI + // Building external N-API, or native module against external N-API +#define NAPI_EXTERN /* nothing */ +#else + // Building native module against node with built-in N-API +#define NAPI_EXTERN __declspec(dllimport) +#endif +#else // Building node with built-in N-API - #define NAPI_EXTERN __declspec(dllexport) - #endif +#define NAPI_EXTERN __declspec(dllexport) +#endif #else - #define NAPI_EXTERN /* nothing */ +#define NAPI_EXTERN /* nothing */ #endif #ifdef _WIN32 -# define NAPI_MODULE_EXPORT __declspec(dllexport) +#define NAPI_MODULE_EXPORT __declspec(dllexport) #else -# define NAPI_MODULE_EXPORT __attribute__((visibility("default"))) +#define NAPI_MODULE_EXPORT __attribute__((visibility("default"))) #endif #ifdef __GNUC__ @@ -46,33 +46,31 @@ struct uv_loop_s; // Forward declaration. #define NAPI_NO_RETURN #endif - typedef napi_value (*napi_addon_register_func)(napi_env env, - napi_value exports); + napi_value exports); typedef struct { - int nm_version; - unsigned int nm_flags; - const char* nm_filename; - napi_addon_register_func nm_register_func; - const char* nm_modname; - void* nm_priv; - void* reserved[4]; + int nm_version; + unsigned int nm_flags; + const char* nm_filename; + napi_addon_register_func nm_register_func; + const char* nm_modname; + void* nm_priv; + void* reserved[4]; } napi_module; -#define NAPI_MODULE_VERSION 1 +#define NAPI_MODULE_VERSION 1 #if defined(_MSC_VER) #pragma section(".CRT$XCU", read) -#define NAPI_C_CTOR(fn) \ - static void __cdecl fn(void); \ - __declspec(dllexport, allocate(".CRT$XCU")) void(__cdecl * fn##_)(void) = \ - fn; \ - static void __cdecl fn(void) +#define NAPI_C_CTOR(fn) \ + static void __cdecl fn(void); \ + __declspec(dllexport, allocate(".CRT$XCU")) void(__cdecl * fn##_)(void) = fn; \ + static void __cdecl fn(void) #else -#define NAPI_C_CTOR(fn) \ - static void fn(void) __attribute__((constructor)); \ - static void fn(void) +#define NAPI_C_CTOR(fn) \ + static void fn(void) __attribute__((constructor)); \ + static void fn(void) #endif #ifdef __cplusplus @@ -83,44 +81,44 @@ typedef struct { #define EXTERN_C_END #endif -#define NAPI_MODULE_X(modname, regfunc, priv, flags) \ - EXTERN_C_START \ - static napi_module _module = \ - { \ - NAPI_MODULE_VERSION, \ - flags, \ - __FILE__, \ - regfunc, \ - #modname, \ - priv, \ - {0}, \ - }; \ - NAPI_C_CTOR(_register_ ## modname) { \ - napi_module_register(&_module); \ - } \ - EXTERN_C_END - -#define NAPI_MODULE(modname, regfunc) \ - NAPI_MODULE_X(modname, regfunc, NULL, 0) // NOLINT (readability/null_usage) +#define NAPI_MODULE_X(modname, regfunc, priv, flags) \ + EXTERN_C_START \ + static napi_module _module = { \ + NAPI_MODULE_VERSION, \ + flags, \ + __FILE__, \ + regfunc, \ + #modname, \ + priv, \ + { 0 }, \ + }; \ + NAPI_C_CTOR(_register_##modname) \ + { \ + napi_module_register(&_module); \ + } \ + EXTERN_C_END + +#define NAPI_MODULE(modname, regfunc) \ + NAPI_MODULE_X(modname, regfunc, NULL, 0) // NOLINT (readability/null_usage) #define NAPI_MODULE_INITIALIZER_BASE napi_register_module_v -#define NAPI_MODULE_INITIALIZER_X(base, version) \ +#define NAPI_MODULE_INITIALIZER_X(base, version) \ NAPI_MODULE_INITIALIZER_X_HELPER(base, version) #define NAPI_MODULE_INITIALIZER_X_HELPER(base, version) base##version -#define NAPI_MODULE_INITIALIZER \ - NAPI_MODULE_INITIALIZER_X(NAPI_MODULE_INITIALIZER_BASE, \ - NAPI_MODULE_VERSION) +#define NAPI_MODULE_INITIALIZER \ + NAPI_MODULE_INITIALIZER_X(NAPI_MODULE_INITIALIZER_BASE, \ + NAPI_MODULE_VERSION) -#define NAPI_MODULE_INIT() \ - EXTERN_C_START \ - NAPI_MODULE_EXPORT napi_value \ - NAPI_MODULE_INITIALIZER(napi_env env, napi_value exports); \ - EXTERN_C_END \ - NAPI_MODULE(NODE_GYP_MODULE_NAME, NAPI_MODULE_INITIALIZER) \ - napi_value NAPI_MODULE_INITIALIZER(napi_env env, \ - napi_value exports) +#define NAPI_MODULE_INIT() \ + EXTERN_C_START \ + NAPI_MODULE_EXPORT napi_value \ + NAPI_MODULE_INITIALIZER(napi_env env, napi_value exports); \ + EXTERN_C_END \ + NAPI_MODULE(NODE_GYP_MODULE_NAME, NAPI_MODULE_INITIALIZER) \ + napi_value NAPI_MODULE_INITIALIZER(napi_env env, \ + napi_value exports) #define NAPI_AUTO_LENGTH SIZE_MAX @@ -130,275 +128,275 @@ NAPI_EXTERN void napi_module_register(napi_module* mod); NAPI_EXTERN napi_status napi_get_last_error_info(napi_env env, - const napi_extended_error_info** result); + const napi_extended_error_info** result); NAPI_EXTERN NAPI_NO_RETURN void napi_fatal_error(const char* location, - size_t location_len, - const char* message, - size_t message_len); + size_t location_len, + const char* message, + size_t message_len); // Getters for defined singletons NAPI_EXTERN napi_status napi_get_undefined(napi_env env, napi_value* result); NAPI_EXTERN napi_status napi_get_null(napi_env env, napi_value* result); NAPI_EXTERN napi_status napi_get_global(napi_env env, napi_value* result); NAPI_EXTERN napi_status napi_get_boolean(napi_env env, - bool value, - napi_value* result); + bool value, + napi_value* result); // Methods to create Primitive types/Objects NAPI_EXTERN napi_status napi_create_object(napi_env env, napi_value* result); NAPI_EXTERN napi_status napi_create_array(napi_env env, napi_value* result); NAPI_EXTERN napi_status napi_create_array_with_length(napi_env env, - size_t length, - napi_value* result); + size_t length, + napi_value* result); NAPI_EXTERN napi_status napi_create_double(napi_env env, - double value, - napi_value* result); + double value, + napi_value* result); NAPI_EXTERN napi_status napi_create_int32(napi_env env, - int32_t value, - napi_value* result); + int32_t value, + napi_value* result); NAPI_EXTERN napi_status napi_create_uint32(napi_env env, - uint32_t value, - napi_value* result); + uint32_t value, + napi_value* result); NAPI_EXTERN napi_status napi_create_int64(napi_env env, - int64_t value, - napi_value* result); + int64_t value, + napi_value* result); NAPI_EXTERN napi_status napi_create_string_latin1(napi_env env, - const char* str, - size_t length, - napi_value* result); + const char* str, + size_t length, + napi_value* result); NAPI_EXTERN napi_status napi_create_string_utf8(napi_env env, - const char* str, - size_t length, - napi_value* result); + const char* str, + size_t length, + napi_value* result); NAPI_EXTERN napi_status napi_create_string_utf16(napi_env env, - const char16_t* str, - size_t length, - napi_value* result); + const char16_t* str, + size_t length, + napi_value* result); NAPI_EXTERN napi_status napi_create_symbol(napi_env env, - napi_value description, - napi_value* result); + napi_value description, + napi_value* result); NAPI_EXTERN napi_status napi_create_function(napi_env env, - const char* utf8name, - size_t length, - napi_callback cb, - void* data, - napi_value* result); + const char* utf8name, + size_t length, + napi_callback cb, + void* data, + napi_value* result); NAPI_EXTERN napi_status napi_create_error(napi_env env, - napi_value code, - napi_value msg, - napi_value* result); + napi_value code, + napi_value msg, + napi_value* result); NAPI_EXTERN napi_status napi_create_type_error(napi_env env, - napi_value code, - napi_value msg, - napi_value* result); + napi_value code, + napi_value msg, + napi_value* result); NAPI_EXTERN napi_status napi_create_range_error(napi_env env, - napi_value code, - napi_value msg, - napi_value* result); + napi_value code, + napi_value msg, + napi_value* result); // Methods to get the native napi_value from Primitive type NAPI_EXTERN napi_status napi_typeof(napi_env env, - napi_value value, - napi_valuetype* result); + napi_value value, + napi_valuetype* result); NAPI_EXTERN napi_status napi_get_value_double(napi_env env, - napi_value value, - double* result); + napi_value value, + double* result); NAPI_EXTERN napi_status napi_get_value_int32(napi_env env, - napi_value value, - int32_t* result); + napi_value value, + int32_t* result); NAPI_EXTERN napi_status napi_get_value_uint32(napi_env env, - napi_value value, - uint32_t* result); + napi_value value, + uint32_t* result); NAPI_EXTERN napi_status napi_get_value_int64(napi_env env, - napi_value value, - int64_t* result); + napi_value value, + int64_t* result); NAPI_EXTERN napi_status napi_get_value_bool(napi_env env, - napi_value value, - bool* result); + napi_value value, + bool* result); // Copies LATIN-1 encoded bytes from a string into a buffer. NAPI_EXTERN napi_status napi_get_value_string_latin1(napi_env env, - napi_value value, - char* buf, - size_t bufsize, - size_t* result); + napi_value value, + char* buf, + size_t bufsize, + size_t* result); // Copies UTF-8 encoded bytes from a string into a buffer. NAPI_EXTERN napi_status napi_get_value_string_utf8(napi_env env, - napi_value value, - char* buf, - size_t bufsize, - size_t* result); + napi_value value, + char* buf, + size_t bufsize, + size_t* result); // Copies UTF-16 encoded bytes from a string into a buffer. NAPI_EXTERN napi_status napi_get_value_string_utf16(napi_env env, - napi_value value, - char16_t* buf, - size_t bufsize, - size_t* result); + napi_value value, + char16_t* buf, + size_t bufsize, + size_t* result); // Methods to coerce values // These APIs may execute user scripts NAPI_EXTERN napi_status napi_coerce_to_bool(napi_env env, - napi_value value, - napi_value* result); + napi_value value, + napi_value* result); NAPI_EXTERN napi_status napi_coerce_to_number(napi_env env, - napi_value value, - napi_value* result); + napi_value value, + napi_value* result); NAPI_EXTERN napi_status napi_coerce_to_object(napi_env env, - napi_value value, - napi_value* result); + napi_value value, + napi_value* result); NAPI_EXTERN napi_status napi_coerce_to_string(napi_env env, - napi_value value, - napi_value* result); + napi_value value, + napi_value* result); // Methods to work with Objects NAPI_EXTERN napi_status napi_get_prototype(napi_env env, - napi_value object, - napi_value* result); + napi_value object, + napi_value* result); NAPI_EXTERN napi_status napi_get_property_names(napi_env env, - napi_value object, - napi_value* result); + napi_value object, + napi_value* result); NAPI_EXTERN napi_status napi_set_property(napi_env env, - napi_value object, - napi_value key, - napi_value value); + napi_value object, + napi_value key, + napi_value value); NAPI_EXTERN napi_status napi_has_property(napi_env env, - napi_value object, - napi_value key, - bool* result); + napi_value object, + napi_value key, + bool* result); NAPI_EXTERN napi_status napi_get_property(napi_env env, - napi_value object, - napi_value key, - napi_value* result); + napi_value object, + napi_value key, + napi_value* result); NAPI_EXTERN napi_status napi_delete_property(napi_env env, - napi_value object, - napi_value key, - bool* result); + napi_value object, + napi_value key, + bool* result); NAPI_EXTERN napi_status napi_has_own_property(napi_env env, - napi_value object, - napi_value key, - bool* result); + napi_value object, + napi_value key, + bool* result); NAPI_EXTERN napi_status napi_set_named_property(napi_env env, - napi_value object, - const char* utf8name, - napi_value value); + napi_value object, + const char* utf8name, + napi_value value); NAPI_EXTERN napi_status napi_has_named_property(napi_env env, - napi_value object, - const char* utf8name, - bool* result); + napi_value object, + const char* utf8name, + bool* result); NAPI_EXTERN napi_status napi_get_named_property(napi_env env, - napi_value object, - const char* utf8name, - napi_value* result); + napi_value object, + const char* utf8name, + napi_value* result); NAPI_EXTERN napi_status napi_set_element(napi_env env, - napi_value object, - uint32_t index, - napi_value value); + napi_value object, + uint32_t index, + napi_value value); NAPI_EXTERN napi_status napi_has_element(napi_env env, - napi_value object, - uint32_t index, - bool* result); + napi_value object, + uint32_t index, + bool* result); NAPI_EXTERN napi_status napi_get_element(napi_env env, - napi_value object, - uint32_t index, - napi_value* result); + napi_value object, + uint32_t index, + napi_value* result); NAPI_EXTERN napi_status napi_delete_element(napi_env env, - napi_value object, - uint32_t index, - bool* result); + napi_value object, + uint32_t index, + bool* result); NAPI_EXTERN napi_status napi_define_properties(napi_env env, - napi_value object, - size_t property_count, - const napi_property_descriptor* properties); + napi_value object, + size_t property_count, + const napi_property_descriptor* properties); // Methods to work with Arrays NAPI_EXTERN napi_status napi_is_array(napi_env env, - napi_value value, - bool* result); + napi_value value, + bool* result); NAPI_EXTERN napi_status napi_get_array_length(napi_env env, - napi_value value, - uint32_t* result); + napi_value value, + uint32_t* result); // Methods to compare values NAPI_EXTERN napi_status napi_strict_equals(napi_env env, - napi_value lhs, - napi_value rhs, - bool* result); + napi_value lhs, + napi_value rhs, + bool* result); // Methods to work with Functions NAPI_EXTERN napi_status napi_call_function(napi_env env, - napi_value recv, - napi_value func, - size_t argc, - const napi_value* argv, - napi_value* result); + napi_value recv, + napi_value func, + size_t argc, + const napi_value* argv, + napi_value* result); NAPI_EXTERN napi_status napi_new_instance(napi_env env, - napi_value constructor, - size_t argc, - const napi_value* argv, - napi_value* result); + napi_value constructor, + size_t argc, + const napi_value* argv, + napi_value* result); NAPI_EXTERN napi_status napi_instanceof(napi_env env, - napi_value object, - napi_value constructor, - bool* result); + napi_value object, + napi_value constructor, + bool* result); // Methods to work with napi_callbacks // Gets all callback info in a single call. (Ugly, but faster.) NAPI_EXTERN napi_status napi_get_cb_info( - napi_env env, // [in] NAPI environment handle - napi_callback_info cbinfo, // [in] Opaque callback-info handle - size_t* argc, // [in-out] Specifies the size of the provided argv array - // and receives the actual count of args. - napi_value* argv, // [out] Array of values - napi_value* this_arg, // [out] Receives the JS 'this' arg for the call - void** data); // [out] Receives the data pointer for the callback. + napi_env env, // [in] NAPI environment handle + napi_callback_info cbinfo, // [in] Opaque callback-info handle + size_t* argc, // [in-out] Specifies the size of the provided argv array + // and receives the actual count of args. + napi_value* argv, // [out] Array of values + napi_value* this_arg, // [out] Receives the JS 'this' arg for the call + void** data); // [out] Receives the data pointer for the callback. NAPI_EXTERN napi_status napi_get_new_target(napi_env env, - napi_callback_info cbinfo, - napi_value* result); + napi_callback_info cbinfo, + napi_value* result); NAPI_EXTERN napi_status napi_define_class(napi_env env, - const char* utf8name, - size_t length, - napi_callback constructor, - void* data, - size_t property_count, - const napi_property_descriptor* properties, - napi_value* result); + const char* utf8name, + size_t length, + napi_callback constructor, + void* data, + size_t property_count, + const napi_property_descriptor* properties, + napi_value* result); // Methods to work with external data objects NAPI_EXTERN napi_status napi_wrap(napi_env env, - napi_value js_object, - void* native_object, - napi_finalize finalize_cb, - void* finalize_hint, - napi_ref* result); + napi_value js_object, + void* native_object, + napi_finalize finalize_cb, + void* finalize_hint, + napi_ref* result); NAPI_EXTERN napi_status napi_unwrap(napi_env env, - napi_value js_object, - void** result); + napi_value js_object, + void** result); NAPI_EXTERN napi_status napi_remove_wrap(napi_env env, - napi_value js_object, - void** result); + napi_value js_object, + void** result); NAPI_EXTERN napi_status napi_create_external(napi_env env, - void* data, - napi_finalize finalize_cb, - void* finalize_hint, - napi_value* result); + void* data, + napi_finalize finalize_cb, + void* finalize_hint, + napi_value* result); NAPI_EXTERN napi_status napi_get_value_external(napi_env env, - napi_value value, - void** result); + napi_value value, + void** result); // Methods to control object lifespan // Set initial_refcount to 0 for a weak reference, >0 for a strong reference. NAPI_EXTERN napi_status napi_create_reference(napi_env env, - napi_value value, - uint32_t initial_refcount, - napi_ref* result); + napi_value value, + uint32_t initial_refcount, + napi_ref* result); // Deletes a reference. The referenced value is released, and may // be GC'd unless there are other references to it. @@ -410,260 +408,260 @@ NAPI_EXTERN napi_status napi_delete_reference(napi_env env, napi_ref ref); // Calling this when the refcount is 0 and the object is unavailable // results in an error. NAPI_EXTERN napi_status napi_reference_ref(napi_env env, - napi_ref ref, - uint32_t* result); + napi_ref ref, + uint32_t* result); // Decrements the reference count, optionally returning the resulting count. // If the result is 0 the reference is now weak and the object may be GC'd // at any time if there are no other references. Calling this when the // refcount is already 0 results in an error. NAPI_EXTERN napi_status napi_reference_unref(napi_env env, - napi_ref ref, - uint32_t* result); + napi_ref ref, + uint32_t* result); // Attempts to get a referenced value. If the reference is weak, // the value might no longer be available, in that case the call // is still successful but the result is NULL. NAPI_EXTERN napi_status napi_get_reference_value(napi_env env, - napi_ref ref, - napi_value* result); + napi_ref ref, + napi_value* result); NAPI_EXTERN napi_status napi_open_handle_scope(napi_env env, - napi_handle_scope* result); + napi_handle_scope* result); NAPI_EXTERN napi_status napi_close_handle_scope(napi_env env, - napi_handle_scope scope); + napi_handle_scope scope); NAPI_EXTERN napi_status napi_open_escapable_handle_scope(napi_env env, - napi_escapable_handle_scope* result); + napi_escapable_handle_scope* result); NAPI_EXTERN napi_status napi_close_escapable_handle_scope(napi_env env, - napi_escapable_handle_scope scope); + napi_escapable_handle_scope scope); NAPI_EXTERN napi_status napi_escape_handle(napi_env env, - napi_escapable_handle_scope scope, - napi_value escapee, - napi_value* result); + napi_escapable_handle_scope scope, + napi_value escapee, + napi_value* result); // Methods to support error handling NAPI_EXTERN napi_status napi_throw(napi_env env, napi_value error); NAPI_EXTERN napi_status napi_throw_error(napi_env env, - const char* code, - const char* msg); + const char* code, + const char* msg); NAPI_EXTERN napi_status napi_throw_type_error(napi_env env, - const char* code, - const char* msg); + const char* code, + const char* msg); NAPI_EXTERN napi_status napi_throw_range_error(napi_env env, - const char* code, - const char* msg); + const char* code, + const char* msg); NAPI_EXTERN napi_status napi_is_error(napi_env env, - napi_value value, - bool* result); + napi_value value, + bool* result); // Methods to support catching exceptions NAPI_EXTERN napi_status napi_is_exception_pending(napi_env env, bool* result); NAPI_EXTERN napi_status napi_get_and_clear_last_exception(napi_env env, - napi_value* result); + napi_value* result); // Methods to provide node::Buffer functionality with napi types NAPI_EXTERN napi_status napi_create_buffer(napi_env env, - size_t length, - void** data, - napi_value* result); + size_t length, + void** data, + napi_value* result); NAPI_EXTERN napi_status napi_create_external_buffer(napi_env env, - size_t length, - void* data, - napi_finalize finalize_cb, - void* finalize_hint, - napi_value* result); + size_t length, + void* data, + napi_finalize finalize_cb, + void* finalize_hint, + napi_value* result); NAPI_EXTERN napi_status napi_create_buffer_copy(napi_env env, - size_t length, - const void* data, - void** result_data, - napi_value* result); + size_t length, + const void* data, + void** result_data, + napi_value* result); NAPI_EXTERN napi_status napi_is_buffer(napi_env env, - napi_value value, - bool* result); + napi_value value, + bool* result); NAPI_EXTERN napi_status napi_get_buffer_info(napi_env env, - napi_value value, - void** data, - size_t* length); + napi_value value, + void** data, + size_t* length); // Methods to work with array buffers and typed arrays NAPI_EXTERN napi_status napi_is_arraybuffer(napi_env env, - napi_value value, - bool* result); + napi_value value, + bool* result); NAPI_EXTERN napi_status napi_create_arraybuffer(napi_env env, - size_t byte_length, - void** data, - napi_value* result); + size_t byte_length, + void** data, + napi_value* result); NAPI_EXTERN napi_status napi_create_external_arraybuffer(napi_env env, - void* external_data, - size_t byte_length, - napi_finalize finalize_cb, - void* finalize_hint, - napi_value* result); + void* external_data, + size_t byte_length, + napi_finalize finalize_cb, + void* finalize_hint, + napi_value* result); NAPI_EXTERN napi_status napi_get_arraybuffer_info(napi_env env, - napi_value arraybuffer, - void** data, - size_t* byte_length); + napi_value arraybuffer, + void** data, + size_t* byte_length); NAPI_EXTERN napi_status napi_is_typedarray(napi_env env, - napi_value value, - bool* result); + napi_value value, + bool* result); NAPI_EXTERN napi_status napi_create_typedarray(napi_env env, - napi_typedarray_type type, - size_t length, - napi_value arraybuffer, - size_t byte_offset, - napi_value* result); + napi_typedarray_type type, + size_t length, + napi_value arraybuffer, + size_t byte_offset, + napi_value* result); NAPI_EXTERN napi_status napi_get_typedarray_info(napi_env env, - napi_value typedarray, - napi_typedarray_type* type, - size_t* length, - void** data, - napi_value* arraybuffer, - size_t* byte_offset); + napi_value typedarray, + napi_typedarray_type* type, + size_t* length, + void** data, + napi_value* arraybuffer, + size_t* byte_offset); NAPI_EXTERN napi_status napi_create_dataview(napi_env env, - size_t length, - napi_value arraybuffer, - size_t byte_offset, - napi_value* result); + size_t length, + napi_value arraybuffer, + size_t byte_offset, + napi_value* result); NAPI_EXTERN napi_status napi_is_dataview(napi_env env, - napi_value value, - bool* result); + napi_value value, + bool* result); NAPI_EXTERN napi_status napi_get_dataview_info(napi_env env, - napi_value dataview, - size_t* bytelength, - void** data, - napi_value* arraybuffer, - size_t* byte_offset); + napi_value dataview, + size_t* bytelength, + void** data, + napi_value* arraybuffer, + size_t* byte_offset); // Methods to manage simple async operations NAPI_EXTERN napi_status napi_create_async_work(napi_env env, - napi_value async_resource, - napi_value async_resource_name, - napi_async_execute_callback execute, - napi_async_complete_callback complete, - void* data, - napi_async_work* result); + napi_value async_resource, + napi_value async_resource_name, + napi_async_execute_callback execute, + napi_async_complete_callback complete, + void* data, + napi_async_work* result); NAPI_EXTERN napi_status napi_delete_async_work(napi_env env, - napi_async_work work); + napi_async_work work); NAPI_EXTERN napi_status napi_queue_async_work(napi_env env, - napi_async_work work); + napi_async_work work); NAPI_EXTERN napi_status napi_cancel_async_work(napi_env env, - napi_async_work work); + napi_async_work work); // Methods for custom handling of async operations NAPI_EXTERN napi_status napi_async_init(napi_env env, - napi_value async_resource, - napi_value async_resource_name, - napi_async_context* result); + napi_value async_resource, + napi_value async_resource_name, + napi_async_context* result); NAPI_EXTERN napi_status napi_async_destroy(napi_env env, - napi_async_context async_context); + napi_async_context async_context); NAPI_EXTERN napi_status napi_make_callback(napi_env env, - napi_async_context async_context, - napi_value recv, - napi_value func, - size_t argc, - const napi_value* argv, - napi_value* result); + napi_async_context async_context, + napi_value recv, + napi_value func, + size_t argc, + const napi_value* argv, + napi_value* result); // version management NAPI_EXTERN napi_status napi_get_version(napi_env env, uint32_t* result); NAPI_EXTERN napi_status napi_get_node_version(napi_env env, - const napi_node_version** version); + const napi_node_version** version); // Promises NAPI_EXTERN napi_status napi_create_promise(napi_env env, - napi_deferred* deferred, - napi_value* promise); + napi_deferred* deferred, + napi_value* promise); NAPI_EXTERN napi_status napi_resolve_deferred(napi_env env, - napi_deferred deferred, - napi_value resolution); + napi_deferred deferred, + napi_value resolution); NAPI_EXTERN napi_status napi_reject_deferred(napi_env env, - napi_deferred deferred, - napi_value rejection); + napi_deferred deferred, + napi_value rejection); NAPI_EXTERN napi_status napi_is_promise(napi_env env, - napi_value promise, - bool* is_promise); + napi_value promise, + bool* is_promise); // Memory management NAPI_EXTERN napi_status napi_adjust_external_memory(napi_env env, - int64_t change_in_bytes, - int64_t* adjusted_value); + int64_t change_in_bytes, + int64_t* adjusted_value); // Running a script NAPI_EXTERN napi_status napi_run_script(napi_env env, - napi_value script, - napi_value* result); + napi_value script, + napi_value* result); #if NAPI_VERSION >= 2 // Return the current libuv event loop for a given environment NAPI_EXTERN napi_status napi_get_uv_event_loop(napi_env env, - struct uv_loop_s** loop); + struct uv_loop_s** loop); -#endif // NAPI_VERSION >= 2 +#endif // NAPI_VERSION >= 2 #if NAPI_VERSION >= 3 NAPI_EXTERN napi_status napi_open_callback_scope(napi_env env, - napi_value resource_object, - napi_async_context context, - napi_callback_scope* result); + napi_value resource_object, + napi_async_context context, + napi_callback_scope* result); NAPI_EXTERN napi_status napi_close_callback_scope(napi_env env, - napi_callback_scope scope); + napi_callback_scope scope); NAPI_EXTERN napi_status napi_fatal_exception(napi_env env, napi_value err); NAPI_EXTERN napi_status napi_add_env_cleanup_hook(napi_env env, - void (*fun)(void* arg), - void* arg); + void (*fun)(void* arg), + void* arg); NAPI_EXTERN napi_status napi_remove_env_cleanup_hook(napi_env env, - void (*fun)(void* arg), - void* arg); + void (*fun)(void* arg), + void* arg); -#endif // NAPI_VERSION >= 3 +#endif // NAPI_VERSION >= 3 #ifdef NAPI_EXPERIMENTAL // Calling into JS from other threads NAPI_EXTERN napi_status napi_create_threadsafe_function(napi_env env, - napi_value func, - napi_value async_resource, - napi_value async_resource_name, - size_t max_queue_size, - size_t initial_thread_count, - void* thread_finalize_data, - napi_finalize thread_finalize_cb, - void* context, - napi_threadsafe_function_call_js call_js_cb, - napi_threadsafe_function* result); + napi_value func, + napi_value async_resource, + napi_value async_resource_name, + size_t max_queue_size, + size_t initial_thread_count, + void* thread_finalize_data, + napi_finalize thread_finalize_cb, + void* context, + napi_threadsafe_function_call_js call_js_cb, + napi_threadsafe_function* result); NAPI_EXTERN napi_status napi_get_threadsafe_function_context(napi_threadsafe_function func, - void** result); + void** result); NAPI_EXTERN napi_status napi_call_threadsafe_function(napi_threadsafe_function func, - void* data, - napi_threadsafe_function_call_mode is_blocking); + void* data, + napi_threadsafe_function_call_mode is_blocking); NAPI_EXTERN napi_status napi_acquire_threadsafe_function(napi_threadsafe_function func); NAPI_EXTERN napi_status napi_release_threadsafe_function(napi_threadsafe_function func, - napi_threadsafe_function_release_mode mode); + napi_threadsafe_function_release_mode mode); NAPI_EXTERN napi_status napi_unref_threadsafe_function(napi_env env, napi_threadsafe_function func); @@ -672,37 +670,44 @@ NAPI_EXTERN napi_status napi_ref_threadsafe_function(napi_env env, napi_threadsafe_function func); NAPI_EXTERN napi_status napi_create_bigint_int64(napi_env env, - int64_t value, - napi_value* result); + int64_t value, + napi_value* result); NAPI_EXTERN napi_status napi_create_bigint_uint64(napi_env env, - uint64_t value, - napi_value* result); + uint64_t value, + napi_value* result); NAPI_EXTERN napi_status napi_create_bigint_words(napi_env env, - int sign_bit, - size_t word_count, - const uint64_t* words, - napi_value* result); + int sign_bit, + size_t word_count, + const uint64_t* words, + napi_value* result); NAPI_EXTERN napi_status napi_get_value_bigint_int64(napi_env env, - napi_value value, - int64_t* result, - bool* lossless); + napi_value value, + int64_t* result, + bool* lossless); NAPI_EXTERN napi_status napi_get_value_bigint_uint64(napi_env env, - napi_value value, - uint64_t* result, - bool* lossless); + napi_value value, + uint64_t* result, + bool* lossless); NAPI_EXTERN napi_status napi_get_value_bigint_words(napi_env env, - napi_value value, - int* sign_bit, - size_t* word_count, - uint64_t* words); + napi_value value, + int* sign_bit, + size_t* word_count, + uint64_t* words); NAPI_EXTERN napi_status napi_add_finalizer(napi_env env, - napi_value js_object, - void* native_object, - napi_finalize finalize_cb, - void* finalize_hint, - napi_ref* result); -#endif // NAPI_EXPERIMENTAL + napi_value js_object, + void* native_object, + napi_finalize finalize_cb, + void* finalize_hint, + napi_ref* result); +#endif // NAPI_EXPERIMENTAL + +NAPI_EXTERN napi_status napi_set_instance_data(napi_env env, + void* data, + napi_finalize finalize_cb, + void* finalize_hint); + +NAPI_EXTERN napi_status napi_get_instance_data(napi_env env, void** data); EXTERN_C_END -#endif // SRC_NODE_API_H_ +#endif // SRC_NODE_API_H_ diff --git a/node/src/node_api_types.h b/node/src/node_api_types.h index 10215d9aa3..193d231fb5 100644 --- a/node/src/node_api_types.h +++ b/node/src/node_api_types.h @@ -5,7 +5,7 @@ #include #if !defined __cplusplus || (defined(_MSC_VER) && _MSC_VER < 1900) - typedef uint16_t char16_t; +typedef uint16_t char16_t; #endif // JSVM API types are all opaque pointers for ABI stability @@ -22,124 +22,124 @@ typedef struct napi_async_work__* napi_async_work; typedef struct napi_deferred__* napi_deferred; #ifdef NAPI_EXPERIMENTAL typedef struct napi_threadsafe_function__* napi_threadsafe_function; -#endif // NAPI_EXPERIMENTAL +#endif // NAPI_EXPERIMENTAL typedef enum { - napi_default = 0, - napi_writable = 1 << 0, - napi_enumerable = 1 << 1, - napi_configurable = 1 << 2, - - // Used with napi_define_class to distinguish static properties - // from instance properties. Ignored by napi_define_properties. - napi_static = 1 << 10, + napi_default = 0, + napi_writable = 1 << 0, + napi_enumerable = 1 << 1, + napi_configurable = 1 << 2, + + // Used with napi_define_class to distinguish static properties + // from instance properties. Ignored by napi_define_properties. + napi_static = 1 << 10, } napi_property_attributes; typedef enum { - // ES6 types (corresponds to typeof) - napi_undefined, - napi_null, - napi_boolean, - napi_number, - napi_string, - napi_symbol, - napi_object, - napi_function, - napi_external, - napi_bigint, + // ES6 types (corresponds to typeof) + napi_undefined, + napi_null, + napi_boolean, + napi_number, + napi_string, + napi_symbol, + napi_object, + napi_function, + napi_external, + napi_bigint, } napi_valuetype; typedef enum { - napi_int8_array, - napi_uint8_array, - napi_uint8_clamped_array, - napi_int16_array, - napi_uint16_array, - napi_int32_array, - napi_uint32_array, - napi_float32_array, - napi_float64_array, - napi_bigint64_array, - napi_biguint64_array, + napi_int8_array, + napi_uint8_array, + napi_uint8_clamped_array, + napi_int16_array, + napi_uint16_array, + napi_int32_array, + napi_uint32_array, + napi_float32_array, + napi_float64_array, + napi_bigint64_array, + napi_biguint64_array, } napi_typedarray_type; typedef enum { - napi_ok, - napi_invalid_arg, - napi_object_expected, - napi_string_expected, - napi_name_expected, - napi_function_expected, - napi_number_expected, - napi_boolean_expected, - napi_array_expected, - napi_generic_failure, - napi_pending_exception, - napi_cancelled, - napi_escape_called_twice, - napi_handle_scope_mismatch, - napi_callback_scope_mismatch, - napi_queue_full, - napi_closing, - napi_bigint_expected, + napi_ok, + napi_invalid_arg, + napi_object_expected, + napi_string_expected, + napi_name_expected, + napi_function_expected, + napi_number_expected, + napi_boolean_expected, + napi_array_expected, + napi_generic_failure, + napi_pending_exception, + napi_cancelled, + napi_escape_called_twice, + napi_handle_scope_mismatch, + napi_callback_scope_mismatch, + napi_queue_full, + napi_closing, + napi_bigint_expected, } napi_status; #ifdef NAPI_EXPERIMENTAL typedef enum { - napi_tsfn_release, - napi_tsfn_abort + napi_tsfn_release, + napi_tsfn_abort } napi_threadsafe_function_release_mode; typedef enum { - napi_tsfn_nonblocking, - napi_tsfn_blocking + napi_tsfn_nonblocking, + napi_tsfn_blocking } napi_threadsafe_function_call_mode; -#endif // NAPI_EXPERIMENTAL +#endif // NAPI_EXPERIMENTAL typedef napi_value (*napi_callback)(napi_env env, - napi_callback_info info); + napi_callback_info info); typedef void (*napi_finalize)(napi_env env, - void* finalize_data, - void* finalize_hint); + void* finalize_data, + void* finalize_hint); typedef void (*napi_async_execute_callback)(napi_env env, - void* data); + void* data); typedef void (*napi_async_complete_callback)(napi_env env, - napi_status status, - void* data); + napi_status status, + void* data); #ifdef NAPI_EXPERIMENTAL typedef void (*napi_threadsafe_function_call_js)(napi_env env, - napi_value js_callback, - void* context, - void* data); -#endif // NAPI_EXPERIMENTAL + napi_value js_callback, + void* context, + void* data); +#endif // NAPI_EXPERIMENTAL typedef struct { - // One of utf8name or name should be NULL. - const char* utf8name; - napi_value name; + // One of utf8name or name should be NULL. + const char* utf8name; + napi_value name; - napi_callback method; - napi_callback getter; - napi_callback setter; - napi_value value; + napi_callback method; + napi_callback getter; + napi_callback setter; + napi_value value; - napi_property_attributes attributes; - void* data; + napi_property_attributes attributes; + void* data; } napi_property_descriptor; typedef struct { - const char* error_message; - void* engine_reserved; - uint32_t engine_error_code; - napi_status error_code; + const char* error_message; + void* engine_reserved; + uint32_t engine_error_code; + napi_status error_code; } napi_extended_error_info; typedef struct { - uint32_t major; - uint32_t minor; - uint32_t patch; - const char* release; + uint32_t major; + uint32_t minor; + uint32_t patch; + const char* release; } napi_node_version; -#endif // SRC_NODE_API_TYPES_H_ +#endif // SRC_NODE_API_TYPES_H_ diff --git a/node/src/node_buffer.cc b/node/src/node_buffer.cc index a90e7a5ede..8f4a12709a 100644 --- a/node/src/node_buffer.cc +++ b/node/src/node_buffer.cc @@ -17,39 +17,40 @@ #define MIN(a, b) ((a) < (b) ? (a) : (b)) -#define THROW_AND_RETURN_IF_OOB(r) \ - do { \ - if (!(r)) return env->ThrowRangeError("out of range index"); \ - } while (0) - -#define THROW_AND_RETURN_UNLESS_BUFFER(env, obj) \ - do { \ - if (!HasInstance(obj)) \ - return env->ThrowTypeError("argument should be a Buffer"); \ - } while (0) - -#define SPREAD_ARG(val, name) \ - CHECK((val)->IsUint8Array()); \ - Local name = (val).As(); \ - ArrayBuffer::Contents name##_c = name->Buffer()->GetContents(); \ - const size_t name##_offset = name->ByteOffset(); \ - const size_t name##_length = name->ByteLength(); \ - char* const name##_data = \ - static_cast(name##_c.Data()) + name##_offset; \ - if (name##_length > 0) \ - CHECK_NE(name##_data, nullptr); - -#define SLICE_START_END(start_arg, end_arg, end_max) \ - size_t start; \ - size_t end; \ - THROW_AND_RETURN_IF_OOB(ParseArrayIndex(start_arg, 0, &start)); \ - THROW_AND_RETURN_IF_OOB(ParseArrayIndex(end_arg, end_max, &end)); \ - if (end < start) end = start; \ - THROW_AND_RETURN_IF_OOB(end <= end_max); \ - size_t length = end - start; - -#define BUFFER_MALLOC(length) \ - zero_fill_all_buffers ? node::Calloc(length, 1) : node::Malloc(length) +#define THROW_AND_RETURN_IF_OOB(r) \ + do { \ + if (!(r)) \ + return env->ThrowRangeError("out of range index"); \ + } while (0) + +#define THROW_AND_RETURN_UNLESS_BUFFER(env, obj) \ + do { \ + if (!HasInstance(obj)) \ + return env->ThrowTypeError("argument should be a Buffer"); \ + } while (0) + +#define SPREAD_ARG(val, name) \ + NODE_CHECK((val)->IsUint8Array()); \ + Local name = (val).As(); \ + ArrayBuffer::Contents name##_c = name->Buffer()->GetContents(); \ + const size_t name##_offset = name->ByteOffset(); \ + const size_t name##_length = name->ByteLength(); \ + char* const name##_data = static_cast(name##_c.Data()) + name##_offset; \ + if (name##_length > 0) \ + NODE_CHECK_NE(name##_data, nullptr); + +#define SLICE_START_END(start_arg, end_arg, end_max) \ + size_t start; \ + size_t end; \ + THROW_AND_RETURN_IF_OOB(ParseArrayIndex(start_arg, 0, &start)); \ + THROW_AND_RETURN_IF_OOB(ParseArrayIndex(end_arg, end_max, &end)); \ + if (end < start) \ + end = start; \ + THROW_AND_RETURN_IF_OOB(end <= end_max); \ + size_t length = end - start; + +#define BUFFER_MALLOC(length) \ + zero_fill_all_buffers ? node::Calloc(length, 1) : node::Malloc(length) namespace node { @@ -58,1217 +59,1206 @@ bool zero_fill_all_buffers = false; namespace Buffer { -using v8::ArrayBuffer; -using v8::ArrayBufferCreationMode; -using v8::Context; -using v8::EscapableHandleScope; -using v8::FunctionCallbackInfo; -using v8::Integer; -using v8::Isolate; -using v8::Local; -using v8::Maybe; -using v8::MaybeLocal; -using v8::Object; -using v8::Persistent; -using v8::String; -using v8::Uint32Array; -using v8::Uint8Array; -using v8::Value; -using v8::WeakCallbackInfo; - -class CallbackInfo { - public: - static inline void Free(char* data, void* hint); - static inline CallbackInfo* New(Isolate* isolate, - Local object, - FreeCallback callback, - char* data, - void* hint = 0); - private: - static void WeakCallback(const WeakCallbackInfo&); - inline void WeakCallback(Isolate* isolate); - inline CallbackInfo(Isolate* isolate, - Local object, - FreeCallback callback, - char* data, - void* hint); - ~CallbackInfo(); - Persistent persistent_; - FreeCallback const callback_; - char* const data_; - void* const hint_; - DISALLOW_COPY_AND_ASSIGN(CallbackInfo); -}; - - -void CallbackInfo::Free(char* data, void*) { - ::free(data); -} - - -CallbackInfo* CallbackInfo::New(Isolate* isolate, - Local object, - FreeCallback callback, - char* data, - void* hint) { - return new CallbackInfo(isolate, object, callback, data, hint); -} - - -CallbackInfo::CallbackInfo(Isolate* isolate, - Local object, - FreeCallback callback, - char* data, - void* hint) - : persistent_(isolate, object), - callback_(callback), - data_(data), - hint_(hint) { - ArrayBuffer::Contents obj_c = object->GetContents(); - CHECK_EQ(data_, static_cast(obj_c.Data())); - if (object->ByteLength() != 0) - CHECK_NE(data_, nullptr); - - persistent_.SetWeak(this, WeakCallback, v8::WeakCallbackType::kParameter); - persistent_.SetWrapperClassId(BUFFER_ID); - persistent_.MarkIndependent(); - isolate->AdjustAmountOfExternalAllocatedMemory(sizeof(*this)); -} - - -CallbackInfo::~CallbackInfo() { - persistent_.Reset(); -} - - -void CallbackInfo::WeakCallback( - const WeakCallbackInfo& data) { - CallbackInfo* self = data.GetParameter(); - self->WeakCallback(data.GetIsolate()); - delete self; -} - - -void CallbackInfo::WeakCallback(Isolate* isolate) { - callback_(data_, hint_); - int64_t change_in_bytes = -static_cast(sizeof(*this)); - isolate->AdjustAmountOfExternalAllocatedMemory(change_in_bytes); -} - - -// Parse index for external array data. -inline MUST_USE_RESULT bool ParseArrayIndex(Local arg, - size_t def, - size_t* ret) { - if (arg->IsUndefined()) { - *ret = def; - return true; - } - - int64_t tmp_i = arg->IntegerValue(); - - if (tmp_i < 0) - return false; - - // Check that the result fits in a size_t. - const uint64_t kSizeMax = static_cast(static_cast(-1)); - // coverity[pointless_expression] - if (static_cast(tmp_i) > kSizeMax) - return false; - - *ret = static_cast(tmp_i); - return true; -} - - -// Buffer methods - -bool HasInstance(Local val) { - return val->IsUint8Array(); -} - - -bool HasInstance(Local obj) { - return obj->IsUint8Array(); -} - - -char* Data(Local val) { - CHECK(val->IsUint8Array()); - Local ui = val.As(); - ArrayBuffer::Contents ab_c = ui->Buffer()->GetContents(); - return static_cast(ab_c.Data()) + ui->ByteOffset(); -} - - -char* Data(Local obj) { - CHECK(obj->IsUint8Array()); - Local ui = obj.As(); - ArrayBuffer::Contents ab_c = ui->Buffer()->GetContents(); - return static_cast(ab_c.Data()) + ui->ByteOffset(); -} - - -size_t Length(Local val) { - CHECK(val->IsUint8Array()); - Local ui = val.As(); - return ui->ByteLength(); -} - - -size_t Length(Local obj) { - CHECK(obj->IsUint8Array()); - Local ui = obj.As(); - return ui->ByteLength(); -} - - -MaybeLocal New(Isolate* isolate, - Local string, - enum encoding enc) { - EscapableHandleScope scope(isolate); - - const size_t length = StringBytes::Size(isolate, string, enc); - size_t actual = 0; - char* data = nullptr; + using v8::ArrayBuffer; + using v8::ArrayBufferCreationMode; + using v8::Context; + using v8::EscapableHandleScope; + using v8::FunctionCallbackInfo; + using v8::Integer; + using v8::Isolate; + using v8::Local; + using v8::Maybe; + using v8::MaybeLocal; + using v8::Object; + using v8::Persistent; + using v8::String; + using v8::Uint32Array; + using v8::Uint8Array; + using v8::Value; + using v8::WeakCallbackInfo; + + class CallbackInfo { + public: + static inline void Free(char* data, void* hint); + static inline CallbackInfo* New(Isolate* isolate, + Local object, + FreeCallback callback, + char* data, + void* hint = 0); + + private: + static void WeakCallback(const WeakCallbackInfo&); + inline void WeakCallback(Isolate* isolate); + inline CallbackInfo(Isolate* isolate, + Local object, + FreeCallback callback, + char* data, + void* hint); + ~CallbackInfo(); + Persistent persistent_; + FreeCallback const callback_; + char* const data_; + void* const hint_; + DISALLOW_COPY_AND_ASSIGN(CallbackInfo); + }; + + void CallbackInfo::Free(char* data, void*) + { + ::free(data); + } - if (length > 0) { - data = static_cast(BUFFER_MALLOC(length)); + CallbackInfo* CallbackInfo::New(Isolate* isolate, + Local object, + FreeCallback callback, + char* data, + void* hint) + { + return new CallbackInfo(isolate, object, callback, data, hint); + } - if (data == nullptr) - return Local(); - - actual = StringBytes::Write(isolate, data, length, string, enc); - CHECK(actual <= length); - - if (actual == 0) { - free(data); - data = nullptr; - } else if (actual < length) { - data = static_cast(node::Realloc(data, actual)); - CHECK_NE(data, nullptr); - } - } - - Local buf; - if (New(isolate, data, actual).ToLocal(&buf)) - return scope.Escape(buf); - - // Object failed to be created. Clean up resources. - free(data); - return Local(); -} - - -MaybeLocal New(Isolate* isolate, size_t length) { - EscapableHandleScope handle_scope(isolate); - Local obj; - if (Buffer::New(Environment::GetCurrent(isolate), length).ToLocal(&obj)) - return handle_scope.Escape(obj); - return Local(); -} - - -MaybeLocal New(Environment* env, size_t length) { - EscapableHandleScope scope(env->isolate()); - - // V8 currently only allows a maximum Typed Array index of max Smi. - if (length > kMaxLength) { - return Local(); - } - - void* data; - if (length > 0) { - data = BUFFER_MALLOC(length); - if (data == nullptr) - return Local(); - } else { - data = nullptr; - } - - Local ab = - ArrayBuffer::New(env->isolate(), - data, - length, - ArrayBufferCreationMode::kInternalized); - Local ui = Uint8Array::New(ab, 0, length); - Maybe mb = - ui->SetPrototype(env->context(), env->buffer_prototype_object()); - if (mb.FromMaybe(false)) - return scope.Escape(ui); - - // Object failed to be created. Clean up resources. - free(data); - return Local(); -} - - -MaybeLocal Copy(Isolate* isolate, const char* data, size_t length) { - EscapableHandleScope handle_scope(isolate); - Environment* env = Environment::GetCurrent(isolate); - Local obj; - if (Buffer::Copy(env, data, length).ToLocal(&obj)) - return handle_scope.Escape(obj); - return Local(); -} - - -MaybeLocal Copy(Environment* env, const char* data, size_t length) { - EscapableHandleScope scope(env->isolate()); - - // V8 currently only allows a maximum Typed Array index of max Smi. - if (length > kMaxLength) { - return Local(); - } - - void* new_data; - if (length > 0) { - CHECK_NE(data, nullptr); - new_data = node::Malloc(length); - if (new_data == nullptr) - return Local(); - memcpy(new_data, data, length); - } else { - new_data = nullptr; - } - - Local ab = - ArrayBuffer::New(env->isolate(), - new_data, - length, - ArrayBufferCreationMode::kInternalized); - Local ui = Uint8Array::New(ab, 0, length); - Maybe mb = - ui->SetPrototype(env->context(), env->buffer_prototype_object()); - if (mb.FromMaybe(false)) - return scope.Escape(ui); - - // Object failed to be created. Clean up resources. - free(new_data); - return Local(); -} - - -MaybeLocal New(Isolate* isolate, - char* data, - size_t length, - FreeCallback callback, - void* hint) { - EscapableHandleScope handle_scope(isolate); - Environment* env = Environment::GetCurrent(isolate); - Local obj; - if (Buffer::New(env, data, length, callback, hint).ToLocal(&obj)) - return handle_scope.Escape(obj); - return Local(); -} - - -MaybeLocal New(Environment* env, - char* data, - size_t length, - FreeCallback callback, - void* hint) { - EscapableHandleScope scope(env->isolate()); - - if (length > kMaxLength) { - return Local(); - } - - Local ab = ArrayBuffer::New(env->isolate(), data, length); - // `Neuter()`ing is required here to prevent materialization of the backing - // store in v8. `nullptr` buffers are not writable, so this is semantically - // correct. - if (data == nullptr) - ab->Neuter(); - Local ui = Uint8Array::New(ab, 0, length); - Maybe mb = - ui->SetPrototype(env->context(), env->buffer_prototype_object()); - - if (!mb.FromMaybe(false)) - return Local(); - - CallbackInfo::New(env->isolate(), ab, callback, data, hint); - return scope.Escape(ui); -} - - -MaybeLocal New(Isolate* isolate, char* data, size_t length) { - EscapableHandleScope handle_scope(isolate); - Environment* env = Environment::GetCurrent(isolate); - Local obj; - if (Buffer::New(env, data, length).ToLocal(&obj)) - return handle_scope.Escape(obj); - return Local(); -} - - -MaybeLocal New(Environment* env, char* data, size_t length) { - EscapableHandleScope scope(env->isolate()); - - if (length > 0) { - CHECK_NE(data, nullptr); - CHECK(length <= kMaxLength); - } - - Local ab = - ArrayBuffer::New(env->isolate(), - data, - length, - ArrayBufferCreationMode::kInternalized); - Local ui = Uint8Array::New(ab, 0, length); - Maybe mb = - ui->SetPrototype(env->context(), env->buffer_prototype_object()); - if (mb.FromMaybe(false)) - return scope.Escape(ui); - return Local(); -} - - -void CreateFromString(const FunctionCallbackInfo& args) { - CHECK(args[0]->IsString()); - CHECK(args[1]->IsString()); - - enum encoding enc = ParseEncoding(args.GetIsolate(), - args[1].As(), - UTF8); - Local buf; - if (New(args.GetIsolate(), args[0].As(), enc).ToLocal(&buf)) - args.GetReturnValue().Set(buf); -} - - -template -void StringSlice(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - Isolate* isolate = env->isolate(); + CallbackInfo::CallbackInfo(Isolate* isolate, + Local object, + FreeCallback callback, + char* data, + void* hint) + : persistent_(isolate, object) + , callback_(callback) + , data_(data) + , hint_(hint) + { + ArrayBuffer::Contents obj_c = object->GetContents(); + NODE_CHECK_EQ(data_, static_cast(obj_c.Data())); + if (object->ByteLength() != 0) + NODE_CHECK_NE(data_, nullptr); + + persistent_.SetWeak(this, WeakCallback, v8::WeakCallbackType::kParameter); + persistent_.SetWrapperClassId(BUFFER_ID); + persistent_.MarkIndependent(); + isolate->AdjustAmountOfExternalAllocatedMemory(sizeof(*this)); + } - THROW_AND_RETURN_UNLESS_BUFFER(env, args.This()); - SPREAD_ARG(args.This(), ts_obj); + CallbackInfo::~CallbackInfo() + { + persistent_.Reset(); + } - if (ts_obj_length == 0) - return args.GetReturnValue().SetEmptyString(); + void CallbackInfo::WeakCallback( + const WeakCallbackInfo& data) + { + CallbackInfo* self = data.GetParameter(); + self->WeakCallback(data.GetIsolate()); + delete self; + } - SLICE_START_END(args[0], args[1], ts_obj_length) - - args.GetReturnValue().Set( - StringBytes::Encode(isolate, ts_obj_data + start, length, encoding)); -} + void CallbackInfo::WeakCallback(Isolate* isolate) + { + callback_(data_, hint_); + int64_t change_in_bytes = -static_cast(sizeof(*this)); + isolate->AdjustAmountOfExternalAllocatedMemory(change_in_bytes); + } + // Parse index for external array data. + inline MUST_USE_RESULT bool ParseArrayIndex(Local arg, + size_t def, + size_t* ret) + { + if (arg->IsUndefined()) { + *ret = def; + return true; + } + + int64_t tmp_i = arg->IntegerValue(); + + if (tmp_i < 0) + return false; + + // Check that the result fits in a size_t. + const uint64_t kSizeMax = static_cast(static_cast(-1)); + // coverity[pointless_expression] + if (static_cast(tmp_i) > kSizeMax) + return false; + + *ret = static_cast(tmp_i); + return true; + } -template <> -void StringSlice(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); + // Buffer methods - THROW_AND_RETURN_UNLESS_BUFFER(env, args.This()); - SPREAD_ARG(args.This(), ts_obj); + bool HasInstance(Local val) + { + return val->IsUint8Array(); + } - if (ts_obj_length == 0) - return args.GetReturnValue().SetEmptyString(); + bool HasInstance(Local obj) + { + return obj->IsUint8Array(); + } - SLICE_START_END(args[0], args[1], ts_obj_length) - length /= 2; + char* Data(Local val) + { + NODE_CHECK(val->IsUint8Array()); + Local ui = val.As(); + ArrayBuffer::Contents ab_c = ui->Buffer()->GetContents(); + return static_cast(ab_c.Data()) + ui->ByteOffset(); + } - const char* data = ts_obj_data + start; - const uint16_t* buf; - bool release = false; + char* Data(Local obj) + { + NODE_CHECK(obj->IsUint8Array()); + Local ui = obj.As(); + ArrayBuffer::Contents ab_c = ui->Buffer()->GetContents(); + return static_cast(ab_c.Data()) + ui->ByteOffset(); + } - // Node's "ucs2" encoding expects LE character data inside a Buffer, so we - // need to reorder on BE platforms. See http://nodejs.org/api/buffer.html - // regarding Node's "ucs2" encoding specification. - const bool aligned = (reinterpret_cast(data) % sizeof(*buf) == 0); - if (IsLittleEndian() && !aligned) { - // Make a copy to avoid unaligned accesses in v8::String::NewFromTwoByte(). - // This applies ONLY to little endian platforms, as misalignment will be - // handled by a byte-swapping operation in StringBytes::Encode on - // big endian platforms. - uint16_t* copy = new uint16_t[length]; - for (size_t i = 0, k = 0; i < length; i += 1, k += 2) { - // Assumes that the input is little endian. - const uint8_t lo = static_cast(data[k + 0]); - const uint8_t hi = static_cast(data[k + 1]); - copy[i] = lo | hi << 8; + size_t Length(Local val) + { + NODE_CHECK(val->IsUint8Array()); + Local ui = val.As(); + return ui->ByteLength(); } - buf = copy; - release = true; - } else { - buf = reinterpret_cast(data); - } - args.GetReturnValue().Set(StringBytes::Encode(env->isolate(), buf, length)); + size_t Length(Local obj) + { + NODE_CHECK(obj->IsUint8Array()); + Local ui = obj.As(); + return ui->ByteLength(); + } - if (release) - delete[] buf; -} + MaybeLocal New(Isolate* isolate, + Local string, + enum encoding enc) + { + EscapableHandleScope scope(isolate); + + const size_t length = StringBytes::Size(isolate, string, enc); + size_t actual = 0; + char* data = nullptr; + + if (length > 0) { + data = static_cast(BUFFER_MALLOC(length)); + + if (data == nullptr) + return Local(); + + actual = StringBytes::Write(isolate, data, length, string, enc); + NODE_CHECK(actual <= length); + + if (actual == 0) { + free(data); + data = nullptr; + } else if (actual < length) { + data = static_cast(node::Realloc(data, actual)); + NODE_CHECK_NE(data, nullptr); + } + } + + Local buf; + if (New(isolate, data, actual).ToLocal(&buf)) + return scope.Escape(buf); + + // Object failed to be created. Clean up resources. + free(data); + return Local(); + } + MaybeLocal New(Isolate* isolate, size_t length) + { + EscapableHandleScope handle_scope(isolate); + Local obj; + if (Buffer::New(Environment::GetCurrent(isolate), length).ToLocal(&obj)) + return handle_scope.Escape(obj); + return Local(); + } -void Latin1Slice(const FunctionCallbackInfo& args) { - StringSlice(args); -} + MaybeLocal New(Environment* env, size_t length) + { + EscapableHandleScope scope(env->isolate()); + + // V8 currently only allows a maximum Typed Array index of max Smi. + if (length > kMaxLength) { + return Local(); + } + + void* data; + if (length > 0) { + data = BUFFER_MALLOC(length); + if (data == nullptr) + return Local(); + } else { + data = nullptr; + } + + Local ab = ArrayBuffer::New(env->isolate(), + data, + length, + ArrayBufferCreationMode::kInternalized); + Local ui = Uint8Array::New(ab, 0, length); + Maybe mb = ui->SetPrototype(env->context(), env->buffer_prototype_object()); + if (mb.FromMaybe(false)) + return scope.Escape(ui); + + // Object failed to be created. Clean up resources. + free(data); + return Local(); + } + MaybeLocal Copy(Isolate* isolate, const char* data, size_t length) + { + EscapableHandleScope handle_scope(isolate); + Environment* env = Environment::GetCurrent(isolate); + Local obj; + if (Buffer::Copy(env, data, length).ToLocal(&obj)) + return handle_scope.Escape(obj); + return Local(); + } -void AsciiSlice(const FunctionCallbackInfo& args) { - StringSlice(args); -} + MaybeLocal Copy(Environment* env, const char* data, size_t length) + { + EscapableHandleScope scope(env->isolate()); + + // V8 currently only allows a maximum Typed Array index of max Smi. + if (length > kMaxLength) { + return Local(); + } + + void* new_data; + if (length > 0) { + NODE_CHECK_NE(data, nullptr); + new_data = node::Malloc(length); + if (new_data == nullptr) + return Local(); + memcpy(new_data, data, length); + } else { + new_data = nullptr; + } + + Local ab = ArrayBuffer::New(env->isolate(), + new_data, + length, + ArrayBufferCreationMode::kInternalized); + Local ui = Uint8Array::New(ab, 0, length); + Maybe mb = ui->SetPrototype(env->context(), env->buffer_prototype_object()); + if (mb.FromMaybe(false)) + return scope.Escape(ui); + + // Object failed to be created. Clean up resources. + free(new_data); + return Local(); + } + MaybeLocal New(Isolate* isolate, + char* data, + size_t length, + FreeCallback callback, + void* hint) + { + EscapableHandleScope handle_scope(isolate); + Environment* env = Environment::GetCurrent(isolate); + Local obj; + if (Buffer::New(env, data, length, callback, hint).ToLocal(&obj)) + return handle_scope.Escape(obj); + return Local(); + } -void Utf8Slice(const FunctionCallbackInfo& args) { - StringSlice(args); -} + MaybeLocal New(Environment* env, + char* data, + size_t length, + FreeCallback callback, + void* hint) + { + EscapableHandleScope scope(env->isolate()); + + if (length > kMaxLength) { + return Local(); + } + + Local ab = ArrayBuffer::New(env->isolate(), data, length); + // `Neuter()`ing is required here to prevent materialization of the backing + // store in v8. `nullptr` buffers are not writable, so this is semantically + // correct. + if (data == nullptr) + ab->Neuter(); + Local ui = Uint8Array::New(ab, 0, length); + Maybe mb = ui->SetPrototype(env->context(), env->buffer_prototype_object()); + + if (!mb.FromMaybe(false)) + return Local(); + + CallbackInfo::New(env->isolate(), ab, callback, data, hint); + return scope.Escape(ui); + } + MaybeLocal New(Isolate* isolate, char* data, size_t length) + { + EscapableHandleScope handle_scope(isolate); + Environment* env = Environment::GetCurrent(isolate); + Local obj; + if (Buffer::New(env, data, length).ToLocal(&obj)) + return handle_scope.Escape(obj); + return Local(); + } -void Ucs2Slice(const FunctionCallbackInfo& args) { - StringSlice(args); -} + MaybeLocal New(Environment* env, char* data, size_t length) + { + EscapableHandleScope scope(env->isolate()); + + if (length > 0) { + NODE_CHECK_NE(data, nullptr); + NODE_CHECK(length <= kMaxLength); + } + + Local ab = ArrayBuffer::New(env->isolate(), + data, + length, + ArrayBufferCreationMode::kInternalized); + Local ui = Uint8Array::New(ab, 0, length); + Maybe mb = ui->SetPrototype(env->context(), env->buffer_prototype_object()); + if (mb.FromMaybe(false)) + return scope.Escape(ui); + return Local(); + } + void CreateFromString(const FunctionCallbackInfo& args) + { + NODE_CHECK(args[0]->IsString()); + NODE_CHECK(args[1]->IsString()); + + enum encoding enc = ParseEncoding(args.GetIsolate(), + args[1].As(), + UTF8); + Local buf; + if (New(args.GetIsolate(), args[0].As(), enc).ToLocal(&buf)) + args.GetReturnValue().Set(buf); + } -void HexSlice(const FunctionCallbackInfo& args) { - StringSlice(args); -} + template + void StringSlice(const FunctionCallbackInfo& args) + { + Environment* env = Environment::GetCurrent(args); + Isolate* isolate = env->isolate(); + THROW_AND_RETURN_UNLESS_BUFFER(env, args.This()); + SPREAD_ARG(args.This(), ts_obj); -void Base64Slice(const FunctionCallbackInfo& args) { - StringSlice(args); -} + if (ts_obj_length == 0) + return args.GetReturnValue().SetEmptyString(); + SLICE_START_END(args[0], args[1], ts_obj_length) -// bytesCopied = buffer.copy(target[, targetStart][, sourceStart][, sourceEnd]); -void Copy(const FunctionCallbackInfo &args) { - Environment* env = Environment::GetCurrent(args); + args.GetReturnValue().Set( + StringBytes::Encode(isolate, ts_obj_data + start, length, encoding)); + } - THROW_AND_RETURN_UNLESS_BUFFER(env, args.This()); - THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]); - Local target_obj = args[0].As(); - SPREAD_ARG(args.This(), ts_obj); - SPREAD_ARG(target_obj, target); + template <> + void StringSlice(const FunctionCallbackInfo& args) + { + Environment* env = Environment::GetCurrent(args); + + THROW_AND_RETURN_UNLESS_BUFFER(env, args.This()); + SPREAD_ARG(args.This(), ts_obj); + + if (ts_obj_length == 0) + return args.GetReturnValue().SetEmptyString(); + + SLICE_START_END(args[0], args[1], ts_obj_length) + length /= 2; + + const char* data = ts_obj_data + start; + const uint16_t* buf; + bool release = false; + + // Node's "ucs2" encoding expects LE character data inside a Buffer, so we + // need to reorder on BE platforms. See http://nodejs.org/api/buffer.html + // regarding Node's "ucs2" encoding specification. + const bool aligned = (reinterpret_cast(data) % sizeof(*buf) == 0); + if (IsLittleEndian() && !aligned) { + // Make a copy to avoid unaligned accesses in v8::String::NewFromTwoByte(). + // This applies ONLY to little endian platforms, as misalignment will be + // handled by a byte-swapping operation in StringBytes::Encode on + // big endian platforms. + uint16_t* copy = new uint16_t[length]; + for (size_t i = 0, k = 0; i < length; i += 1, k += 2) { + // Assumes that the input is little endian. + const uint8_t lo = static_cast(data[k + 0]); + const uint8_t hi = static_cast(data[k + 1]); + copy[i] = lo | hi << 8; + } + buf = copy; + release = true; + } else { + buf = reinterpret_cast(data); + } + + args.GetReturnValue().Set(StringBytes::Encode(env->isolate(), buf, length)); + + if (release) + delete[] buf; + } - size_t target_start; - size_t source_start; - size_t source_end; + void Latin1Slice(const FunctionCallbackInfo& args) + { + StringSlice(args); + } - THROW_AND_RETURN_IF_OOB(ParseArrayIndex(args[1], 0, &target_start)); - THROW_AND_RETURN_IF_OOB(ParseArrayIndex(args[2], 0, &source_start)); - THROW_AND_RETURN_IF_OOB(ParseArrayIndex(args[3], ts_obj_length, &source_end)); + void AsciiSlice(const FunctionCallbackInfo& args) + { + StringSlice(args); + } - // Copy 0 bytes; we're done - if (target_start >= target_length || source_start >= source_end) - return args.GetReturnValue().Set(0); + void Utf8Slice(const FunctionCallbackInfo& args) + { + StringSlice(args); + } - if (source_start > ts_obj_length) - return env->ThrowRangeError("out of range index"); - - if (source_end - source_start > target_length - target_start) - source_end = source_start + target_length - target_start; - - uint32_t to_copy = MIN(MIN(source_end - source_start, - target_length - target_start), - ts_obj_length - source_start); - - memmove(target_data + target_start, ts_obj_data + source_start, to_copy); - args.GetReturnValue().Set(to_copy); -} + void Ucs2Slice(const FunctionCallbackInfo& args) + { + StringSlice(args); + } + void HexSlice(const FunctionCallbackInfo& args) + { + StringSlice(args); + } -void Fill(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); + void Base64Slice(const FunctionCallbackInfo& args) + { + StringSlice(args); + } - THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]); - SPREAD_ARG(args[0], ts_obj); + // bytesCopied = buffer.copy(target[, targetStart][, sourceStart][, sourceEnd]); + void Copy(const FunctionCallbackInfo& args) + { + Environment* env = Environment::GetCurrent(args); - size_t start = args[2]->Uint32Value(); - size_t end = args[3]->Uint32Value(); - size_t fill_length = end - start; - Local str_obj; - size_t str_length; - enum encoding enc; - CHECK(fill_length + start <= ts_obj_length); + THROW_AND_RETURN_UNLESS_BUFFER(env, args.This()); + THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]); + Local target_obj = args[0].As(); + SPREAD_ARG(args.This(), ts_obj); + SPREAD_ARG(target_obj, target); - // First check if Buffer has been passed. - if (Buffer::HasInstance(args[1])) { - SPREAD_ARG(args[1], fill_obj); - str_length = fill_obj_length; - memcpy(ts_obj_data + start, fill_obj_data, MIN(str_length, fill_length)); - goto start_fill; - } + size_t target_start; + size_t source_start; + size_t source_end; - // Then coerce everything that's not a string. - if (!args[1]->IsString()) { - int value = args[1]->Uint32Value() & 255; - memset(ts_obj_data + start, value, fill_length); - return; - } + THROW_AND_RETURN_IF_OOB(ParseArrayIndex(args[1], 0, &target_start)); + THROW_AND_RETURN_IF_OOB(ParseArrayIndex(args[2], 0, &source_start)); + THROW_AND_RETURN_IF_OOB(ParseArrayIndex(args[3], ts_obj_length, &source_end)); - str_obj = args[1]->ToString(env->isolate()); - enc = ParseEncoding(env->isolate(), args[4], UTF8); - str_length = - enc == UTF8 ? str_obj->Utf8Length() : - enc == UCS2 ? str_obj->Length() * sizeof(uint16_t) : str_obj->Length(); + // Copy 0 bytes; we're done + if (target_start >= target_length || source_start >= source_end) + return args.GetReturnValue().Set(0); - if (enc == HEX && str_length % 2 != 0) - return env->ThrowTypeError("Invalid hex string"); + if (source_start > ts_obj_length) + return env->ThrowRangeError("out of range index"); - if (str_length == 0) - return; + if (source_end - source_start > target_length - target_start) + source_end = source_start + target_length - target_start; - // Can't use StringBytes::Write() in all cases. For example if attempting - // to write a two byte character into a one byte Buffer. - if (enc == UTF8) { - node::Utf8Value str(env->isolate(), args[1]); - memcpy(ts_obj_data + start, *str, MIN(str_length, fill_length)); + uint32_t to_copy = MIN(MIN(source_end - source_start, + target_length - target_start), + ts_obj_length - source_start); - } else if (enc == UCS2) { - node::TwoByteValue str(env->isolate(), args[1]); - memcpy(ts_obj_data + start, *str, MIN(str_length, fill_length)); + memmove(target_data + target_start, ts_obj_data + source_start, to_copy); + args.GetReturnValue().Set(to_copy); + } - } else { - // Write initial String to Buffer, then use that memory to copy remainder - // of string. Correct the string length for cases like HEX where less than - // the total string length is written. - str_length = StringBytes::Write(env->isolate(), - ts_obj_data + start, - fill_length, - str_obj, - enc, - nullptr); - // This check is also needed in case Write() returns that no bytes could - // be written. - // TODO(trevnorris): Should this throw? Because of the string length was - // greater than 0 but couldn't be written then the string was invalid. - if (str_length == 0) - return; - } + void Fill(const FunctionCallbackInfo& args) + { + Environment* env = Environment::GetCurrent(args); + + THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]); + SPREAD_ARG(args[0], ts_obj); + + size_t start = args[2]->Uint32Value(); + size_t end = args[3]->Uint32Value(); + size_t fill_length = end - start; + Local str_obj; + size_t str_length; + enum encoding enc; + NODE_CHECK(fill_length + start <= ts_obj_length); + + // First check if Buffer has been passed. + if (Buffer::HasInstance(args[1])) { + SPREAD_ARG(args[1], fill_obj); + str_length = fill_obj_length; + memcpy(ts_obj_data + start, fill_obj_data, MIN(str_length, fill_length)); + goto start_fill; + } + + // Then coerce everything that's not a string. + if (!args[1]->IsString()) { + int value = args[1]->Uint32Value() & 255; + memset(ts_obj_data + start, value, fill_length); + return; + } + + str_obj = args[1]->ToString(env->isolate()); + enc = ParseEncoding(env->isolate(), args[4], UTF8); + str_length = enc == UTF8 ? str_obj->Utf8Length() : enc == UCS2 ? str_obj->Length() * sizeof(uint16_t) : str_obj->Length(); + + if (enc == HEX && str_length % 2 != 0) + return env->ThrowTypeError("Invalid hex string"); + + if (str_length == 0) + return; + + // Can't use StringBytes::Write() in all cases. For example if attempting + // to write a two byte character into a one byte Buffer. + if (enc == UTF8) { + node::Utf8Value str(env->isolate(), args[1]); + memcpy(ts_obj_data + start, *str, MIN(str_length, fill_length)); + + } else if (enc == UCS2) { + node::TwoByteValue str(env->isolate(), args[1]); + memcpy(ts_obj_data + start, *str, MIN(str_length, fill_length)); + + } else { + // Write initial String to Buffer, then use that memory to copy remainder + // of string. Correct the string length for cases like HEX where less than + // the total string length is written. + str_length = StringBytes::Write(env->isolate(), + ts_obj_data + start, + fill_length, + str_obj, + enc, + nullptr); + // This check is also needed in case Write() returns that no bytes could + // be written. + // TODO(trevnorris): Should this throw? Because of the string length was + // greater than 0 but couldn't be written then the string was invalid. + if (str_length == 0) + return; + } + + start_fill: + + if (str_length >= fill_length) + return; + + size_t in_there = str_length; + char* ptr = ts_obj_data + start + str_length; + + while (in_there < fill_length - in_there) { + memcpy(ptr, ts_obj_data + start, in_there); + ptr += in_there; + in_there *= 2; + } + + if (in_there < fill_length) { + memcpy(ptr, ts_obj_data + start, fill_length - in_there); + } + } - start_fill: + template + void StringWrite(const FunctionCallbackInfo& args) + { + Environment* env = Environment::GetCurrent(args); - if (str_length >= fill_length) - return; + THROW_AND_RETURN_UNLESS_BUFFER(env, args.This()); + SPREAD_ARG(args.This(), ts_obj); + if (!args[0]->IsString()) + return env->ThrowTypeError("Argument must be a string"); - size_t in_there = str_length; - char* ptr = ts_obj_data + start + str_length; + Local str = args[0]->ToString(env->isolate()); - while (in_there < fill_length - in_there) { - memcpy(ptr, ts_obj_data + start, in_there); - ptr += in_there; - in_there *= 2; - } + if (encoding == HEX && str->Length() % 2 != 0) + return env->ThrowTypeError("Invalid hex string"); - if (in_there < fill_length) { - memcpy(ptr, ts_obj_data + start, fill_length - in_there); - } -} + size_t offset; + size_t max_length; + THROW_AND_RETURN_IF_OOB(ParseArrayIndex(args[1], 0, &offset)); + if (offset > ts_obj_length) + return env->ThrowRangeError("Offset is out of bounds"); -template -void StringWrite(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); + THROW_AND_RETURN_IF_OOB(ParseArrayIndex(args[2], ts_obj_length - offset, + &max_length)); - THROW_AND_RETURN_UNLESS_BUFFER(env, args.This()); - SPREAD_ARG(args.This(), ts_obj); + max_length = MIN(ts_obj_length - offset, max_length); - if (!args[0]->IsString()) - return env->ThrowTypeError("Argument must be a string"); + if (max_length == 0) + return args.GetReturnValue().Set(0); - Local str = args[0]->ToString(env->isolate()); + uint32_t written = StringBytes::Write(env->isolate(), + ts_obj_data + offset, + max_length, + str, + encoding, + nullptr); + args.GetReturnValue().Set(written); + } - if (encoding == HEX && str->Length() % 2 != 0) - return env->ThrowTypeError("Invalid hex string"); + void Base64Write(const FunctionCallbackInfo& args) + { + StringWrite(args); + } - size_t offset; - size_t max_length; + void Latin1Write(const FunctionCallbackInfo& args) + { + StringWrite(args); + } - THROW_AND_RETURN_IF_OOB(ParseArrayIndex(args[1], 0, &offset)); - if (offset > ts_obj_length) - return env->ThrowRangeError("Offset is out of bounds"); + void Utf8Write(const FunctionCallbackInfo& args) + { + StringWrite(args); + } - THROW_AND_RETURN_IF_OOB(ParseArrayIndex(args[2], ts_obj_length - offset, - &max_length)); + void Ucs2Write(const FunctionCallbackInfo& args) + { + StringWrite(args); + } - max_length = MIN(ts_obj_length - offset, max_length); + void HexWrite(const FunctionCallbackInfo& args) + { + StringWrite(args); + } - if (max_length == 0) - return args.GetReturnValue().Set(0); + void AsciiWrite(const FunctionCallbackInfo& args) + { + StringWrite(args); + } - uint32_t written = StringBytes::Write(env->isolate(), - ts_obj_data + offset, - max_length, - str, - encoding, - nullptr); - args.GetReturnValue().Set(written); -} + static inline void Swizzle(char* start, unsigned int len) + { + char* end = start + len - 1; + while (start < end) { + char tmp = *start; + *start++ = *end; + *end-- = tmp; + } + } + template + void ReadFloatGeneric(const FunctionCallbackInfo& args) + { + THROW_AND_RETURN_UNLESS_BUFFER(Environment::GetCurrent(args), args[0]); + SPREAD_ARG(args[0], ts_obj); -void Base64Write(const FunctionCallbackInfo& args) { - StringWrite(args); -} + uint32_t offset = args[1]->Uint32Value(); + NODE_CHECK_LE(offset + sizeof(T), ts_obj_length); + union NoAlias { + T val; + char bytes[sizeof(T)]; + }; -void Latin1Write(const FunctionCallbackInfo& args) { - StringWrite(args); -} + union NoAlias na; + const char* ptr = static_cast(ts_obj_data) + offset; + memcpy(na.bytes, ptr, sizeof(na.bytes)); + if (endianness != GetEndianness()) + Swizzle(na.bytes, sizeof(na.bytes)); + args.GetReturnValue().Set(na.val); + } -void Utf8Write(const FunctionCallbackInfo& args) { - StringWrite(args); -} + void ReadFloatLE(const FunctionCallbackInfo& args) + { + ReadFloatGeneric(args); + } + void ReadFloatBE(const FunctionCallbackInfo& args) + { + ReadFloatGeneric(args); + } -void Ucs2Write(const FunctionCallbackInfo& args) { - StringWrite(args); -} + void ReadDoubleLE(const FunctionCallbackInfo& args) + { + ReadFloatGeneric(args); + } + void ReadDoubleBE(const FunctionCallbackInfo& args) + { + ReadFloatGeneric(args); + } -void HexWrite(const FunctionCallbackInfo& args) { - StringWrite(args); -} + template + void WriteFloatGeneric(const FunctionCallbackInfo& args) + { + Environment* env = Environment::GetCurrent(args); + bool should_NODE_ASSERT = args.Length() < 4; -void AsciiWrite(const FunctionCallbackInfo& args) { - StringWrite(args); -} + if (should_NODE_ASSERT) { + THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]); + } + Local ts_obj = args[0].As(); + ArrayBuffer::Contents ts_obj_c = ts_obj->Buffer()->GetContents(); + const size_t ts_obj_offset = ts_obj->ByteOffset(); + const size_t ts_obj_length = ts_obj->ByteLength(); + char* const ts_obj_data = static_cast(ts_obj_c.Data()) + ts_obj_offset; + if (ts_obj_length > 0) + NODE_CHECK_NE(ts_obj_data, nullptr); -static inline void Swizzle(char* start, unsigned int len) { - char* end = start + len - 1; - while (start < end) { - char tmp = *start; - *start++ = *end; - *end-- = tmp; - } -} + T val = args[1]->NumberValue(env->context()).FromMaybe(0); + size_t offset = args[2]->IntegerValue(env->context()).FromMaybe(0); + size_t memcpy_num = sizeof(T); -template -void ReadFloatGeneric(const FunctionCallbackInfo& args) { - THROW_AND_RETURN_UNLESS_BUFFER(Environment::GetCurrent(args), args[0]); - SPREAD_ARG(args[0], ts_obj); + if (should_NODE_ASSERT) { + THROW_AND_RETURN_IF_OOB(offset + memcpy_num >= memcpy_num); + THROW_AND_RETURN_IF_OOB(offset + memcpy_num <= ts_obj_length); + } - uint32_t offset = args[1]->Uint32Value(); - CHECK_LE(offset + sizeof(T), ts_obj_length); + if (offset + memcpy_num > ts_obj_length) + memcpy_num = ts_obj_length - offset; - union NoAlias { - T val; - char bytes[sizeof(T)]; - }; + union NoAlias { + T val; + char bytes[sizeof(T)]; + }; - union NoAlias na; - const char* ptr = static_cast(ts_obj_data) + offset; - memcpy(na.bytes, ptr, sizeof(na.bytes)); - if (endianness != GetEndianness()) - Swizzle(na.bytes, sizeof(na.bytes)); + union NoAlias na = { val }; + char* ptr = static_cast(ts_obj_data) + offset; + if (endianness != GetEndianness()) + Swizzle(na.bytes, sizeof(na.bytes)); + memcpy(ptr, na.bytes, memcpy_num); + } - args.GetReturnValue().Set(na.val); -} + void WriteFloatLE(const FunctionCallbackInfo& args) + { + WriteFloatGeneric(args); + } + void WriteFloatBE(const FunctionCallbackInfo& args) + { + WriteFloatGeneric(args); + } -void ReadFloatLE(const FunctionCallbackInfo& args) { - ReadFloatGeneric(args); -} + void WriteDoubleLE(const FunctionCallbackInfo& args) + { + WriteFloatGeneric(args); + } + void WriteDoubleBE(const FunctionCallbackInfo& args) + { + WriteFloatGeneric(args); + } -void ReadFloatBE(const FunctionCallbackInfo& args) { - ReadFloatGeneric(args); -} + void ByteLengthUtf8(const FunctionCallbackInfo& args) + { + NODE_CHECK(args[0]->IsString()); + // Fast case: avoid StringBytes on UTF8 string. Jump to v8. + args.GetReturnValue().Set(args[0].As()->Utf8Length()); + } -void ReadDoubleLE(const FunctionCallbackInfo& args) { - ReadFloatGeneric(args); -} + // Normalize val to be an integer in the range of [1, -1] since + // implementations of memcmp() can vary by platform. + static int normalizeCompareVal(int val, size_t a_length, size_t b_length) + { + if (val == 0) { + if (a_length > b_length) + return 1; + else if (a_length < b_length) + return -1; + } else { + if (val > 0) + return 1; + else + return -1; + } + return val; + } + void CompareOffset(const FunctionCallbackInfo& args) + { + Environment* env = Environment::GetCurrent(args); + + THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]); + THROW_AND_RETURN_UNLESS_BUFFER(env, args[1]); + SPREAD_ARG(args[0], ts_obj); + SPREAD_ARG(args[1], target); + + size_t target_start; + size_t source_start; + size_t source_end; + size_t target_end; + + THROW_AND_RETURN_IF_OOB(ParseArrayIndex(args[2], 0, &target_start)); + THROW_AND_RETURN_IF_OOB(ParseArrayIndex(args[3], 0, &source_start)); + THROW_AND_RETURN_IF_OOB(ParseArrayIndex(args[4], target_length, &target_end)); + THROW_AND_RETURN_IF_OOB(ParseArrayIndex(args[5], ts_obj_length, &source_end)); + + if (source_start > ts_obj_length) + return env->ThrowRangeError("out of range index"); + if (target_start > target_length) + return env->ThrowRangeError("out of range index"); + + NODE_CHECK_LE(source_start, source_end); + NODE_CHECK_LE(target_start, target_end); + + size_t to_cmp = MIN(MIN(source_end - source_start, + target_end - target_start), + ts_obj_length - source_start); + + int val = normalizeCompareVal(to_cmp > 0 ? memcmp(ts_obj_data + source_start, + target_data + target_start, + to_cmp) + : 0, + source_end - source_start, + target_end - target_start); + + args.GetReturnValue().Set(val); + } -void ReadDoubleBE(const FunctionCallbackInfo& args) { - ReadFloatGeneric(args); -} + void Compare(const FunctionCallbackInfo& args) + { + Environment* env = Environment::GetCurrent(args); + THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]); + THROW_AND_RETURN_UNLESS_BUFFER(env, args[1]); + SPREAD_ARG(args[0], obj_a); + SPREAD_ARG(args[1], obj_b); -template -void WriteFloatGeneric(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); + size_t cmp_length = MIN(obj_a_length, obj_b_length); - bool should_assert = args.Length() < 4; + int val = normalizeCompareVal(cmp_length > 0 ? memcmp(obj_a_data, obj_b_data, cmp_length) : 0, + obj_a_length, obj_b_length); + args.GetReturnValue().Set(val); + } - if (should_assert) { - THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]); - } + // Computes the offset for starting an indexOf or lastIndexOf search. + // Returns either a valid offset in [0...], ie inside the Buffer, + // or -1 to signal that there is no possible match. + int64_t IndexOfOffset(size_t length, int64_t offset_i64, bool is_forward) + { + int64_t length_i64 = static_cast(length); + if (length_i64 == 0) { + // Empty buffer, no match. + return -1; + } + if (offset_i64 < 0) { + if (offset_i64 + length_i64 >= 0) { + // Negative offsets count backwards from the end of the buffer. + return length_i64 + offset_i64; + } else if (is_forward) { + // indexOf from before the start of the buffer: search the whole buffer. + return 0; + } else { + // lastIndexOf from before the start of the buffer: no match. + return -1; + } + } else { + if (offset_i64 < length_i64) { + // Valid positive offset. + return offset_i64; + } else if (is_forward) { + // indexOf from past the end of the buffer: no match. + return -1; + } else { + // lastIndexOf from past the end of the buffer: search the whole buffer. + return length_i64 - 1; + } + } + } - Local ts_obj = args[0].As(); - ArrayBuffer::Contents ts_obj_c = ts_obj->Buffer()->GetContents(); - const size_t ts_obj_offset = ts_obj->ByteOffset(); - const size_t ts_obj_length = ts_obj->ByteLength(); - char* const ts_obj_data = - static_cast(ts_obj_c.Data()) + ts_obj_offset; - if (ts_obj_length > 0) - CHECK_NE(ts_obj_data, nullptr); + void IndexOfString(const FunctionCallbackInfo& args) + { + NODE_ASSERT(args[1]->IsString()); + NODE_ASSERT(args[2]->IsNumber()); + NODE_ASSERT(args[4]->IsBoolean()); + + enum encoding enc = ParseEncoding(args.GetIsolate(), + args[3], + UTF8); + + THROW_AND_RETURN_UNLESS_BUFFER(Environment::GetCurrent(args), args[0]); + SPREAD_ARG(args[0], ts_obj); + + Local needle = args[1].As(); + int64_t offset_i64 = args[2]->IntegerValue(); + bool is_forward = args[4]->IsTrue(); + + const char* haystack = ts_obj_data; + // Round down to the nearest multiple of 2 in case of UCS2. + const size_t haystack_length = (enc == UCS2) ? ts_obj_length & ~1 : ts_obj_length; // NOLINT(whitespace/operators) + + const size_t needle_length = StringBytes::Size(args.GetIsolate(), needle, enc); + + if (needle_length == 0 || haystack_length == 0) { + return args.GetReturnValue().Set(-1); + } + + int64_t opt_offset = IndexOfOffset(haystack_length, offset_i64, is_forward); + if (opt_offset <= -1) { + return args.GetReturnValue().Set(-1); + } + size_t offset = static_cast(opt_offset); + NODE_CHECK_LT(offset, haystack_length); + if ((is_forward && needle_length + offset > haystack_length) || needle_length > haystack_length) { + return args.GetReturnValue().Set(-1); + } + + size_t result = haystack_length; + + if (enc == UCS2) { + String::Value needle_value(args.GetIsolate(), needle); + if (*needle_value == nullptr) + return args.GetReturnValue().Set(-1); + + if (haystack_length < 2 || needle_value.length() < 1) { + return args.GetReturnValue().Set(-1); + } + + if (IsBigEndian()) { + StringBytes::InlineDecoder decoder; + decoder.Decode(Environment::GetCurrent(args), needle, args[3], UCS2); + const uint16_t* decoded_string = reinterpret_cast(decoder.out()); + + if (decoded_string == nullptr) + return args.GetReturnValue().Set(-1); + + result = SearchString(reinterpret_cast(haystack), + haystack_length / 2, + decoded_string, + decoder.size() / 2, + offset / 2, + is_forward); + } else { + result = SearchString(reinterpret_cast(haystack), + haystack_length / 2, + reinterpret_cast(*needle_value), + needle_value.length(), + offset / 2, + is_forward); + } + result *= 2; + } else if (enc == UTF8) { + String::Utf8Value needle_value(needle); + if (*needle_value == nullptr) + return args.GetReturnValue().Set(-1); + + result = SearchString(reinterpret_cast(haystack), + haystack_length, + reinterpret_cast(*needle_value), + needle_length, + offset, + is_forward); + } else if (enc == LATIN1) { + uint8_t* needle_data = static_cast(node::Malloc(needle_length)); + if (needle_data == nullptr) { + return args.GetReturnValue().Set(-1); + } + needle->WriteOneByte( + needle_data, 0, needle_length, String::NO_NULL_TERMINATION); + + result = SearchString(reinterpret_cast(haystack), + haystack_length, + needle_data, + needle_length, + offset, + is_forward); + free(needle_data); + } + + args.GetReturnValue().Set( + result == haystack_length ? -1 : static_cast(result)); + } - T val = args[1]->NumberValue(env->context()).FromMaybe(0); - size_t offset = args[2]->IntegerValue(env->context()).FromMaybe(0); + void IndexOfBuffer(const FunctionCallbackInfo& args) + { + NODE_ASSERT(args[1]->IsObject()); + NODE_ASSERT(args[2]->IsNumber()); + NODE_ASSERT(args[4]->IsBoolean()); + + enum encoding enc = ParseEncoding(args.GetIsolate(), + args[3], + UTF8); + + THROW_AND_RETURN_UNLESS_BUFFER(Environment::GetCurrent(args), args[0]); + THROW_AND_RETURN_UNLESS_BUFFER(Environment::GetCurrent(args), args[1]); + SPREAD_ARG(args[0], ts_obj); + SPREAD_ARG(args[1], buf); + int64_t offset_i64 = args[2]->IntegerValue(); + bool is_forward = args[4]->IsTrue(); + + const char* haystack = ts_obj_data; + const size_t haystack_length = ts_obj_length; + const char* needle = buf_data; + const size_t needle_length = buf_length; + + if (needle_length == 0 || haystack_length == 0) { + return args.GetReturnValue().Set(-1); + } + + int64_t opt_offset = IndexOfOffset(haystack_length, offset_i64, is_forward); + if (opt_offset <= -1) { + return args.GetReturnValue().Set(-1); + } + size_t offset = static_cast(opt_offset); + NODE_CHECK_LT(offset, haystack_length); + if ((is_forward && needle_length + offset > haystack_length) || needle_length > haystack_length) { + return args.GetReturnValue().Set(-1); + } + + size_t result = haystack_length; + + if (enc == UCS2) { + if (haystack_length < 2 || needle_length < 2) { + return args.GetReturnValue().Set(-1); + } + result = SearchString( + reinterpret_cast(haystack), + haystack_length / 2, + reinterpret_cast(needle), + needle_length / 2, + offset / 2, + is_forward); + result *= 2; + } else { + result = SearchString( + reinterpret_cast(haystack), + haystack_length, + reinterpret_cast(needle), + needle_length, + offset, + is_forward); + } + + args.GetReturnValue().Set( + result == haystack_length ? -1 : static_cast(result)); + } - size_t memcpy_num = sizeof(T); + void IndexOfNumber(const FunctionCallbackInfo& args) + { + NODE_ASSERT(args[1]->IsNumber()); + NODE_ASSERT(args[2]->IsNumber()); + NODE_ASSERT(args[3]->IsBoolean()); + + THROW_AND_RETURN_UNLESS_BUFFER(Environment::GetCurrent(args), args[0]); + SPREAD_ARG(args[0], ts_obj); + + uint32_t needle = args[1]->Uint32Value(); + int64_t offset_i64 = args[2]->IntegerValue(); + bool is_forward = args[3]->IsTrue(); + + int64_t opt_offset = IndexOfOffset(ts_obj_length, offset_i64, is_forward); + if (opt_offset <= -1) { + return args.GetReturnValue().Set(-1); + } + size_t offset = static_cast(opt_offset); + NODE_CHECK_LT(offset, ts_obj_length); + + const void* ptr; + if (is_forward) { + ptr = memchr(ts_obj_data + offset, needle, ts_obj_length - offset); + } else { + ptr = node::stringsearch::MemrchrFill(ts_obj_data, needle, offset + 1); + } + const char* ptr_char = static_cast(ptr); + args.GetReturnValue().Set(ptr ? static_cast(ptr_char - ts_obj_data) + : -1); + } - if (should_assert) { - THROW_AND_RETURN_IF_OOB(offset + memcpy_num >= memcpy_num); - THROW_AND_RETURN_IF_OOB(offset + memcpy_num <= ts_obj_length); - } + void Swap16(const FunctionCallbackInfo& args) + { + Environment* env = Environment::GetCurrent(args); + THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]); + SPREAD_ARG(args[0], ts_obj); + SwapBytes16(ts_obj_data, ts_obj_length); + args.GetReturnValue().Set(args[0]); + } - if (offset + memcpy_num > ts_obj_length) - memcpy_num = ts_obj_length - offset; + void Swap32(const FunctionCallbackInfo& args) + { + Environment* env = Environment::GetCurrent(args); + THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]); + SPREAD_ARG(args[0], ts_obj); + SwapBytes32(ts_obj_data, ts_obj_length); + args.GetReturnValue().Set(args[0]); + } - union NoAlias { - T val; - char bytes[sizeof(T)]; - }; + void Swap64(const FunctionCallbackInfo& args) + { + Environment* env = Environment::GetCurrent(args); + THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]); + SPREAD_ARG(args[0], ts_obj); + SwapBytes64(ts_obj_data, ts_obj_length); + args.GetReturnValue().Set(args[0]); + } - union NoAlias na = { val }; - char* ptr = static_cast(ts_obj_data) + offset; - if (endianness != GetEndianness()) - Swizzle(na.bytes, sizeof(na.bytes)); - memcpy(ptr, na.bytes, memcpy_num); -} + // pass Buffer object to load prototype methods + void SetupBufferJS(const FunctionCallbackInfo& args) + { + Environment* env = Environment::GetCurrent(args); + NODE_CHECK(args[0]->IsObject()); + Local proto = args[0].As(); + env->set_buffer_prototype_object(proto); -void WriteFloatLE(const FunctionCallbackInfo& args) { - WriteFloatGeneric(args); -} + env->SetMethod(proto, "asciiSlice", AsciiSlice); + env->SetMethod(proto, "base64Slice", Base64Slice); + env->SetMethod(proto, "latin1Slice", Latin1Slice); + env->SetMethod(proto, "hexSlice", HexSlice); + env->SetMethod(proto, "ucs2Slice", Ucs2Slice); + env->SetMethod(proto, "utf8Slice", Utf8Slice); + env->SetMethod(proto, "asciiWrite", AsciiWrite); + env->SetMethod(proto, "base64Write", Base64Write); + env->SetMethod(proto, "latin1Write", Latin1Write); + env->SetMethod(proto, "hexWrite", HexWrite); + env->SetMethod(proto, "ucs2Write", Ucs2Write); + env->SetMethod(proto, "utf8Write", Utf8Write); -void WriteFloatBE(const FunctionCallbackInfo& args) { - WriteFloatGeneric(args); -} + env->SetMethod(proto, "copy", Copy); + NODE_CHECK(args[1]->IsObject()); + Local bObj = args[1].As(); -void WriteDoubleLE(const FunctionCallbackInfo& args) { - WriteFloatGeneric(args); -} + uint32_t* const fields = env->array_buffer_allocator_info()->fields(); + uint32_t const fields_count = env->array_buffer_allocator_info()->fields_count(); + Local array_buffer = ArrayBuffer::New(env->isolate(), fields, sizeof(*fields) * fields_count); -void WriteDoubleBE(const FunctionCallbackInfo& args) { - WriteFloatGeneric(args); -} - - -void ByteLengthUtf8(const FunctionCallbackInfo &args) { - CHECK(args[0]->IsString()); - - // Fast case: avoid StringBytes on UTF8 string. Jump to v8. - args.GetReturnValue().Set(args[0].As()->Utf8Length()); -} - -// Normalize val to be an integer in the range of [1, -1] since -// implementations of memcmp() can vary by platform. -static int normalizeCompareVal(int val, size_t a_length, size_t b_length) { - if (val == 0) { - if (a_length > b_length) - return 1; - else if (a_length < b_length) - return -1; - } else { - if (val > 0) - return 1; - else - return -1; - } - return val; -} - -void CompareOffset(const FunctionCallbackInfo &args) { - Environment* env = Environment::GetCurrent(args); - - THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]); - THROW_AND_RETURN_UNLESS_BUFFER(env, args[1]); - SPREAD_ARG(args[0], ts_obj); - SPREAD_ARG(args[1], target); - - size_t target_start; - size_t source_start; - size_t source_end; - size_t target_end; - - THROW_AND_RETURN_IF_OOB(ParseArrayIndex(args[2], 0, &target_start)); - THROW_AND_RETURN_IF_OOB(ParseArrayIndex(args[3], 0, &source_start)); - THROW_AND_RETURN_IF_OOB(ParseArrayIndex(args[4], target_length, &target_end)); - THROW_AND_RETURN_IF_OOB(ParseArrayIndex(args[5], ts_obj_length, &source_end)); - - if (source_start > ts_obj_length) - return env->ThrowRangeError("out of range index"); - if (target_start > target_length) - return env->ThrowRangeError("out of range index"); - - CHECK_LE(source_start, source_end); - CHECK_LE(target_start, target_end); - - size_t to_cmp = MIN(MIN(source_end - source_start, - target_end - target_start), - ts_obj_length - source_start); - - int val = normalizeCompareVal(to_cmp > 0 ? - memcmp(ts_obj_data + source_start, - target_data + target_start, - to_cmp) : 0, - source_end - source_start, - target_end - target_start); - - args.GetReturnValue().Set(val); -} - -void Compare(const FunctionCallbackInfo &args) { - Environment* env = Environment::GetCurrent(args); - - THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]); - THROW_AND_RETURN_UNLESS_BUFFER(env, args[1]); - SPREAD_ARG(args[0], obj_a); - SPREAD_ARG(args[1], obj_b); - - size_t cmp_length = MIN(obj_a_length, obj_b_length); - - int val = normalizeCompareVal(cmp_length > 0 ? - memcmp(obj_a_data, obj_b_data, cmp_length) : 0, - obj_a_length, obj_b_length); - args.GetReturnValue().Set(val); -} - - -// Computes the offset for starting an indexOf or lastIndexOf search. -// Returns either a valid offset in [0...], ie inside the Buffer, -// or -1 to signal that there is no possible match. -int64_t IndexOfOffset(size_t length, int64_t offset_i64, bool is_forward) { - int64_t length_i64 = static_cast(length); - if (length_i64 == 0) { - // Empty buffer, no match. - return -1; - } - if (offset_i64 < 0) { - if (offset_i64 + length_i64 >= 0) { - // Negative offsets count backwards from the end of the buffer. - return length_i64 + offset_i64; - } else if (is_forward) { - // indexOf from before the start of the buffer: search the whole buffer. - return 0; - } else { - // lastIndexOf from before the start of the buffer: no match. - return -1; - } - } else { - if (offset_i64 < length_i64) { - // Valid positive offset. - return offset_i64; - } else if (is_forward) { - // indexOf from past the end of the buffer: no match. - return -1; - } else { - // lastIndexOf from past the end of the buffer: search the whole buffer. - return length_i64 - 1; - } - } -} - -void IndexOfString(const FunctionCallbackInfo& args) { - ASSERT(args[1]->IsString()); - ASSERT(args[2]->IsNumber()); - ASSERT(args[4]->IsBoolean()); - - enum encoding enc = ParseEncoding(args.GetIsolate(), - args[3], - UTF8); - - THROW_AND_RETURN_UNLESS_BUFFER(Environment::GetCurrent(args), args[0]); - SPREAD_ARG(args[0], ts_obj); - - Local needle = args[1].As(); - int64_t offset_i64 = args[2]->IntegerValue(); - bool is_forward = args[4]->IsTrue(); - - const char* haystack = ts_obj_data; - // Round down to the nearest multiple of 2 in case of UCS2. - const size_t haystack_length = (enc == UCS2) ? - ts_obj_length &~ 1 : ts_obj_length; // NOLINT(whitespace/operators) - - const size_t needle_length = - StringBytes::Size(args.GetIsolate(), needle, enc); - - if (needle_length == 0 || haystack_length == 0) { - return args.GetReturnValue().Set(-1); - } - - int64_t opt_offset = IndexOfOffset(haystack_length, offset_i64, is_forward); - if (opt_offset <= -1) { - return args.GetReturnValue().Set(-1); - } - size_t offset = static_cast(opt_offset); - CHECK_LT(offset, haystack_length); - if ((is_forward && needle_length + offset > haystack_length) || - needle_length > haystack_length) { - return args.GetReturnValue().Set(-1); - } - - size_t result = haystack_length; - - if (enc == UCS2) { - String::Value needle_value(needle); - if (*needle_value == nullptr) - return args.GetReturnValue().Set(-1); - - if (haystack_length < 2 || needle_value.length() < 1) { - return args.GetReturnValue().Set(-1); - } - - if (IsBigEndian()) { - StringBytes::InlineDecoder decoder; - decoder.Decode(Environment::GetCurrent(args), needle, args[3], UCS2); - const uint16_t* decoded_string = - reinterpret_cast(decoder.out()); - - if (decoded_string == nullptr) - return args.GetReturnValue().Set(-1); - - result = SearchString(reinterpret_cast(haystack), - haystack_length / 2, - decoded_string, - decoder.size() / 2, - offset / 2, - is_forward); - } else { - result = SearchString(reinterpret_cast(haystack), - haystack_length / 2, - reinterpret_cast(*needle_value), - needle_value.length(), - offset / 2, - is_forward); - } - result *= 2; - } else if (enc == UTF8) { - String::Utf8Value needle_value(needle); - if (*needle_value == nullptr) - return args.GetReturnValue().Set(-1); - - result = SearchString(reinterpret_cast(haystack), - haystack_length, - reinterpret_cast(*needle_value), - needle_length, - offset, - is_forward); - } else if (enc == LATIN1) { - uint8_t* needle_data = static_cast(node::Malloc(needle_length)); - if (needle_data == nullptr) { - return args.GetReturnValue().Set(-1); - } - needle->WriteOneByte( - needle_data, 0, needle_length, String::NO_NULL_TERMINATION); - - result = SearchString(reinterpret_cast(haystack), - haystack_length, - needle_data, - needle_length, - offset, - is_forward); - free(needle_data); - } - - args.GetReturnValue().Set( - result == haystack_length ? -1 : static_cast(result)); -} - -void IndexOfBuffer(const FunctionCallbackInfo& args) { - ASSERT(args[1]->IsObject()); - ASSERT(args[2]->IsNumber()); - ASSERT(args[4]->IsBoolean()); - - enum encoding enc = ParseEncoding(args.GetIsolate(), - args[3], - UTF8); - - THROW_AND_RETURN_UNLESS_BUFFER(Environment::GetCurrent(args), args[0]); - THROW_AND_RETURN_UNLESS_BUFFER(Environment::GetCurrent(args), args[1]); - SPREAD_ARG(args[0], ts_obj); - SPREAD_ARG(args[1], buf); - int64_t offset_i64 = args[2]->IntegerValue(); - bool is_forward = args[4]->IsTrue(); - - const char* haystack = ts_obj_data; - const size_t haystack_length = ts_obj_length; - const char* needle = buf_data; - const size_t needle_length = buf_length; - - if (needle_length == 0 || haystack_length == 0) { - return args.GetReturnValue().Set(-1); - } - - int64_t opt_offset = IndexOfOffset(haystack_length, offset_i64, is_forward); - if (opt_offset <= -1) { - return args.GetReturnValue().Set(-1); - } - size_t offset = static_cast(opt_offset); - CHECK_LT(offset, haystack_length); - if ((is_forward && needle_length + offset > haystack_length) || - needle_length > haystack_length) { - return args.GetReturnValue().Set(-1); - } - - size_t result = haystack_length; - - if (enc == UCS2) { - if (haystack_length < 2 || needle_length < 2) { - return args.GetReturnValue().Set(-1); - } - result = SearchString( - reinterpret_cast(haystack), - haystack_length / 2, - reinterpret_cast(needle), - needle_length / 2, - offset / 2, - is_forward); - result *= 2; - } else { - result = SearchString( - reinterpret_cast(haystack), - haystack_length, - reinterpret_cast(needle), - needle_length, - offset, - is_forward); - } - - args.GetReturnValue().Set( - result == haystack_length ? -1 : static_cast(result)); -} - -void IndexOfNumber(const FunctionCallbackInfo& args) { - ASSERT(args[1]->IsNumber()); - ASSERT(args[2]->IsNumber()); - ASSERT(args[3]->IsBoolean()); - - THROW_AND_RETURN_UNLESS_BUFFER(Environment::GetCurrent(args), args[0]); - SPREAD_ARG(args[0], ts_obj); - - uint32_t needle = args[1]->Uint32Value(); - int64_t offset_i64 = args[2]->IntegerValue(); - bool is_forward = args[3]->IsTrue(); - - int64_t opt_offset = IndexOfOffset(ts_obj_length, offset_i64, is_forward); - if (opt_offset <= -1) { - return args.GetReturnValue().Set(-1); - } - size_t offset = static_cast(opt_offset); - CHECK_LT(offset, ts_obj_length); - - const void* ptr; - if (is_forward) { - ptr = memchr(ts_obj_data + offset, needle, ts_obj_length - offset); - } else { - ptr = node::stringsearch::MemrchrFill(ts_obj_data, needle, offset + 1); - } - const char* ptr_char = static_cast(ptr); - args.GetReturnValue().Set(ptr ? static_cast(ptr_char - ts_obj_data) - : -1); -} - - -void Swap16(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]); - SPREAD_ARG(args[0], ts_obj); - SwapBytes16(ts_obj_data, ts_obj_length); - args.GetReturnValue().Set(args[0]); -} - - -void Swap32(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]); - SPREAD_ARG(args[0], ts_obj); - SwapBytes32(ts_obj_data, ts_obj_length); - args.GetReturnValue().Set(args[0]); -} - - -void Swap64(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]); - SPREAD_ARG(args[0], ts_obj); - SwapBytes64(ts_obj_data, ts_obj_length); - args.GetReturnValue().Set(args[0]); -} - - -// pass Buffer object to load prototype methods -void SetupBufferJS(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - - CHECK(args[0]->IsObject()); - Local proto = args[0].As(); - env->set_buffer_prototype_object(proto); - - env->SetMethod(proto, "asciiSlice", AsciiSlice); - env->SetMethod(proto, "base64Slice", Base64Slice); - env->SetMethod(proto, "latin1Slice", Latin1Slice); - env->SetMethod(proto, "hexSlice", HexSlice); - env->SetMethod(proto, "ucs2Slice", Ucs2Slice); - env->SetMethod(proto, "utf8Slice", Utf8Slice); - - env->SetMethod(proto, "asciiWrite", AsciiWrite); - env->SetMethod(proto, "base64Write", Base64Write); - env->SetMethod(proto, "latin1Write", Latin1Write); - env->SetMethod(proto, "hexWrite", HexWrite); - env->SetMethod(proto, "ucs2Write", Ucs2Write); - env->SetMethod(proto, "utf8Write", Utf8Write); - - env->SetMethod(proto, "copy", Copy); - - CHECK(args[1]->IsObject()); - Local bObj = args[1].As(); - - uint32_t* const fields = env->array_buffer_allocator_info()->fields(); - uint32_t const fields_count = - env->array_buffer_allocator_info()->fields_count(); - - Local array_buffer = - ArrayBuffer::New(env->isolate(), fields, sizeof(*fields) * fields_count); - - bObj->Set(String::NewFromUtf8(env->isolate(), "flags"), + bObj->Set(String::NewFromUtf8(env->isolate(), "flags"), Uint32Array::New(array_buffer, 0, fields_count)); -} - - -void Initialize(Local target, - Local unused, - Local context) { - Environment* env = Environment::GetCurrent(context); - - env->SetMethod(target, "setupBufferJS", SetupBufferJS); - env->SetMethod(target, "createFromString", CreateFromString); - - env->SetMethod(target, "byteLengthUtf8", ByteLengthUtf8); - env->SetMethod(target, "compare", Compare); - env->SetMethod(target, "compareOffset", CompareOffset); - env->SetMethod(target, "fill", Fill); - env->SetMethod(target, "indexOfBuffer", IndexOfBuffer); - env->SetMethod(target, "indexOfNumber", IndexOfNumber); - env->SetMethod(target, "indexOfString", IndexOfString); - - env->SetMethod(target, "readDoubleBE", ReadDoubleBE); - env->SetMethod(target, "readDoubleLE", ReadDoubleLE); - env->SetMethod(target, "readFloatBE", ReadFloatBE); - env->SetMethod(target, "readFloatLE", ReadFloatLE); - - env->SetMethod(target, "writeDoubleBE", WriteDoubleBE); - env->SetMethod(target, "writeDoubleLE", WriteDoubleLE); - env->SetMethod(target, "writeFloatBE", WriteFloatBE); - env->SetMethod(target, "writeFloatLE", WriteFloatLE); - - env->SetMethod(target, "swap16", Swap16); - env->SetMethod(target, "swap32", Swap32); - env->SetMethod(target, "swap64", Swap64); - - target->Set(env->context(), - FIXED_ONE_BYTE_STRING(env->isolate(), "kMaxLength"), - Integer::NewFromUnsigned(env->isolate(), kMaxLength)).FromJust(); - - target->Set(env->context(), - FIXED_ONE_BYTE_STRING(env->isolate(), "kStringMaxLength"), - Integer::New(env->isolate(), String::kMaxLength)).FromJust(); -} + } + void Initialize(Local target, + Local unused, + Local context) + { + Environment* env = Environment::GetCurrent(context); + + env->SetMethod(target, "setupBufferJS", SetupBufferJS); + env->SetMethod(target, "createFromString", CreateFromString); + + env->SetMethod(target, "byteLengthUtf8", ByteLengthUtf8); + env->SetMethod(target, "compare", Compare); + env->SetMethod(target, "compareOffset", CompareOffset); + env->SetMethod(target, "fill", Fill); + env->SetMethod(target, "indexOfBuffer", IndexOfBuffer); + env->SetMethod(target, "indexOfNumber", IndexOfNumber); + env->SetMethod(target, "indexOfString", IndexOfString); + + env->SetMethod(target, "readDoubleBE", ReadDoubleBE); + env->SetMethod(target, "readDoubleLE", ReadDoubleLE); + env->SetMethod(target, "readFloatBE", ReadFloatBE); + env->SetMethod(target, "readFloatLE", ReadFloatLE); + + env->SetMethod(target, "writeDoubleBE", WriteDoubleBE); + env->SetMethod(target, "writeDoubleLE", WriteDoubleLE); + env->SetMethod(target, "writeFloatBE", WriteFloatBE); + env->SetMethod(target, "writeFloatLE", WriteFloatLE); + + env->SetMethod(target, "swap16", Swap16); + env->SetMethod(target, "swap32", Swap32); + env->SetMethod(target, "swap64", Swap64); + + target->Set(env->context(), + FIXED_ONE_BYTE_STRING(env->isolate(), "kMaxLength"), + Integer::NewFromUnsigned(env->isolate(), kMaxLength)) + .FromJust(); + + target->Set(env->context(), + FIXED_ONE_BYTE_STRING(env->isolate(), "kStringMaxLength"), + Integer::New(env->isolate(), String::kMaxLength)) + .FromJust(); + } -} // namespace Buffer -} // namespace node +} // namespace Buffer +} // namespace node NODE_MODULE_CONTEXT_AWARE_BUILTIN(buffer, node::Buffer::Initialize) diff --git a/node/src/node_buffer.h b/node/src/node_buffer.h index 686450d984..0b15dbc01c 100644 --- a/node/src/node_buffer.h +++ b/node/src/node_buffer.h @@ -1,3 +1,6 @@ +//Copyright Joyent, Inc. and other Node contributors. +//The MIT License (MIT) + #ifndef SRC_NODE_BUFFER_H_ #define SRC_NODE_BUFFER_H_ @@ -10,59 +13,59 @@ extern bool zero_fill_all_buffers; namespace Buffer { -static const unsigned int kMaxLength = - sizeof(int32_t) == sizeof(intptr_t) ? 0x3fffffff : 0x7fffffff; - -NODE_EXTERN typedef void (*FreeCallback)(char* data, void* hint); - -NODE_EXTERN bool HasInstance(v8::Local val); -NODE_EXTERN bool HasInstance(v8::Local val); -NODE_EXTERN char* Data(v8::Local val); -NODE_EXTERN char* Data(v8::Local val); -NODE_EXTERN size_t Length(v8::Local val); -NODE_EXTERN size_t Length(v8::Local val); - -// public constructor - data is copied -NODE_EXTERN v8::MaybeLocal Copy(v8::Isolate* isolate, - const char* data, - size_t len); - -// public constructor -NODE_EXTERN v8::MaybeLocal New(v8::Isolate* isolate, size_t length); - -// public constructor from string -NODE_EXTERN v8::MaybeLocal New(v8::Isolate* isolate, - v8::Local string, - enum encoding enc = UTF8); - -// public constructor - data is used, callback is passed data on object gc -NODE_EXTERN v8::MaybeLocal New(v8::Isolate* isolate, - char* data, - size_t length, - FreeCallback callback, - void* hint); - -// public constructor - data is used. -NODE_EXTERN v8::MaybeLocal New(v8::Isolate* isolate, - char* data, - size_t len); - -// This is verbose to be explicit with inline commenting -static inline bool IsWithinBounds(size_t off, size_t len, size_t max) { - // Asking to seek too far into the buffer - // check to avoid wrapping in subsequent subtraction - if (off > max) - return false; - - // Asking for more than is left over in the buffer - if (max - off < len) - return false; - - // Otherwise we're in bounds - return true; -} - -} // namespace Buffer -} // namespace node - -#endif // SRC_NODE_BUFFER_H_ + static const unsigned int kMaxLength = sizeof(int32_t) == sizeof(intptr_t) ? 0x3fffffff : 0x7fffffff; + + NODE_EXTERN typedef void (*FreeCallback)(char* data, void* hint); + + NODE_EXTERN bool HasInstance(v8::Local val); + NODE_EXTERN bool HasInstance(v8::Local val); + NODE_EXTERN char* Data(v8::Local val); + NODE_EXTERN char* Data(v8::Local val); + NODE_EXTERN size_t Length(v8::Local val); + NODE_EXTERN size_t Length(v8::Local val); + + // public constructor - data is copied + NODE_EXTERN v8::MaybeLocal Copy(v8::Isolate* isolate, + const char* data, + size_t len); + + // public constructor + NODE_EXTERN v8::MaybeLocal New(v8::Isolate* isolate, size_t length); + + // public constructor from string + NODE_EXTERN v8::MaybeLocal New(v8::Isolate* isolate, + v8::Local string, + enum encoding enc = UTF8); + + // public constructor - data is used, callback is passed data on object gc + NODE_EXTERN v8::MaybeLocal New(v8::Isolate* isolate, + char* data, + size_t length, + FreeCallback callback, + void* hint); + + // public constructor - data is used. + NODE_EXTERN v8::MaybeLocal New(v8::Isolate* isolate, + char* data, + size_t len); + + // This is verbose to be explicit with inline commenting + static inline bool IsWithinBounds(size_t off, size_t len, size_t max) + { + // Asking to seek too far into the buffer + // check to avoid wrapping in subsequent subtraction + if (off > max) + return false; + + // Asking for more than is left over in the buffer + if (max - off < len) + return false; + + // Otherwise we're in bounds + return true; + } + +} // namespace Buffer +} // namespace node + +#endif // SRC_NODE_BUFFER_H_ diff --git a/node/src/node_config.cc b/node/src/node_config.cc index 401345f6a6..4ccff08ed0 100644 --- a/node/src/node_config.cc +++ b/node/src/node_config.cc @@ -5,7 +5,6 @@ #include "util.h" #include "util-inl.h" - namespace node { using v8::Context; @@ -19,33 +18,35 @@ using v8::Value; // alternative to dropping additional properties onto the process object as // has been the practice previously in node.cc. -#define READONLY_BOOLEAN_PROPERTY(str) \ - do { \ - target->DefineOwnProperty(env->context(), \ - OneByteString(env->isolate(), str), \ - True(env->isolate()), ReadOnly).FromJust(); \ - } while (0) +#define READONLY_BOOLEAN_PROPERTY(str) \ + do { \ + target->DefineOwnProperty(env->context(), \ + OneByteString(env->isolate(), str), \ + True(env->isolate()), ReadOnly) \ + .FromJust(); \ + } while (0) void InitConfig(Local target, - Local unused, - Local context) { - Environment* env = Environment::GetCurrent(context); + Local unused, + Local context) +{ + Environment* env = Environment::GetCurrent(context); #ifdef NODE_HAVE_I18N_SUPPORT - READONLY_BOOLEAN_PROPERTY("hasIntl"); + READONLY_BOOLEAN_PROPERTY("hasIntl"); #ifdef NODE_HAVE_SMALL_ICU - READONLY_BOOLEAN_PROPERTY("hasSmallICU"); -#endif // NODE_HAVE_SMALL_ICU + READONLY_BOOLEAN_PROPERTY("hasSmallICU"); +#endif // NODE_HAVE_SMALL_ICU - if (flag_icu_data_dir) - READONLY_BOOLEAN_PROPERTY("usingICUDataDir"); -#endif // NODE_HAVE_I18N_SUPPORT + if (flag_icu_data_dir) + READONLY_BOOLEAN_PROPERTY("usingICUDataDir"); +#endif // NODE_HAVE_I18N_SUPPORT - if (config_preserve_symlinks) - READONLY_BOOLEAN_PROPERTY("preserveSymlinks"); -} // InitConfig + if (config_preserve_symlinks) + READONLY_BOOLEAN_PROPERTY("preserveSymlinks"); +} // InitConfig -} // namespace node +} // namespace node NODE_MODULE_CONTEXT_AWARE_BUILTIN(config, node::InitConfig) diff --git a/node/src/node_constants.cc b/node/src/node_constants.cc index 2e6be8df37..a3d7ac090b 100644 --- a/node/src/node_constants.cc +++ b/node/src/node_constants.cc @@ -14,11 +14,11 @@ #include #if HAVE_OPENSSL -# include -# include -# ifndef OPENSSL_NO_ENGINE -# include -# endif // !OPENSSL_NO_ENGINE +#include +#include +#ifndef OPENSSL_NO_ENGINE +#include +#endif // !OPENSSL_NO_ENGINE #endif namespace node { @@ -30,708 +30,711 @@ using v8::Object; const char* default_cipher_list = DEFAULT_CIPHER_LIST_CORE; #endif -void DefineErrnoConstants(Local target) { +void DefineErrnoConstants(Local target) +{ #ifdef E2BIG - NODE_DEFINE_CONSTANT(target, E2BIG); + NODE_DEFINE_CONSTANT(target, E2BIG); #endif #ifdef EACCES - NODE_DEFINE_CONSTANT(target, EACCES); + NODE_DEFINE_CONSTANT(target, EACCES); #endif #ifdef EADDRINUSE - NODE_DEFINE_CONSTANT(target, EADDRINUSE); + NODE_DEFINE_CONSTANT(target, EADDRINUSE); #endif #ifdef EADDRNOTAVAIL - NODE_DEFINE_CONSTANT(target, EADDRNOTAVAIL); + NODE_DEFINE_CONSTANT(target, EADDRNOTAVAIL); #endif #ifdef EAFNOSUPPORT - NODE_DEFINE_CONSTANT(target, EAFNOSUPPORT); + NODE_DEFINE_CONSTANT(target, EAFNOSUPPORT); #endif #ifdef EAGAIN - NODE_DEFINE_CONSTANT(target, EAGAIN); + NODE_DEFINE_CONSTANT(target, EAGAIN); #endif #ifdef EALREADY - NODE_DEFINE_CONSTANT(target, EALREADY); + NODE_DEFINE_CONSTANT(target, EALREADY); #endif #ifdef EBADF - NODE_DEFINE_CONSTANT(target, EBADF); + NODE_DEFINE_CONSTANT(target, EBADF); #endif #ifdef EBADMSG - NODE_DEFINE_CONSTANT(target, EBADMSG); + NODE_DEFINE_CONSTANT(target, EBADMSG); #endif #ifdef EBUSY - NODE_DEFINE_CONSTANT(target, EBUSY); + NODE_DEFINE_CONSTANT(target, EBUSY); #endif #ifdef ECANCELED - NODE_DEFINE_CONSTANT(target, ECANCELED); + NODE_DEFINE_CONSTANT(target, ECANCELED); #endif #ifdef ECHILD - NODE_DEFINE_CONSTANT(target, ECHILD); + NODE_DEFINE_CONSTANT(target, ECHILD); #endif #ifdef ECONNABORTED - NODE_DEFINE_CONSTANT(target, ECONNABORTED); + NODE_DEFINE_CONSTANT(target, ECONNABORTED); #endif #ifdef ECONNREFUSED - NODE_DEFINE_CONSTANT(target, ECONNREFUSED); + NODE_DEFINE_CONSTANT(target, ECONNREFUSED); #endif #ifdef ECONNRESET - NODE_DEFINE_CONSTANT(target, ECONNRESET); + NODE_DEFINE_CONSTANT(target, ECONNRESET); #endif #ifdef EDEADLK - NODE_DEFINE_CONSTANT(target, EDEADLK); + NODE_DEFINE_CONSTANT(target, EDEADLK); #endif #ifdef EDESTADDRREQ - NODE_DEFINE_CONSTANT(target, EDESTADDRREQ); + NODE_DEFINE_CONSTANT(target, EDESTADDRREQ); #endif #ifdef EDOM - NODE_DEFINE_CONSTANT(target, EDOM); + NODE_DEFINE_CONSTANT(target, EDOM); #endif #ifdef EDQUOT - NODE_DEFINE_CONSTANT(target, EDQUOT); + NODE_DEFINE_CONSTANT(target, EDQUOT); #endif #ifdef EEXIST - NODE_DEFINE_CONSTANT(target, EEXIST); + NODE_DEFINE_CONSTANT(target, EEXIST); #endif #ifdef EFAULT - NODE_DEFINE_CONSTANT(target, EFAULT); + NODE_DEFINE_CONSTANT(target, EFAULT); #endif #ifdef EFBIG - NODE_DEFINE_CONSTANT(target, EFBIG); + NODE_DEFINE_CONSTANT(target, EFBIG); #endif #ifdef EHOSTUNREACH - NODE_DEFINE_CONSTANT(target, EHOSTUNREACH); + NODE_DEFINE_CONSTANT(target, EHOSTUNREACH); #endif #ifdef EIDRM - NODE_DEFINE_CONSTANT(target, EIDRM); + NODE_DEFINE_CONSTANT(target, EIDRM); #endif #ifdef EILSEQ - NODE_DEFINE_CONSTANT(target, EILSEQ); + NODE_DEFINE_CONSTANT(target, EILSEQ); #endif #ifdef EINPROGRESS - NODE_DEFINE_CONSTANT(target, EINPROGRESS); + NODE_DEFINE_CONSTANT(target, EINPROGRESS); #endif #ifdef EINTR - NODE_DEFINE_CONSTANT(target, EINTR); + NODE_DEFINE_CONSTANT(target, EINTR); #endif #ifdef EINVAL - NODE_DEFINE_CONSTANT(target, EINVAL); + NODE_DEFINE_CONSTANT(target, EINVAL); #endif #ifdef EIO - NODE_DEFINE_CONSTANT(target, EIO); + NODE_DEFINE_CONSTANT(target, EIO); #endif #ifdef EISCONN - NODE_DEFINE_CONSTANT(target, EISCONN); + NODE_DEFINE_CONSTANT(target, EISCONN); #endif #ifdef EISDIR - NODE_DEFINE_CONSTANT(target, EISDIR); + NODE_DEFINE_CONSTANT(target, EISDIR); #endif #ifdef ELOOP - NODE_DEFINE_CONSTANT(target, ELOOP); + NODE_DEFINE_CONSTANT(target, ELOOP); #endif #ifdef EMFILE - NODE_DEFINE_CONSTANT(target, EMFILE); + NODE_DEFINE_CONSTANT(target, EMFILE); #endif #ifdef EMLINK - NODE_DEFINE_CONSTANT(target, EMLINK); + NODE_DEFINE_CONSTANT(target, EMLINK); #endif #ifdef EMSGSIZE - NODE_DEFINE_CONSTANT(target, EMSGSIZE); + NODE_DEFINE_CONSTANT(target, EMSGSIZE); #endif #ifdef EMULTIHOP - NODE_DEFINE_CONSTANT(target, EMULTIHOP); + NODE_DEFINE_CONSTANT(target, EMULTIHOP); #endif #ifdef ENAMETOOLONG - NODE_DEFINE_CONSTANT(target, ENAMETOOLONG); + NODE_DEFINE_CONSTANT(target, ENAMETOOLONG); #endif #ifdef ENETDOWN - NODE_DEFINE_CONSTANT(target, ENETDOWN); + NODE_DEFINE_CONSTANT(target, ENETDOWN); #endif #ifdef ENETRESET - NODE_DEFINE_CONSTANT(target, ENETRESET); + NODE_DEFINE_CONSTANT(target, ENETRESET); #endif #ifdef ENETUNREACH - NODE_DEFINE_CONSTANT(target, ENETUNREACH); + NODE_DEFINE_CONSTANT(target, ENETUNREACH); #endif #ifdef ENFILE - NODE_DEFINE_CONSTANT(target, ENFILE); + NODE_DEFINE_CONSTANT(target, ENFILE); #endif #ifdef ENOBUFS - NODE_DEFINE_CONSTANT(target, ENOBUFS); + NODE_DEFINE_CONSTANT(target, ENOBUFS); #endif #ifdef ENODATA - NODE_DEFINE_CONSTANT(target, ENODATA); + NODE_DEFINE_CONSTANT(target, ENODATA); #endif #ifdef ENODEV - NODE_DEFINE_CONSTANT(target, ENODEV); + NODE_DEFINE_CONSTANT(target, ENODEV); #endif #ifdef ENOENT - NODE_DEFINE_CONSTANT(target, ENOENT); + NODE_DEFINE_CONSTANT(target, ENOENT); #endif #ifdef ENOEXEC - NODE_DEFINE_CONSTANT(target, ENOEXEC); + NODE_DEFINE_CONSTANT(target, ENOEXEC); #endif #ifdef ENOLCK - NODE_DEFINE_CONSTANT(target, ENOLCK); + NODE_DEFINE_CONSTANT(target, ENOLCK); #endif #ifdef ENOLINK - NODE_DEFINE_CONSTANT(target, ENOLINK); + NODE_DEFINE_CONSTANT(target, ENOLINK); #endif #ifdef ENOMEM - NODE_DEFINE_CONSTANT(target, ENOMEM); + NODE_DEFINE_CONSTANT(target, ENOMEM); #endif #ifdef ENOMSG - NODE_DEFINE_CONSTANT(target, ENOMSG); + NODE_DEFINE_CONSTANT(target, ENOMSG); #endif #ifdef ENOPROTOOPT - NODE_DEFINE_CONSTANT(target, ENOPROTOOPT); + NODE_DEFINE_CONSTANT(target, ENOPROTOOPT); #endif #ifdef ENOSPC - NODE_DEFINE_CONSTANT(target, ENOSPC); + NODE_DEFINE_CONSTANT(target, ENOSPC); #endif #ifdef ENOSR - NODE_DEFINE_CONSTANT(target, ENOSR); + NODE_DEFINE_CONSTANT(target, ENOSR); #endif #ifdef ENOSTR - NODE_DEFINE_CONSTANT(target, ENOSTR); + NODE_DEFINE_CONSTANT(target, ENOSTR); #endif #ifdef ENOSYS - NODE_DEFINE_CONSTANT(target, ENOSYS); + NODE_DEFINE_CONSTANT(target, ENOSYS); #endif #ifdef ENOTCONN - NODE_DEFINE_CONSTANT(target, ENOTCONN); + NODE_DEFINE_CONSTANT(target, ENOTCONN); #endif #ifdef ENOTDIR - NODE_DEFINE_CONSTANT(target, ENOTDIR); + NODE_DEFINE_CONSTANT(target, ENOTDIR); #endif #ifdef ENOTEMPTY - NODE_DEFINE_CONSTANT(target, ENOTEMPTY); + NODE_DEFINE_CONSTANT(target, ENOTEMPTY); #endif #ifdef ENOTSOCK - NODE_DEFINE_CONSTANT(target, ENOTSOCK); + NODE_DEFINE_CONSTANT(target, ENOTSOCK); #endif #ifdef ENOTSUP - NODE_DEFINE_CONSTANT(target, ENOTSUP); + NODE_DEFINE_CONSTANT(target, ENOTSUP); #endif #ifdef ENOTTY - NODE_DEFINE_CONSTANT(target, ENOTTY); + NODE_DEFINE_CONSTANT(target, ENOTTY); #endif #ifdef ENXIO - NODE_DEFINE_CONSTANT(target, ENXIO); + NODE_DEFINE_CONSTANT(target, ENXIO); #endif #ifdef EOPNOTSUPP - NODE_DEFINE_CONSTANT(target, EOPNOTSUPP); + NODE_DEFINE_CONSTANT(target, EOPNOTSUPP); #endif #ifdef EOVERFLOW - NODE_DEFINE_CONSTANT(target, EOVERFLOW); + NODE_DEFINE_CONSTANT(target, EOVERFLOW); #endif #ifdef EPERM - NODE_DEFINE_CONSTANT(target, EPERM); + NODE_DEFINE_CONSTANT(target, EPERM); #endif #ifdef EPIPE - NODE_DEFINE_CONSTANT(target, EPIPE); + NODE_DEFINE_CONSTANT(target, EPIPE); #endif #ifdef EPROTO - NODE_DEFINE_CONSTANT(target, EPROTO); + NODE_DEFINE_CONSTANT(target, EPROTO); #endif #ifdef EPROTONOSUPPORT - NODE_DEFINE_CONSTANT(target, EPROTONOSUPPORT); + NODE_DEFINE_CONSTANT(target, EPROTONOSUPPORT); #endif #ifdef EPROTOTYPE - NODE_DEFINE_CONSTANT(target, EPROTOTYPE); + NODE_DEFINE_CONSTANT(target, EPROTOTYPE); #endif #ifdef ERANGE - NODE_DEFINE_CONSTANT(target, ERANGE); + NODE_DEFINE_CONSTANT(target, ERANGE); #endif #ifdef EROFS - NODE_DEFINE_CONSTANT(target, EROFS); + NODE_DEFINE_CONSTANT(target, EROFS); #endif #ifdef ESPIPE - NODE_DEFINE_CONSTANT(target, ESPIPE); + NODE_DEFINE_CONSTANT(target, ESPIPE); #endif #ifdef ESRCH - NODE_DEFINE_CONSTANT(target, ESRCH); + NODE_DEFINE_CONSTANT(target, ESRCH); #endif #ifdef ESTALE - NODE_DEFINE_CONSTANT(target, ESTALE); + NODE_DEFINE_CONSTANT(target, ESTALE); #endif #ifdef ETIME - NODE_DEFINE_CONSTANT(target, ETIME); + NODE_DEFINE_CONSTANT(target, ETIME); #endif #ifdef ETIMEDOUT - NODE_DEFINE_CONSTANT(target, ETIMEDOUT); + NODE_DEFINE_CONSTANT(target, ETIMEDOUT); #endif #ifdef ETXTBSY - NODE_DEFINE_CONSTANT(target, ETXTBSY); + NODE_DEFINE_CONSTANT(target, ETXTBSY); #endif #ifdef EWOULDBLOCK - NODE_DEFINE_CONSTANT(target, EWOULDBLOCK); + NODE_DEFINE_CONSTANT(target, EWOULDBLOCK); #endif #ifdef EXDEV - NODE_DEFINE_CONSTANT(target, EXDEV); + NODE_DEFINE_CONSTANT(target, EXDEV); #endif } -void DefineWindowsErrorConstants(Local target) { +void DefineWindowsErrorConstants(Local target) +{ #ifdef WSAEINTR - NODE_DEFINE_CONSTANT(target, WSAEINTR); + NODE_DEFINE_CONSTANT(target, WSAEINTR); #endif #ifdef WSAEBADF - NODE_DEFINE_CONSTANT(target, WSAEBADF); + NODE_DEFINE_CONSTANT(target, WSAEBADF); #endif #ifdef WSAEACCES - NODE_DEFINE_CONSTANT(target, WSAEACCES); + NODE_DEFINE_CONSTANT(target, WSAEACCES); #endif #ifdef WSAEFAULT - NODE_DEFINE_CONSTANT(target, WSAEFAULT); + NODE_DEFINE_CONSTANT(target, WSAEFAULT); #endif #ifdef WSAEINVAL - NODE_DEFINE_CONSTANT(target, WSAEINVAL); + NODE_DEFINE_CONSTANT(target, WSAEINVAL); #endif #ifdef WSAEMFILE - NODE_DEFINE_CONSTANT(target, WSAEMFILE); + NODE_DEFINE_CONSTANT(target, WSAEMFILE); #endif #ifdef WSAEWOULDBLOCK - NODE_DEFINE_CONSTANT(target, WSAEWOULDBLOCK); + NODE_DEFINE_CONSTANT(target, WSAEWOULDBLOCK); #endif #ifdef WSAEINPROGRESS - NODE_DEFINE_CONSTANT(target, WSAEINPROGRESS); + NODE_DEFINE_CONSTANT(target, WSAEINPROGRESS); #endif #ifdef WSAEALREADY - NODE_DEFINE_CONSTANT(target, WSAEALREADY); + NODE_DEFINE_CONSTANT(target, WSAEALREADY); #endif #ifdef WSAENOTSOCK - NODE_DEFINE_CONSTANT(target, WSAENOTSOCK); + NODE_DEFINE_CONSTANT(target, WSAENOTSOCK); #endif #ifdef WSAEDESTADDRREQ - NODE_DEFINE_CONSTANT(target, WSAEDESTADDRREQ); + NODE_DEFINE_CONSTANT(target, WSAEDESTADDRREQ); #endif #ifdef WSAEMSGSIZE - NODE_DEFINE_CONSTANT(target, WSAEMSGSIZE); + NODE_DEFINE_CONSTANT(target, WSAEMSGSIZE); #endif #ifdef WSAEPROTOTYPE - NODE_DEFINE_CONSTANT(target, WSAEPROTOTYPE); + NODE_DEFINE_CONSTANT(target, WSAEPROTOTYPE); #endif #ifdef WSAENOPROTOOPT - NODE_DEFINE_CONSTANT(target, WSAENOPROTOOPT); + NODE_DEFINE_CONSTANT(target, WSAENOPROTOOPT); #endif #ifdef WSAEPROTONOSUPPORT - NODE_DEFINE_CONSTANT(target, WSAEPROTONOSUPPORT); + NODE_DEFINE_CONSTANT(target, WSAEPROTONOSUPPORT); #endif #ifdef WSAESOCKTNOSUPPORT - NODE_DEFINE_CONSTANT(target, WSAESOCKTNOSUPPORT); + NODE_DEFINE_CONSTANT(target, WSAESOCKTNOSUPPORT); #endif #ifdef WSAEOPNOTSUPP - NODE_DEFINE_CONSTANT(target, WSAEOPNOTSUPP); + NODE_DEFINE_CONSTANT(target, WSAEOPNOTSUPP); #endif #ifdef WSAEPFNOSUPPORT - NODE_DEFINE_CONSTANT(target, WSAEPFNOSUPPORT); + NODE_DEFINE_CONSTANT(target, WSAEPFNOSUPPORT); #endif #ifdef WSAEAFNOSUPPORT - NODE_DEFINE_CONSTANT(target, WSAEAFNOSUPPORT); + NODE_DEFINE_CONSTANT(target, WSAEAFNOSUPPORT); #endif #ifdef WSAEADDRINUSE - NODE_DEFINE_CONSTANT(target, WSAEADDRINUSE); + NODE_DEFINE_CONSTANT(target, WSAEADDRINUSE); #endif #ifdef WSAEADDRNOTAVAIL - NODE_DEFINE_CONSTANT(target, WSAEADDRNOTAVAIL); + NODE_DEFINE_CONSTANT(target, WSAEADDRNOTAVAIL); #endif #ifdef WSAENETDOWN - NODE_DEFINE_CONSTANT(target, WSAENETDOWN); + NODE_DEFINE_CONSTANT(target, WSAENETDOWN); #endif #ifdef WSAENETUNREACH - NODE_DEFINE_CONSTANT(target, WSAENETUNREACH); + NODE_DEFINE_CONSTANT(target, WSAENETUNREACH); #endif #ifdef WSAENETRESET - NODE_DEFINE_CONSTANT(target, WSAENETRESET); + NODE_DEFINE_CONSTANT(target, WSAENETRESET); #endif #ifdef WSAECONNABORTED - NODE_DEFINE_CONSTANT(target, WSAECONNABORTED); + NODE_DEFINE_CONSTANT(target, WSAECONNABORTED); #endif #ifdef WSAECONNRESET - NODE_DEFINE_CONSTANT(target, WSAECONNRESET); + NODE_DEFINE_CONSTANT(target, WSAECONNRESET); #endif #ifdef WSAENOBUFS - NODE_DEFINE_CONSTANT(target, WSAENOBUFS); + NODE_DEFINE_CONSTANT(target, WSAENOBUFS); #endif #ifdef WSAEISCONN - NODE_DEFINE_CONSTANT(target, WSAEISCONN); + NODE_DEFINE_CONSTANT(target, WSAEISCONN); #endif #ifdef WSAENOTCONN - NODE_DEFINE_CONSTANT(target, WSAENOTCONN); + NODE_DEFINE_CONSTANT(target, WSAENOTCONN); #endif #ifdef WSAESHUTDOWN - NODE_DEFINE_CONSTANT(target, WSAESHUTDOWN); + NODE_DEFINE_CONSTANT(target, WSAESHUTDOWN); #endif #ifdef WSAETOOMANYREFS - NODE_DEFINE_CONSTANT(target, WSAETOOMANYREFS); + NODE_DEFINE_CONSTANT(target, WSAETOOMANYREFS); #endif #ifdef WSAETIMEDOUT - NODE_DEFINE_CONSTANT(target, WSAETIMEDOUT); + NODE_DEFINE_CONSTANT(target, WSAETIMEDOUT); #endif #ifdef WSAECONNREFUSED - NODE_DEFINE_CONSTANT(target, WSAECONNREFUSED); + NODE_DEFINE_CONSTANT(target, WSAECONNREFUSED); #endif #ifdef WSAELOOP - NODE_DEFINE_CONSTANT(target, WSAELOOP); + NODE_DEFINE_CONSTANT(target, WSAELOOP); #endif #ifdef WSAENAMETOOLONG - NODE_DEFINE_CONSTANT(target, WSAENAMETOOLONG); + NODE_DEFINE_CONSTANT(target, WSAENAMETOOLONG); #endif #ifdef WSAEHOSTDOWN - NODE_DEFINE_CONSTANT(target, WSAEHOSTDOWN); + NODE_DEFINE_CONSTANT(target, WSAEHOSTDOWN); #endif #ifdef WSAEHOSTUNREACH - NODE_DEFINE_CONSTANT(target, WSAEHOSTUNREACH); + NODE_DEFINE_CONSTANT(target, WSAEHOSTUNREACH); #endif #ifdef WSAENOTEMPTY - NODE_DEFINE_CONSTANT(target, WSAENOTEMPTY); + NODE_DEFINE_CONSTANT(target, WSAENOTEMPTY); #endif #ifdef WSAEPROCLIM - NODE_DEFINE_CONSTANT(target, WSAEPROCLIM); + NODE_DEFINE_CONSTANT(target, WSAEPROCLIM); #endif #ifdef WSAEUSERS - NODE_DEFINE_CONSTANT(target, WSAEUSERS); + NODE_DEFINE_CONSTANT(target, WSAEUSERS); #endif #ifdef WSAEDQUOT - NODE_DEFINE_CONSTANT(target, WSAEDQUOT); + NODE_DEFINE_CONSTANT(target, WSAEDQUOT); #endif #ifdef WSAESTALE - NODE_DEFINE_CONSTANT(target, WSAESTALE); + NODE_DEFINE_CONSTANT(target, WSAESTALE); #endif #ifdef WSAEREMOTE - NODE_DEFINE_CONSTANT(target, WSAEREMOTE); + NODE_DEFINE_CONSTANT(target, WSAEREMOTE); #endif #ifdef WSASYSNOTREADY - NODE_DEFINE_CONSTANT(target, WSASYSNOTREADY); + NODE_DEFINE_CONSTANT(target, WSASYSNOTREADY); #endif #ifdef WSAVERNOTSUPPORTED - NODE_DEFINE_CONSTANT(target, WSAVERNOTSUPPORTED); + NODE_DEFINE_CONSTANT(target, WSAVERNOTSUPPORTED); #endif #ifdef WSANOTINITIALISED - NODE_DEFINE_CONSTANT(target, WSANOTINITIALISED); + NODE_DEFINE_CONSTANT(target, WSANOTINITIALISED); #endif #ifdef WSAEDISCON - NODE_DEFINE_CONSTANT(target, WSAEDISCON); + NODE_DEFINE_CONSTANT(target, WSAEDISCON); #endif #ifdef WSAENOMORE - NODE_DEFINE_CONSTANT(target, WSAENOMORE); + NODE_DEFINE_CONSTANT(target, WSAENOMORE); #endif #ifdef WSAECANCELLED - NODE_DEFINE_CONSTANT(target, WSAECANCELLED); + NODE_DEFINE_CONSTANT(target, WSAECANCELLED); #endif #ifdef WSAEINVALIDPROCTABLE - NODE_DEFINE_CONSTANT(target, WSAEINVALIDPROCTABLE); + NODE_DEFINE_CONSTANT(target, WSAEINVALIDPROCTABLE); #endif #ifdef WSAEINVALIDPROVIDER - NODE_DEFINE_CONSTANT(target, WSAEINVALIDPROVIDER); + NODE_DEFINE_CONSTANT(target, WSAEINVALIDPROVIDER); #endif #ifdef WSAEPROVIDERFAILEDINIT - NODE_DEFINE_CONSTANT(target, WSAEPROVIDERFAILEDINIT); + NODE_DEFINE_CONSTANT(target, WSAEPROVIDERFAILEDINIT); #endif #ifdef WSASYSCALLFAILURE - NODE_DEFINE_CONSTANT(target, WSASYSCALLFAILURE); + NODE_DEFINE_CONSTANT(target, WSASYSCALLFAILURE); #endif #ifdef WSASERVICE_NOT_FOUND - NODE_DEFINE_CONSTANT(target, WSASERVICE_NOT_FOUND); + NODE_DEFINE_CONSTANT(target, WSASERVICE_NOT_FOUND); #endif #ifdef WSATYPE_NOT_FOUND - NODE_DEFINE_CONSTANT(target, WSATYPE_NOT_FOUND); + NODE_DEFINE_CONSTANT(target, WSATYPE_NOT_FOUND); #endif #ifdef WSA_E_NO_MORE - NODE_DEFINE_CONSTANT(target, WSA_E_NO_MORE); + NODE_DEFINE_CONSTANT(target, WSA_E_NO_MORE); #endif #ifdef WSA_E_CANCELLED - NODE_DEFINE_CONSTANT(target, WSA_E_CANCELLED); + NODE_DEFINE_CONSTANT(target, WSA_E_CANCELLED); #endif #ifdef WSAEREFUSED - NODE_DEFINE_CONSTANT(target, WSAEREFUSED); + NODE_DEFINE_CONSTANT(target, WSAEREFUSED); #endif } -void DefineSignalConstants(Local target) { +void DefineSignalConstants(Local target) +{ #ifdef SIGHUP - NODE_DEFINE_CONSTANT(target, SIGHUP); + NODE_DEFINE_CONSTANT(target, SIGHUP); #endif #ifdef SIGINT - NODE_DEFINE_CONSTANT(target, SIGINT); + NODE_DEFINE_CONSTANT(target, SIGINT); #endif #ifdef SIGQUIT - NODE_DEFINE_CONSTANT(target, SIGQUIT); + NODE_DEFINE_CONSTANT(target, SIGQUIT); #endif #ifdef SIGILL - NODE_DEFINE_CONSTANT(target, SIGILL); + NODE_DEFINE_CONSTANT(target, SIGILL); #endif #ifdef SIGTRAP - NODE_DEFINE_CONSTANT(target, SIGTRAP); + NODE_DEFINE_CONSTANT(target, SIGTRAP); #endif #ifdef SIGABRT - NODE_DEFINE_CONSTANT(target, SIGABRT); + NODE_DEFINE_CONSTANT(target, SIGABRT); #endif #ifdef SIGIOT - NODE_DEFINE_CONSTANT(target, SIGIOT); + NODE_DEFINE_CONSTANT(target, SIGIOT); #endif #ifdef SIGBUS - NODE_DEFINE_CONSTANT(target, SIGBUS); + NODE_DEFINE_CONSTANT(target, SIGBUS); #endif #ifdef SIGFPE - NODE_DEFINE_CONSTANT(target, SIGFPE); + NODE_DEFINE_CONSTANT(target, SIGFPE); #endif #ifdef SIGKILL - NODE_DEFINE_CONSTANT(target, SIGKILL); + NODE_DEFINE_CONSTANT(target, SIGKILL); #endif #ifdef SIGUSR1 - NODE_DEFINE_CONSTANT(target, SIGUSR1); + NODE_DEFINE_CONSTANT(target, SIGUSR1); #endif #ifdef SIGSEGV - NODE_DEFINE_CONSTANT(target, SIGSEGV); + NODE_DEFINE_CONSTANT(target, SIGSEGV); #endif #ifdef SIGUSR2 - NODE_DEFINE_CONSTANT(target, SIGUSR2); + NODE_DEFINE_CONSTANT(target, SIGUSR2); #endif #ifdef SIGPIPE - NODE_DEFINE_CONSTANT(target, SIGPIPE); + NODE_DEFINE_CONSTANT(target, SIGPIPE); #endif #ifdef SIGALRM - NODE_DEFINE_CONSTANT(target, SIGALRM); + NODE_DEFINE_CONSTANT(target, SIGALRM); #endif - NODE_DEFINE_CONSTANT(target, SIGTERM); + NODE_DEFINE_CONSTANT(target, SIGTERM); #ifdef SIGCHLD - NODE_DEFINE_CONSTANT(target, SIGCHLD); + NODE_DEFINE_CONSTANT(target, SIGCHLD); #endif #ifdef SIGSTKFLT - NODE_DEFINE_CONSTANT(target, SIGSTKFLT); + NODE_DEFINE_CONSTANT(target, SIGSTKFLT); #endif - #ifdef SIGCONT - NODE_DEFINE_CONSTANT(target, SIGCONT); + NODE_DEFINE_CONSTANT(target, SIGCONT); #endif #ifdef SIGSTOP - NODE_DEFINE_CONSTANT(target, SIGSTOP); + NODE_DEFINE_CONSTANT(target, SIGSTOP); #endif #ifdef SIGTSTP - NODE_DEFINE_CONSTANT(target, SIGTSTP); + NODE_DEFINE_CONSTANT(target, SIGTSTP); #endif #ifdef SIGBREAK - NODE_DEFINE_CONSTANT(target, SIGBREAK); + NODE_DEFINE_CONSTANT(target, SIGBREAK); #endif #ifdef SIGTTIN - NODE_DEFINE_CONSTANT(target, SIGTTIN); + NODE_DEFINE_CONSTANT(target, SIGTTIN); #endif #ifdef SIGTTOU - NODE_DEFINE_CONSTANT(target, SIGTTOU); + NODE_DEFINE_CONSTANT(target, SIGTTOU); #endif #ifdef SIGURG - NODE_DEFINE_CONSTANT(target, SIGURG); + NODE_DEFINE_CONSTANT(target, SIGURG); #endif #ifdef SIGXCPU - NODE_DEFINE_CONSTANT(target, SIGXCPU); + NODE_DEFINE_CONSTANT(target, SIGXCPU); #endif #ifdef SIGXFSZ - NODE_DEFINE_CONSTANT(target, SIGXFSZ); + NODE_DEFINE_CONSTANT(target, SIGXFSZ); #endif #ifdef SIGVTALRM - NODE_DEFINE_CONSTANT(target, SIGVTALRM); + NODE_DEFINE_CONSTANT(target, SIGVTALRM); #endif #ifdef SIGPROF - NODE_DEFINE_CONSTANT(target, SIGPROF); + NODE_DEFINE_CONSTANT(target, SIGPROF); #endif #ifdef SIGWINCH - NODE_DEFINE_CONSTANT(target, SIGWINCH); + NODE_DEFINE_CONSTANT(target, SIGWINCH); #endif #ifdef SIGIO - NODE_DEFINE_CONSTANT(target, SIGIO); + NODE_DEFINE_CONSTANT(target, SIGIO); #endif #ifdef SIGPOLL - NODE_DEFINE_CONSTANT(target, SIGPOLL); + NODE_DEFINE_CONSTANT(target, SIGPOLL); #endif #ifdef SIGLOST - NODE_DEFINE_CONSTANT(target, SIGLOST); + NODE_DEFINE_CONSTANT(target, SIGLOST); #endif #ifdef SIGPWR - NODE_DEFINE_CONSTANT(target, SIGPWR); + NODE_DEFINE_CONSTANT(target, SIGPWR); #endif #ifdef SIGINFO - NODE_DEFINE_CONSTANT(target, SIGINFO); + NODE_DEFINE_CONSTANT(target, SIGINFO); #endif #ifdef SIGSYS - NODE_DEFINE_CONSTANT(target, SIGSYS); + NODE_DEFINE_CONSTANT(target, SIGSYS); #endif #ifdef SIGUNUSED - NODE_DEFINE_CONSTANT(target, SIGUNUSED); + NODE_DEFINE_CONSTANT(target, SIGUNUSED); #endif } -void DefineOpenSSLConstants(Local target) { +void DefineOpenSSLConstants(Local target) +{ #ifdef SSL_OP_ALL NODE_DEFINE_CONSTANT(target, SSL_OP_ALL); #endif @@ -832,12 +835,12 @@ void DefineOpenSSLConstants(Local target) { NODE_DEFINE_CONSTANT(target, SSL_OP_NO_TLSv1_2); #endif -#ifdef SSL_OP_PKCS1_CHECK_1 - NODE_DEFINE_CONSTANT(target, SSL_OP_PKCS1_CHECK_1); +#ifdef SSL_OP_PKCS1_NODE_CHECK_1 + NODE_DEFINE_CONSTANT(target, SSL_OP_PKCS1_NODE_CHECK_1); #endif -#ifdef SSL_OP_PKCS1_CHECK_2 - NODE_DEFINE_CONSTANT(target, SSL_OP_PKCS1_CHECK_2); +#ifdef SSL_OP_PKCS1_NODE_CHECK_2 + NODE_DEFINE_CONSTANT(target, SSL_OP_PKCS1_NODE_CHECK_2); #endif #ifdef SSL_OP_SINGLE_DH_USE @@ -868,72 +871,72 @@ void DefineOpenSSLConstants(Local target) { NODE_DEFINE_CONSTANT(target, SSL_OP_TLS_ROLLBACK_BUG); #endif -# ifndef OPENSSL_NO_ENGINE +#ifndef OPENSSL_NO_ENGINE -# ifdef ENGINE_METHOD_RSA +#ifdef ENGINE_METHOD_RSA NODE_DEFINE_CONSTANT(target, ENGINE_METHOD_RSA); -# endif +#endif -# ifdef ENGINE_METHOD_DSA +#ifdef ENGINE_METHOD_DSA NODE_DEFINE_CONSTANT(target, ENGINE_METHOD_DSA); -# endif +#endif -# ifdef ENGINE_METHOD_DH +#ifdef ENGINE_METHOD_DH NODE_DEFINE_CONSTANT(target, ENGINE_METHOD_DH); -# endif +#endif -# ifdef ENGINE_METHOD_RAND +#ifdef ENGINE_METHOD_RAND NODE_DEFINE_CONSTANT(target, ENGINE_METHOD_RAND); -# endif +#endif -# ifdef ENGINE_METHOD_ECDH +#ifdef ENGINE_METHOD_ECDH NODE_DEFINE_CONSTANT(target, ENGINE_METHOD_ECDH); -# endif +#endif -# ifdef ENGINE_METHOD_ECDSA +#ifdef ENGINE_METHOD_ECDSA NODE_DEFINE_CONSTANT(target, ENGINE_METHOD_ECDSA); -# endif +#endif -# ifdef ENGINE_METHOD_CIPHERS +#ifdef ENGINE_METHOD_CIPHERS NODE_DEFINE_CONSTANT(target, ENGINE_METHOD_CIPHERS); -# endif +#endif -# ifdef ENGINE_METHOD_DIGESTS +#ifdef ENGINE_METHOD_DIGESTS NODE_DEFINE_CONSTANT(target, ENGINE_METHOD_DIGESTS); -# endif +#endif -# ifdef ENGINE_METHOD_STORE +#ifdef ENGINE_METHOD_STORE NODE_DEFINE_CONSTANT(target, ENGINE_METHOD_STORE); -# endif +#endif -# ifdef ENGINE_METHOD_PKEY_METHS +#ifdef ENGINE_METHOD_PKEY_METHS NODE_DEFINE_CONSTANT(target, ENGINE_METHOD_PKEY_METHS); -# endif +#endif -# ifdef ENGINE_METHOD_PKEY_ASN1_METHS +#ifdef ENGINE_METHOD_PKEY_ASN1_METHS NODE_DEFINE_CONSTANT(target, ENGINE_METHOD_PKEY_ASN1_METHS); -# endif +#endif -# ifdef ENGINE_METHOD_ALL +#ifdef ENGINE_METHOD_ALL NODE_DEFINE_CONSTANT(target, ENGINE_METHOD_ALL); -# endif +#endif -# ifdef ENGINE_METHOD_NONE +#ifdef ENGINE_METHOD_NONE NODE_DEFINE_CONSTANT(target, ENGINE_METHOD_NONE); -# endif +#endif -# endif // !OPENSSL_NO_ENGINE +#endif // !OPENSSL_NO_ENGINE -#ifdef DH_CHECK_P_NOT_SAFE_PRIME - NODE_DEFINE_CONSTANT(target, DH_CHECK_P_NOT_SAFE_PRIME); +#ifdef DH_NODE_CHECK_P_NOT_SAFE_PRIME + NODE_DEFINE_CONSTANT(target, DH_NODE_CHECK_P_NOT_SAFE_PRIME); #endif -#ifdef DH_CHECK_P_NOT_PRIME - NODE_DEFINE_CONSTANT(target, DH_CHECK_P_NOT_PRIME); +#ifdef DH_NODE_CHECK_P_NOT_PRIME + NODE_DEFINE_CONSTANT(target, DH_NODE_CHECK_P_NOT_PRIME); #endif -#ifdef DH_UNABLE_TO_CHECK_GENERATOR - NODE_DEFINE_CONSTANT(target, DH_UNABLE_TO_CHECK_GENERATOR); +#ifdef DH_UNABLE_TO_NODE_CHECK_GENERATOR + NODE_DEFINE_CONSTANT(target, DH_UNABLE_TO_NODE_CHECK_GENERATOR); #endif #ifdef DH_NOT_SUITABLE_GENERATOR @@ -975,193 +978,197 @@ void DefineOpenSSLConstants(Local target) { #endif #if HAVE_OPENSSL - // NOTE: These are not defines - NODE_DEFINE_CONSTANT(target, POINT_CONVERSION_COMPRESSED); + // NOTE: These are not defines + NODE_DEFINE_CONSTANT(target, POINT_CONVERSION_COMPRESSED); - NODE_DEFINE_CONSTANT(target, POINT_CONVERSION_UNCOMPRESSED); + NODE_DEFINE_CONSTANT(target, POINT_CONVERSION_UNCOMPRESSED); - NODE_DEFINE_CONSTANT(target, POINT_CONVERSION_HYBRID); + NODE_DEFINE_CONSTANT(target, POINT_CONVERSION_HYBRID); #endif } -void DefineSystemConstants(Local target) { - // file access modes - NODE_DEFINE_CONSTANT(target, O_RDONLY); - NODE_DEFINE_CONSTANT(target, O_WRONLY); - NODE_DEFINE_CONSTANT(target, O_RDWR); - - NODE_DEFINE_CONSTANT(target, S_IFMT); - NODE_DEFINE_CONSTANT(target, S_IFREG); - NODE_DEFINE_CONSTANT(target, S_IFDIR); - NODE_DEFINE_CONSTANT(target, S_IFCHR); +void DefineSystemConstants(Local target) +{ + // file access modes + NODE_DEFINE_CONSTANT(target, O_RDONLY); + NODE_DEFINE_CONSTANT(target, O_WRONLY); + NODE_DEFINE_CONSTANT(target, O_RDWR); + + NODE_DEFINE_CONSTANT(target, S_IFMT); + NODE_DEFINE_CONSTANT(target, S_IFREG); + NODE_DEFINE_CONSTANT(target, S_IFDIR); + NODE_DEFINE_CONSTANT(target, S_IFCHR); #ifdef S_IFBLK - NODE_DEFINE_CONSTANT(target, S_IFBLK); + NODE_DEFINE_CONSTANT(target, S_IFBLK); #endif #ifdef S_IFIFO - NODE_DEFINE_CONSTANT(target, S_IFIFO); + NODE_DEFINE_CONSTANT(target, S_IFIFO); #endif #ifdef S_IFLNK - NODE_DEFINE_CONSTANT(target, S_IFLNK); + NODE_DEFINE_CONSTANT(target, S_IFLNK); #endif #ifdef S_IFSOCK - NODE_DEFINE_CONSTANT(target, S_IFSOCK); + NODE_DEFINE_CONSTANT(target, S_IFSOCK); #endif #ifdef O_CREAT - NODE_DEFINE_CONSTANT(target, O_CREAT); + NODE_DEFINE_CONSTANT(target, O_CREAT); #endif #ifdef O_EXCL - NODE_DEFINE_CONSTANT(target, O_EXCL); + NODE_DEFINE_CONSTANT(target, O_EXCL); #endif #ifdef O_NOCTTY - NODE_DEFINE_CONSTANT(target, O_NOCTTY); + NODE_DEFINE_CONSTANT(target, O_NOCTTY); #endif #ifdef O_TRUNC - NODE_DEFINE_CONSTANT(target, O_TRUNC); + NODE_DEFINE_CONSTANT(target, O_TRUNC); #endif #ifdef O_APPEND - NODE_DEFINE_CONSTANT(target, O_APPEND); + NODE_DEFINE_CONSTANT(target, O_APPEND); #endif #ifdef O_DIRECTORY - NODE_DEFINE_CONSTANT(target, O_DIRECTORY); + NODE_DEFINE_CONSTANT(target, O_DIRECTORY); #endif #ifdef O_EXCL - NODE_DEFINE_CONSTANT(target, O_EXCL); + NODE_DEFINE_CONSTANT(target, O_EXCL); #endif #ifdef O_NOATIME - NODE_DEFINE_CONSTANT(target, O_NOATIME); + NODE_DEFINE_CONSTANT(target, O_NOATIME); #endif #ifdef O_NOFOLLOW - NODE_DEFINE_CONSTANT(target, O_NOFOLLOW); + NODE_DEFINE_CONSTANT(target, O_NOFOLLOW); #endif #ifdef O_SYNC - NODE_DEFINE_CONSTANT(target, O_SYNC); + NODE_DEFINE_CONSTANT(target, O_SYNC); #endif #ifdef O_SYMLINK - NODE_DEFINE_CONSTANT(target, O_SYMLINK); + NODE_DEFINE_CONSTANT(target, O_SYMLINK); #endif #ifdef O_DIRECT - NODE_DEFINE_CONSTANT(target, O_DIRECT); + NODE_DEFINE_CONSTANT(target, O_DIRECT); #endif #ifdef O_NONBLOCK - NODE_DEFINE_CONSTANT(target, O_NONBLOCK); + NODE_DEFINE_CONSTANT(target, O_NONBLOCK); #endif #ifdef S_IRWXU - NODE_DEFINE_CONSTANT(target, S_IRWXU); + NODE_DEFINE_CONSTANT(target, S_IRWXU); #endif #ifdef S_IRUSR - NODE_DEFINE_CONSTANT(target, S_IRUSR); + NODE_DEFINE_CONSTANT(target, S_IRUSR); #endif #ifdef S_IWUSR - NODE_DEFINE_CONSTANT(target, S_IWUSR); + NODE_DEFINE_CONSTANT(target, S_IWUSR); #endif #ifdef S_IXUSR - NODE_DEFINE_CONSTANT(target, S_IXUSR); + NODE_DEFINE_CONSTANT(target, S_IXUSR); #endif #ifdef S_IRWXG - NODE_DEFINE_CONSTANT(target, S_IRWXG); + NODE_DEFINE_CONSTANT(target, S_IRWXG); #endif #ifdef S_IRGRP - NODE_DEFINE_CONSTANT(target, S_IRGRP); + NODE_DEFINE_CONSTANT(target, S_IRGRP); #endif #ifdef S_IWGRP - NODE_DEFINE_CONSTANT(target, S_IWGRP); + NODE_DEFINE_CONSTANT(target, S_IWGRP); #endif #ifdef S_IXGRP - NODE_DEFINE_CONSTANT(target, S_IXGRP); + NODE_DEFINE_CONSTANT(target, S_IXGRP); #endif #ifdef S_IRWXO - NODE_DEFINE_CONSTANT(target, S_IRWXO); + NODE_DEFINE_CONSTANT(target, S_IRWXO); #endif #ifdef S_IROTH - NODE_DEFINE_CONSTANT(target, S_IROTH); + NODE_DEFINE_CONSTANT(target, S_IROTH); #endif #ifdef S_IWOTH - NODE_DEFINE_CONSTANT(target, S_IWOTH); + NODE_DEFINE_CONSTANT(target, S_IWOTH); #endif #ifdef S_IXOTH - NODE_DEFINE_CONSTANT(target, S_IXOTH); + NODE_DEFINE_CONSTANT(target, S_IXOTH); #endif #ifdef F_OK - NODE_DEFINE_CONSTANT(target, F_OK); + NODE_DEFINE_CONSTANT(target, F_OK); #endif #ifdef R_OK - NODE_DEFINE_CONSTANT(target, R_OK); + NODE_DEFINE_CONSTANT(target, R_OK); #endif #ifdef W_OK - NODE_DEFINE_CONSTANT(target, W_OK); + NODE_DEFINE_CONSTANT(target, W_OK); #endif #ifdef X_OK - NODE_DEFINE_CONSTANT(target, X_OK); + NODE_DEFINE_CONSTANT(target, X_OK); #endif } -void DefineUVConstants(Local target) { - NODE_DEFINE_CONSTANT(target, UV_UDP_REUSEADDR); +void DefineUVConstants(Local target) +{ + NODE_DEFINE_CONSTANT(target, UV_UDP_REUSEADDR); } -void DefineCryptoConstants(Local target) { +void DefineCryptoConstants(Local target) +{ #if HAVE_OPENSSL - NODE_DEFINE_STRING_CONSTANT(target, - "defaultCoreCipherList", - DEFAULT_CIPHER_LIST_CORE); - NODE_DEFINE_STRING_CONSTANT(target, - "defaultCipherList", - default_cipher_list); + NODE_DEFINE_STRING_CONSTANT(target, + "defaultCoreCipherList", + DEFAULT_CIPHER_LIST_CORE); + NODE_DEFINE_STRING_CONSTANT(target, + "defaultCipherList", + default_cipher_list); #endif } -void DefineConstants(v8::Isolate* isolate, Local target) { - Local os_constants = Object::New(isolate); - Local err_constants = Object::New(isolate); - Local sig_constants = Object::New(isolate); - Local fs_constants = Object::New(isolate); - Local crypto_constants = Object::New(isolate); - - DefineErrnoConstants(err_constants); - DefineWindowsErrorConstants(err_constants); - DefineSignalConstants(sig_constants); - DefineUVConstants(os_constants); - DefineSystemConstants(fs_constants); - DefineOpenSSLConstants(crypto_constants); - DefineCryptoConstants(crypto_constants); - - os_constants->Set(OneByteString(isolate, "errno"), err_constants); - os_constants->Set(OneByteString(isolate, "signals"), sig_constants); - target->Set(OneByteString(isolate, "os"), os_constants); - target->Set(OneByteString(isolate, "fs"), fs_constants); - target->Set(OneByteString(isolate, "crypto"), crypto_constants); +void DefineConstants(v8::Isolate* isolate, Local target) +{ + Local os_constants = Object::New(isolate); + Local err_constants = Object::New(isolate); + Local sig_constants = Object::New(isolate); + Local fs_constants = Object::New(isolate); + Local crypto_constants = Object::New(isolate); + + DefineErrnoConstants(err_constants); + DefineWindowsErrorConstants(err_constants); + DefineSignalConstants(sig_constants); + DefineUVConstants(os_constants); + DefineSystemConstants(fs_constants); + DefineOpenSSLConstants(crypto_constants); + DefineCryptoConstants(crypto_constants); + + os_constants->Set(OneByteString(isolate, "errno"), err_constants); + os_constants->Set(OneByteString(isolate, "signals"), sig_constants); + target->Set(OneByteString(isolate, "os"), os_constants); + target->Set(OneByteString(isolate, "fs"), fs_constants); + target->Set(OneByteString(isolate, "crypto"), crypto_constants); } -} // namespace node +} // namespace node diff --git a/node/src/node_constants.h b/node/src/node_constants.h index 7ba6ec3bd1..44e54838d8 100644 --- a/node/src/node_constants.h +++ b/node/src/node_constants.h @@ -1,3 +1,6 @@ +//Copyright Joyent, Inc.and other Node contributors. +//The MIT License (MIT) + #ifndef SRC_NODE_CONSTANTS_H_ #define SRC_NODE_CONSTANTS_H_ @@ -7,26 +10,26 @@ #include "v8.h" #if HAVE_OPENSSL -#define DEFAULT_CIPHER_LIST_CORE "ECDHE-RSA-AES128-GCM-SHA256:" \ - "ECDHE-ECDSA-AES128-GCM-SHA256:" \ - "ECDHE-RSA-AES256-GCM-SHA384:" \ - "ECDHE-ECDSA-AES256-GCM-SHA384:" \ - "DHE-RSA-AES128-GCM-SHA256:" \ - "ECDHE-RSA-AES128-SHA256:" \ - "DHE-RSA-AES128-SHA256:" \ - "ECDHE-RSA-AES256-SHA384:" \ - "DHE-RSA-AES256-SHA384:" \ - "ECDHE-RSA-AES256-SHA256:" \ - "DHE-RSA-AES256-SHA256:" \ - "HIGH:" \ - "!aNULL:" \ - "!eNULL:" \ - "!EXPORT:" \ - "!DES:" \ - "!RC4:" \ - "!MD5:" \ - "!PSK:" \ - "!SRP:" \ +#define DEFAULT_CIPHER_LIST_CORE "ECDHE-RSA-AES128-GCM-SHA256:" \ + "ECDHE-ECDSA-AES128-GCM-SHA256:" \ + "ECDHE-RSA-AES256-GCM-SHA384:" \ + "ECDHE-ECDSA-AES256-GCM-SHA384:" \ + "DHE-RSA-AES128-GCM-SHA256:" \ + "ECDHE-RSA-AES128-SHA256:" \ + "DHE-RSA-AES128-SHA256:" \ + "ECDHE-RSA-AES256-SHA384:" \ + "DHE-RSA-AES256-SHA384:" \ + "ECDHE-RSA-AES256-SHA256:" \ + "DHE-RSA-AES256-SHA256:" \ + "HIGH:" \ + "!aNULL:" \ + "!eNULL:" \ + "!EXPORT:" \ + "!DES:" \ + "!RC4:" \ + "!MD5:" \ + "!PSK:" \ + "!SRP:" \ "!CAMELLIA" #endif @@ -37,8 +40,8 @@ extern const char* default_cipher_list; #endif void DefineConstants(v8::Isolate* isolate, v8::Local target); -} // namespace node +} // namespace node -#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS +#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS -#endif // SRC_NODE_CONSTANTS_H_ +#endif // SRC_NODE_CONSTANTS_H_ diff --git a/node/src/node_contextify.cc b/node/src/node_contextify.cc index 56b2e8e3f7..26296dae7d 100644 --- a/node/src/node_contextify.cc +++ b/node/src/node_contextify.cc @@ -43,885 +43,872 @@ using v8::UnboundScript; using v8::Value; using v8::WeakCallbackInfo; - class ContextifyContext { - protected: - // V8 reserves the first field in context objects for the debugger. We use the - // second field to hold a reference to the sandbox object. - enum { kSandboxObjectIndex = 1 }; - - Environment* const env_; - Persistent context_; - - public: - ContextifyContext(Environment* env, Local sandbox_obj) : env_(env) { - Local v8_context = CreateV8Context(env, sandbox_obj); - context_.Reset(env->isolate(), v8_context); - - // Allocation failure or maximum call stack size reached - if (context_.IsEmpty()) - return; - context_.SetWeak(this, WeakCallback, v8::WeakCallbackType::kParameter); - context_.MarkIndependent(); - } - - - ~ContextifyContext() { - context_.Reset(); - } - - - inline Environment* env() const { - return env_; - } - - - inline Local context() const { - return PersistentToLocal(env()->isolate(), context_); - } - - - inline Local global_proxy() const { - return context()->Global(); - } - - - inline Local sandbox() const { - return Local::Cast(context()->GetEmbedderData(kSandboxObjectIndex)); - } - - // XXX(isaacs): This function only exists because of a shortcoming of - // the V8 SetNamedPropertyHandler function. - // - // It does not provide a way to intercept Object.defineProperty(..) - // calls. As a result, these properties are not copied onto the - // contextified sandbox when a new global property is added via either - // a function declaration or a Object.defineProperty(global, ...) call. - // - // Note that any function declarations or Object.defineProperty() - // globals that are created asynchronously (in a setTimeout, callback, - // etc.) will happen AFTER the call to copy properties, and thus not be - // caught. - // - // The way to properly fix this is to add some sort of a - // Object::SetNamedDefinePropertyHandler() function that takes a callback, - // which receives the property name and property descriptor as arguments. - // - // Luckily, such situations are rare, and asynchronously-added globals - // weren't supported by Node's VM module until 0.12 anyway. But, this - // should be fixed properly in V8, and this copy function should be - // removed once there is a better way. - void CopyProperties() { - HandleScope scope(env()->isolate()); - - Local context = PersistentToLocal(env()->isolate(), context_); - Local global = - context->Global()->GetPrototype()->ToObject(env()->isolate()); - Local sandbox_obj = sandbox(); - - Local clone_property_method; - - Local names = global->GetOwnPropertyNames(); - int length = names->Length(); - for (int i = 0; i < length; i++) { - Local key = names->Get(i)->ToString(env()->isolate()); - auto maybe_has = sandbox_obj->HasOwnProperty(context, key); - - // Check for pending exceptions - if (!maybe_has.IsJust()) - break; - - bool has = maybe_has.FromJust(); - - if (!has) { - // Could also do this like so: - // - // PropertyAttribute att = global->GetPropertyAttributes(key_v); - // Local val = global->Get(key_v); - // sandbox->ForceSet(key_v, val, att); - // - // However, this doesn't handle ES6-style properties configured with - // Object.defineProperty, and that's exactly what we're up against at - // this point. ForceSet(key,val,att) only supports value properties - // with the ES3-style attribute flags (DontDelete/DontEnum/ReadOnly), - // which doesn't faithfully capture the full range of configurations - // that can be done using Object.defineProperty. - if (clone_property_method.IsEmpty()) { - Local code = FIXED_ONE_BYTE_STRING(env()->isolate(), - "(function cloneProperty(source, key, target) {\n" - " if (key === 'Proxy') return;\n" - " try {\n" - " var desc = Object.getOwnPropertyDescriptor(source, key);\n" - " if (desc.value === source) desc.value = target;\n" - " Object.defineProperty(target, key, desc);\n" - " } catch (e) {\n" - " // Catch sealed properties errors\n" - " }\n" - "})"); - - Local