diff --git a/src/env_properties.h b/src/env_properties.h index 96e60c12d2b47d..6236c565e9a8e0 100644 --- a/src/env_properties.h +++ b/src/env_properties.h @@ -83,7 +83,6 @@ V(base_string, "base") \ V(base_url_string, "baseURL") \ V(bits_string, "bits") \ - V(block_list_string, "blockList") \ V(buffer_string, "buffer") \ V(bytes_parsed_string, "bytesParsed") \ V(bytes_read_string, "bytesRead") \ @@ -92,11 +91,9 @@ V(cached_data_produced_string, "cachedDataProduced") \ V(cached_data_rejected_string, "cachedDataRejected") \ V(cached_data_string, "cachedData") \ - V(cache_key_string, "cacheKey") \ V(cert_usage_string, "certUsage") \ V(change_string, "change") \ V(changes_string, "changes") \ - V(channel_string, "channel") \ V(chunks_sent_since_last_write_string, "chunksSentSinceLastWrite") \ V(client_id_string, "clientId") \ V(clone_unsupported_type_str, "Cannot clone object of unsupported type.") \ @@ -105,9 +102,6 @@ "transferList") \ V(clone_untransferable_str, "Found invalid value in transferList.") \ V(code_string, "code") \ - V(column_number_string, "columnNumber") \ - V(column_string, "column") \ - V(commonjs_string, "commonjs") \ V(config_string, "config") \ V(constants_string, "constants") \ V(crypto_dh_string, "dh") \ @@ -139,7 +133,6 @@ V(crypto_rsa_pss_string, "rsa-pss") \ V(cwd_string, "cwd") \ V(data_string, "data") \ - V(database_string, "database") \ V(default_is_true_string, "defaultIsTrue") \ V(deserialize_info_string, "deserializeInfo") \ V(dest_string, "dest") \ @@ -166,10 +159,8 @@ V(ecdh_string, "ECDH") \ V(emit_string, "emit") \ V(emit_warning_string, "emitWarning") \ - V(empty_object_string, "{}") \ V(encoding_string, "encoding") \ V(entries_string, "entries") \ - V(entry_type_string, "entryType") \ V(env_pairs_string, "envPairs") \ V(env_var_settings_string, "envVarSettings") \ V(err_sqlite_error_string, "ERR_SQLITE_ERROR") \ @@ -197,10 +188,8 @@ V(fingerprint_string, "fingerprint") \ V(flags_string, "flags") \ V(flowlabel_string, "flowlabel") \ - V(fragment_string, "fragment") \ V(frames_received_string, "framesReceived") \ V(frames_sent_string, "framesSent") \ - V(function_name_string, "functionName") \ V(function_string, "function") \ V(get_string, "get") \ V(get_data_clone_error_string, "_getDataCloneError") \ @@ -228,14 +217,10 @@ V(infoaccess_string, "infoAccess") \ V(inherit_string, "inherit") \ V(input_string, "input") \ - V(inputs_string, "inputs") \ - V(internal_binding_string, "internalBinding") \ - V(internal_string, "internal") \ V(inverse_string, "inverse") \ V(ipv4_string, "IPv4") \ V(ipv6_string, "IPv6") \ V(isclosing_string, "isClosing") \ - V(isfinished_string, "isFinished") \ V(issuer_string, "issuer") \ V(issuercert_string, "issuerCertificate") \ V(iterator_string, "Iterator") \ @@ -267,7 +252,6 @@ V(last_insert_rowid_string, "lastInsertRowid") \ V(length_string, "length") \ V(library_string, "library") \ - V(line_number_string, "lineNumber") \ V(loop_count, "loopCount") \ V(mac_string, "mac") \ V(match_string, "match") \ @@ -285,12 +269,10 @@ V(modulus_length_string, "modulusLength") \ V(name_string, "name") \ V(named_curve_string, "namedCurve") \ - V(netmask_string, "netmask") \ V(next_string, "next") \ V(nistcurve_string, "nistCurve") \ V(node_string, "node") \ V(nsname_string, "nsname") \ - V(num_cols_string, "num_cols") \ V(object_string, "Object") \ V(ocsp_request_string, "OCSPRequest") \ V(oncertcb_string, "oncertcb") \ @@ -348,7 +330,6 @@ V(psk_string, "psk") \ V(pubkey_string, "pubkey") \ V(public_exponent_string, "publicExponent") \ - V(query_string, "query") \ V(rate_string, "rate") \ V(raw_string, "raw") \ V(read_host_object_string, "_readHostObject") \ @@ -366,17 +347,11 @@ "export * from 'original'; export { default } from 'original'; export " \ "const __esModule = true;") \ V(require_string, "require") \ - V(resolve_string, "resolve") \ V(resource_string, "resource") \ V(result_string, "result") \ V(retry_string, "retry") \ V(return_arrays_string, "returnArrays") \ - V(return_string, "return") \ V(salt_length_string, "saltLength") \ - V(scheme_string, "scheme") \ - V(scopeid_string, "scopeid") \ - V(script_id_string, "scriptId") \ - V(script_name_string, "scriptName") \ V(search_string, "search") \ V(selector_string, "selector") \ V(serial_number_string, "serialNumber") \ @@ -398,9 +373,7 @@ V(stack_string, "stack") \ V(standard_name_string, "standardName") \ V(start_string, "start") \ - V(start_time_string, "startTime") \ V(state_string, "state") \ - V(statement_string, "statement") \ V(stats_string, "stats") \ V(status_string, "status") \ V(stdio_string, "stdio") \ @@ -428,12 +401,6 @@ V(type_string, "type") \ V(uid_string, "uid") \ V(unknown_string, "") \ - V(url_special_ftp_string, "ftp:") \ - V(url_special_file_string, "file:") \ - V(url_special_http_string, "http:") \ - V(url_special_https_string, "https:") \ - V(url_special_ws_string, "ws:") \ - V(url_special_wss_string, "wss:") \ V(url_string, "url") \ V(username_string, "username") \ V(valid_from_string, "valid_from") \ @@ -447,57 +414,58 @@ V(wrap_string, "wrap") \ V(writable_string, "writable") \ V(write_host_object_string, "_writeHostObject") \ - V(write_queue_size_string, "writeQueueSize") \ - V(x_forwarded_string, "x-forwarded-for") + V(write_queue_size_string, "writeQueueSize") #define PER_ISOLATE_TEMPLATE_PROPERTIES(V) \ V(async_wrap_ctor_template, v8::FunctionTemplate) \ - V(async_wrap_object_ctor_template, v8::FunctionTemplate) \ V(binding_data_default_template, v8::ObjectTemplate) \ V(blob_constructor_template, v8::FunctionTemplate) \ V(blob_reader_constructor_template, v8::FunctionTemplate) \ V(blocklist_constructor_template, v8::FunctionTemplate) \ + V(callsite_template, v8::DictionaryTemplate) \ V(contextify_global_template, v8::ObjectTemplate) \ V(contextify_wrapper_template, v8::ObjectTemplate) \ + V(cpu_usage_template, v8::DictionaryTemplate) \ V(crypto_key_object_handle_constructor, v8::FunctionTemplate) \ V(env_proxy_template, v8::ObjectTemplate) \ V(env_proxy_ctor_template, v8::FunctionTemplate) \ V(dir_instance_template, v8::ObjectTemplate) \ V(fd_constructor_template, v8::ObjectTemplate) \ V(fdclose_constructor_template, v8::ObjectTemplate) \ - V(fdentry_constructor_template, v8::FunctionTemplate) \ V(filehandlereadwrap_template, v8::ObjectTemplate) \ + V(free_list_statistics_template, v8::DictionaryTemplate) \ V(fsreqpromise_constructor_template, v8::ObjectTemplate) \ V(handle_wrap_ctor_template, v8::FunctionTemplate) \ + V(heap_statistics_template, v8::DictionaryTemplate) \ + V(v8_heap_statistics_template, v8::DictionaryTemplate) \ V(histogram_ctor_template, v8::FunctionTemplate) \ V(http2settings_constructor_template, v8::ObjectTemplate) \ V(http2stream_constructor_template, v8::ObjectTemplate) \ V(http2ping_constructor_template, v8::ObjectTemplate) \ V(i18n_converter_template, v8::ObjectTemplate) \ V(intervalhistogram_constructor_template, v8::FunctionTemplate) \ + V(iter_template, v8::DictionaryTemplate) \ V(js_transferable_constructor_template, v8::FunctionTemplate) \ V(libuv_stream_wrap_ctor_template, v8::FunctionTemplate) \ V(lock_holder_constructor_template, v8::FunctionTemplate) \ V(message_port_constructor_template, v8::FunctionTemplate) \ V(module_wrap_constructor_template, v8::FunctionTemplate) \ - V(microtask_queue_ctor_template, v8::FunctionTemplate) \ + V(object_stats_template, v8::DictionaryTemplate) \ + V(page_stats_template, v8::DictionaryTemplate) \ V(pipe_constructor_template, v8::FunctionTemplate) \ - V(promise_wrap_template, v8::ObjectTemplate) \ - V(sab_lifetimepartner_constructor_template, v8::FunctionTemplate) \ V(script_context_constructor_template, v8::FunctionTemplate) \ V(secure_context_constructor_template, v8::FunctionTemplate) \ V(shutdown_wrap_template, v8::ObjectTemplate) \ V(socketaddress_constructor_template, v8::FunctionTemplate) \ + V(space_stats_template, v8::DictionaryTemplate) \ + V(sqlite_column_template, v8::DictionaryTemplate) \ V(sqlite_statement_sync_constructor_template, v8::FunctionTemplate) \ V(sqlite_statement_sync_iterator_constructor_template, v8::FunctionTemplate) \ V(sqlite_session_constructor_template, v8::FunctionTemplate) \ - V(streambaseentry_ctor_template, v8::FunctionTemplate) \ V(streambaseoutputstream_constructor_template, v8::ObjectTemplate) \ - V(streamentry_ctor_template, v8::FunctionTemplate) \ - V(streamentry_opaque_ctor_template, v8::FunctionTemplate) \ - V(qlogoutputstream_constructor_template, v8::ObjectTemplate) \ V(tcp_constructor_template, v8::FunctionTemplate) \ V(tty_constructor_template, v8::FunctionTemplate) \ + V(urlpatternresult_template, v8::DictionaryTemplate) \ V(write_wrap_template, v8::ObjectTemplate) \ V(worker_cpu_profile_taker_template, v8::ObjectTemplate) \ V(worker_cpu_usage_taker_template, v8::ObjectTemplate) \ @@ -514,11 +482,9 @@ V(async_hooks_init_function, v8::Function) \ V(async_hooks_promise_resolve_function, v8::Function) \ V(buffer_prototype_object, v8::Object) \ - V(crypto_key_object_constructor, v8::Function) \ V(crypto_key_object_private_constructor, v8::Function) \ V(crypto_key_object_public_constructor, v8::Function) \ V(crypto_key_object_secret_constructor, v8::Function) \ - V(domexception_function, v8::Function) \ V(enhance_fatal_stack_after_inspector, v8::Function) \ V(enhance_fatal_stack_before_inspector, v8::Function) \ V(get_source_map_error_source, v8::Function) \ @@ -555,7 +521,6 @@ V(primordials_safe_set_prototype_object, v8::Object) \ V(primordials_safe_weak_map_prototype_object, v8::Object) \ V(primordials_safe_weak_set_prototype_object, v8::Object) \ - V(promise_hook_handler, v8::Function) \ V(promise_reject_callback, v8::Function) \ V(snapshot_serialize_callback, v8::Function) \ V(snapshot_deserialize_callback, v8::Function) \ @@ -566,7 +531,6 @@ V(tls_wrap_constructor_function, v8::Function) \ V(trace_category_state_function, v8::Function) \ V(udp_constructor_function, v8::Function) \ - V(url_constructor_function, v8::Function) \ V(wasm_streaming_compilation_impl, v8::Function) \ V(wasm_streaming_object_constructor, v8::Function) diff --git a/src/node_sqlite.cc b/src/node_sqlite.cc index 54620e95d06019..924ed5789ba364 100644 --- a/src/node_sqlite.cc +++ b/src/node_sqlite.cc @@ -24,6 +24,7 @@ using v8::BigInt; using v8::Boolean; using v8::ConstructorBehavior; using v8::Context; +using v8::DictionaryTemplate; using v8::DontDelete; using v8::Exception; using v8::Function; @@ -119,6 +120,18 @@ using v8::Value; } \ } while (0) +namespace { +Local getLazyIterTemplate(Environment* env) { + auto iter_template = env->iter_template(); + if (iter_template.IsEmpty()) { + static constexpr std::string_view iter_keys[] = {"done", "value"}; + iter_template = DictionaryTemplate::New(env->isolate(), iter_keys); + env->set_iter_template(iter_template); + } + return iter_template; +} +} // namespace + inline MaybeLocal CreateSQLiteError(Isolate* isolate, const char* message) { Local js_msg; @@ -2239,58 +2252,35 @@ void StatementSync::Columns(const FunctionCallbackInfo& args) { int num_cols = sqlite3_column_count(stmt->statement_); Isolate* isolate = env->isolate(); LocalVector cols(isolate); - LocalVector col_keys(isolate, - {env->column_string(), - env->database_string(), - env->name_string(), - env->table_string(), - env->type_string()}); - Local value; + auto sqlite_column_template = env->sqlite_column_template(); + if (sqlite_column_template.IsEmpty()) { + static constexpr std::string_view col_keys[] = { + "column", "database", "name", "table", "type"}; + sqlite_column_template = DictionaryTemplate::New(isolate, col_keys); + env->set_sqlite_column_template(sqlite_column_template); + } cols.reserve(num_cols); for (int i = 0; i < num_cols; ++i) { - LocalVector col_values(isolate); - col_values.reserve(col_keys.size()); - - if (!NullableSQLiteStringToValue( - isolate, sqlite3_column_origin_name(stmt->statement_, i)) - .ToLocal(&value)) { - return; - } - col_values.emplace_back(value); - - if (!NullableSQLiteStringToValue( - isolate, sqlite3_column_database_name(stmt->statement_, i)) - .ToLocal(&value)) { - return; - } - col_values.emplace_back(value); - - if (!stmt->ColumnNameToName(i).ToLocal(&value)) { - return; - } - col_values.emplace_back(value); - - if (!NullableSQLiteStringToValue( - isolate, sqlite3_column_table_name(stmt->statement_, i)) - .ToLocal(&value)) { - return; - } - col_values.emplace_back(value); - - if (!NullableSQLiteStringToValue( - isolate, sqlite3_column_decltype(stmt->statement_, i)) - .ToLocal(&value)) { + MaybeLocal values[] = { + NullableSQLiteStringToValue( + isolate, sqlite3_column_origin_name(stmt->statement_, i)), + NullableSQLiteStringToValue( + isolate, sqlite3_column_database_name(stmt->statement_, i)), + stmt->ColumnNameToName(i), + NullableSQLiteStringToValue( + isolate, sqlite3_column_table_name(stmt->statement_, i)), + NullableSQLiteStringToValue( + isolate, sqlite3_column_decltype(stmt->statement_, i)), + }; + + Local col; + if (!NewDictionaryInstanceNullProto( + env->context(), sqlite_column_template, values) + .ToLocal(&col)) { return; } - col_values.emplace_back(value); - - Local column = Object::New(isolate, - Null(isolate), - col_keys.data(), - col_values.data(), - col_keys.size()); - cols.emplace_back(column); + cols.emplace_back(col); } args.GetReturnValue().Set(Array::New(isolate, cols.data(), cols.size())); @@ -2522,15 +2512,19 @@ void StatementSyncIterator::Next(const FunctionCallbackInfo& args) { THROW_AND_RETURN_ON_BAD_STATE( env, iter->stmt_->IsFinalized(), "statement has been finalized"); Isolate* isolate = env->isolate(); - LocalVector keys(isolate, {env->done_string(), env->value_string()}); + + auto iter_template = getLazyIterTemplate(env); if (iter->done_) { - LocalVector values(isolate, - {Boolean::New(isolate, true), Null(isolate)}); - DCHECK_EQ(values.size(), keys.size()); - Local result = Object::New( - isolate, Null(isolate), keys.data(), values.data(), keys.size()); - args.GetReturnValue().Set(result); + MaybeLocal values[]{ + Boolean::New(isolate, true), + Null(isolate), + }; + Local result; + if (NewDictionaryInstanceNullProto(env->context(), iter_template, values) + .ToLocal(&result)) { + args.GetReturnValue().Set(result); + } return; } @@ -2539,12 +2533,12 @@ void StatementSyncIterator::Next(const FunctionCallbackInfo& args) { CHECK_ERROR_OR_THROW( env->isolate(), iter->stmt_->db_.get(), r, SQLITE_DONE, void()); sqlite3_reset(iter->stmt_->statement_); - LocalVector values(isolate, - {Boolean::New(isolate, true), Null(isolate)}); - DCHECK_EQ(values.size(), keys.size()); - Local result = Object::New( - isolate, Null(isolate), keys.data(), values.data(), keys.size()); - args.GetReturnValue().Set(result); + MaybeLocal values[] = {Boolean::New(isolate, true), Null(isolate)}; + Local result; + if (NewDictionaryInstanceNullProto(env->context(), iter_template, values) + .ToLocal(&result)) { + args.GetReturnValue().Set(result); + } return; } @@ -2572,11 +2566,12 @@ void StatementSyncIterator::Next(const FunctionCallbackInfo& args) { isolate, Null(isolate), row_keys.data(), row_values.data(), num_cols); } - LocalVector values(isolate, {Boolean::New(isolate, false), row_value}); - DCHECK_EQ(keys.size(), values.size()); - Local result = Object::New( - isolate, Null(isolate), keys.data(), values.data(), keys.size()); - args.GetReturnValue().Set(result); + MaybeLocal values[] = {Boolean::New(isolate, false), row_value}; + Local result; + if (NewDictionaryInstanceNullProto(env->context(), iter_template, values) + .ToLocal(&result)) { + args.GetReturnValue().Set(result); + } } void StatementSyncIterator::Return(const FunctionCallbackInfo& args) { @@ -2589,14 +2584,15 @@ void StatementSyncIterator::Return(const FunctionCallbackInfo& args) { sqlite3_reset(iter->stmt_->statement_); iter->done_ = true; - LocalVector keys(isolate, {env->done_string(), env->value_string()}); - LocalVector values(isolate, - {Boolean::New(isolate, true), Null(isolate)}); - DCHECK_EQ(keys.size(), values.size()); - Local result = Object::New( - isolate, Null(isolate), keys.data(), values.data(), keys.size()); - args.GetReturnValue().Set(result); + auto iter_template = getLazyIterTemplate(env); + MaybeLocal values[] = {Boolean::New(isolate, true), Null(isolate)}; + + Local result; + if (NewDictionaryInstanceNullProto(env->context(), iter_template, values) + .ToLocal(&result)) { + args.GetReturnValue().Set(result); + } } Session::Session(Environment* env, diff --git a/src/node_url_pattern.cc b/src/node_url_pattern.cc index e84e9b0de9ab20..f1bddaeab0260e 100644 --- a/src/node_url_pattern.cc +++ b/src/node_url_pattern.cc @@ -54,13 +54,13 @@ namespace node::url_pattern { using v8::Array; using v8::Context; +using v8::DictionaryTemplate; using v8::DontDelete; using v8::FunctionCallbackInfo; using v8::FunctionTemplate; using v8::Global; using v8::Isolate; using v8::Local; -using v8::LocalVector; using v8::MaybeLocal; using v8::Name; using v8::NewStringType; @@ -396,56 +396,49 @@ MaybeLocal URLPattern::URLPatternComponentResult::ToJSObject( MaybeLocal URLPattern::URLPatternResult::ToJSValue( Environment* env, const ada::url_pattern_result& result) { auto isolate = env->isolate(); - Local names[] = { - env->inputs_string(), - env->protocol_string(), - env->username_string(), - env->password_string(), - env->hostname_string(), - env->port_string(), - env->pathname_string(), - env->search_string(), - env->hash_string(), - }; - LocalVector inputs(isolate, result.inputs.size()); - size_t index = 0; - for (auto& input : result.inputs) { - if (std::holds_alternative(input)) { - auto input_str = std::get(input); - if (!ToV8Value(env->context(), input_str).ToLocal(&inputs[index])) { - return {}; - } - } else { - DCHECK(std::holds_alternative(input)); - auto init = std::get(input); - if (!URLPatternInit::ToJsObject(env, init).ToLocal(&inputs[index])) { - return {}; - } - } - index++; - } - LocalVector values(isolate, arraysize(names)); - values[0] = Array::New(isolate, inputs.data(), inputs.size()); - if (!URLPatternComponentResult::ToJSObject(env, result.protocol) - .ToLocal(&values[1]) || - !URLPatternComponentResult::ToJSObject(env, result.username) - .ToLocal(&values[2]) || - !URLPatternComponentResult::ToJSObject(env, result.password) - .ToLocal(&values[3]) || - !URLPatternComponentResult::ToJSObject(env, result.hostname) - .ToLocal(&values[4]) || - !URLPatternComponentResult::ToJSObject(env, result.port) - .ToLocal(&values[5]) || - !URLPatternComponentResult::ToJSObject(env, result.pathname) - .ToLocal(&values[6]) || - !URLPatternComponentResult::ToJSObject(env, result.search) - .ToLocal(&values[7]) || - !URLPatternComponentResult::ToJSObject(env, result.hash) - .ToLocal(&values[8])) { - return {}; + + auto tmpl = env->urlpatternresult_template(); + if (tmpl.IsEmpty()) { + static constexpr std::string_view namesVec[] = { + "inputs", + "protocol", + "username", + "password", + "hostname", + "port", + "pathname", + "search", + "hash", + }; + tmpl = DictionaryTemplate::New(isolate, namesVec); + env->set_urlpatternresult_template(tmpl); } - return Object::New( - isolate, Object::New(isolate), names, values.data(), values.size()); + + size_t index = 0; + MaybeLocal vals[] = { + Array::New(env->context(), + result.inputs.size(), + [&index, &inputs = result.inputs, env]() { + auto& input = inputs[index++]; + if (std::holds_alternative(input)) { + auto input_str = std::get(input); + return ToV8Value(env->context(), input_str); + } else { + DCHECK( + std::holds_alternative(input)); + auto init = std::get(input); + return URLPatternInit::ToJsObject(env, init); + } + }), + URLPatternComponentResult::ToJSObject(env, result.protocol), + URLPatternComponentResult::ToJSObject(env, result.username), + URLPatternComponentResult::ToJSObject(env, result.password), + URLPatternComponentResult::ToJSObject(env, result.hostname), + URLPatternComponentResult::ToJSObject(env, result.port), + URLPatternComponentResult::ToJSObject(env, result.pathname), + URLPatternComponentResult::ToJSObject(env, result.search), + URLPatternComponentResult::ToJSObject(env, result.hash)}; + return NewDictionaryInstanceNullProto(env->context(), tmpl, vals); } std::optional diff --git a/src/node_util.cc b/src/node_util.cc index 1972d30b9b3899..36bd7c0028153a 100644 --- a/src/node_util.cc +++ b/src/node_util.cc @@ -15,6 +15,7 @@ using v8::BigInt; using v8::Boolean; using v8::CFunction; using v8::Context; +using v8::DictionaryTemplate; using v8::External; using v8::FunctionCallbackInfo; using v8::IndexFilter; @@ -23,6 +24,7 @@ using v8::Isolate; using v8::KeyCollectionMode; using v8::Local; using v8::LocalVector; +using v8::MaybeLocal; using v8::Name; using v8::Object; using v8::ObjectTemplate; @@ -263,6 +265,20 @@ static void GetCallSites(const FunctionCallbackInfo& args) { const int frame_count = stack->GetFrameCount(); LocalVector callsite_objects(isolate); + auto callsite_template = env->callsite_template(); + if (callsite_template.IsEmpty()) { + static constexpr std::string_view names[] = { + "functionName", + "scriptId", + "scriptName", + "lineNumber", + "columnNumber", + // TODO(legendecas): deprecate CallSite.column. + "column"}; + callsite_template = DictionaryTemplate::New(isolate, names); + env->set_callsite_template(callsite_template); + } + // Frame 0 is node:util. It should be skipped. for (int i = 1; i < frame_count; ++i) { Local stack_frame = stack->GetFrame(isolate, i); @@ -279,16 +295,7 @@ static void GetCallSites(const FunctionCallbackInfo& args) { std::string script_id = std::to_string(stack_frame->GetScriptId()); - Local names[] = { - env->function_name_string(), - env->script_id_string(), - env->script_name_string(), - env->line_number_string(), - env->column_number_string(), - // TODO(legendecas): deprecate CallSite.column. - env->column_string(), - }; - Local values[] = { + MaybeLocal values[] = { function_name, OneByteString(isolate, script_id), script_name, @@ -297,10 +304,14 @@ static void GetCallSites(const FunctionCallbackInfo& args) { // TODO(legendecas): deprecate CallSite.column. Integer::NewFromUnsigned(isolate, stack_frame->GetColumn()), }; - Local obj = Object::New( - isolate, v8::Null(isolate), names, values, arraysize(names)); - callsite_objects.push_back(obj); + Local callsite; + if (!NewDictionaryInstanceNullProto( + env->context(), callsite_template, values) + .ToLocal(&callsite)) { + return; + } + callsite_objects.push_back(callsite); } Local callsites = diff --git a/src/node_v8.cc b/src/node_v8.cc index 9f3c721680fabf..4d2c86e2da7429 100644 --- a/src/node_v8.cc +++ b/src/node_v8.cc @@ -35,6 +35,7 @@ using v8::Array; using v8::BigInt; using v8::CFunction; using v8::Context; +using v8::DictionaryTemplate; using v8::FunctionCallbackInfo; using v8::FunctionTemplate; using v8::HandleScope; @@ -46,7 +47,6 @@ using v8::Isolate; using v8::Local; using v8::LocalVector; using v8::MaybeLocal; -using v8::Name; using v8::Object; using v8::ScriptCompiler; using v8::String; @@ -326,9 +326,61 @@ static void SetHeapStatistics(JSONWriter* writer, Isolate* isolate) { static MaybeLocal ConvertHeapStatsToJSObject( Isolate* isolate, const cppgc::HeapStatistics& stats) { Local context = isolate->GetCurrentContext(); + Environment* env = Environment::GetCurrent(isolate); // Space Statistics LocalVector space_statistics_array(isolate); space_statistics_array.reserve(stats.space_stats.size()); + + auto object_stats_template = env->object_stats_template(); + auto page_stats_tmpl = env->page_stats_template(); + auto free_list_statistics_template = env->free_list_statistics_template(); + auto space_stats_tmpl = env->space_stats_template(); + auto heap_stats_tmpl = env->v8_heap_statistics_template(); + if (object_stats_template.IsEmpty()) { + static constexpr std::string_view object_stats_names[] = {"allocated_bytes", + "object_count"}; + object_stats_template = + DictionaryTemplate::New(isolate, object_stats_names); + env->set_object_stats_template(object_stats_template); + } + if (page_stats_tmpl.IsEmpty()) { + static constexpr std::string_view page_stats_names[] = { + "committed_size_bytes", + "resident_size_bytes", + "used_size_bytes", + "object_statistics"}; + page_stats_tmpl = DictionaryTemplate::New(isolate, page_stats_names); + env->set_page_stats_template(page_stats_tmpl); + } + if (free_list_statistics_template.IsEmpty()) { + std::string_view free_list_statistics_names[] = { + "bucket_size", "free_count", "free_size"}; + free_list_statistics_template = + DictionaryTemplate::New(isolate, free_list_statistics_names); + env->set_free_list_statistics_template(free_list_statistics_template); + } + if (space_stats_tmpl.IsEmpty()) { + static constexpr std::string_view space_stats_names[] = { + "name", + "committed_size_bytes", + "resident_size_bytes", + "used_size_bytes", + "page_stats", + "free_list_stats"}; + space_stats_tmpl = DictionaryTemplate::New(isolate, space_stats_names); + env->set_space_stats_template(space_stats_tmpl); + } + if (heap_stats_tmpl.IsEmpty()) { + static constexpr std::string_view heap_statistics_names[] = { + "committed_size_bytes", + "resident_size_bytes", + "used_size_bytes", + "space_statistics", + "type_names"}; + heap_stats_tmpl = DictionaryTemplate::New(isolate, heap_statistics_names); + env->set_v8_heap_statistics_template(heap_stats_tmpl); + } + for (size_t i = 0; i < stats.space_stats.size(); i++) { const cppgc::HeapStatistics::SpaceStatistics& space_stats = stats.space_stats[i]; @@ -344,30 +396,22 @@ static MaybeLocal ConvertHeapStatsToJSObject( for (size_t k = 0; k < page_stats.object_statistics.size(); k++) { const cppgc::HeapStatistics::ObjectStatsEntry& object_stats = page_stats.object_statistics[k]; - Local object_stats_names[] = { - FIXED_ONE_BYTE_STRING(isolate, "allocated_bytes"), - FIXED_ONE_BYTE_STRING(isolate, "object_count")}; - Local object_stats_values[] = { + MaybeLocal object_stats_values[] = { Uint32::NewFromUnsigned( isolate, static_cast(object_stats.allocated_bytes)), Uint32::NewFromUnsigned( isolate, static_cast(object_stats.object_count))}; - Local object_stats_object = - Object::New(isolate, - Null(isolate), - object_stats_names, - object_stats_values, - arraysize(object_stats_names)); + Local object_stats_object; + if (!NewDictionaryInstanceNullProto( + context, object_stats_template, object_stats_values) + .ToLocal(&object_stats_object)) { + return MaybeLocal(); + } object_statistics_array.emplace_back(object_stats_object); } // Set page statistics - Local page_stats_names[] = { - FIXED_ONE_BYTE_STRING(isolate, "committed_size_bytes"), - FIXED_ONE_BYTE_STRING(isolate, "resident_size_bytes"), - FIXED_ONE_BYTE_STRING(isolate, "used_size_bytes"), - FIXED_ONE_BYTE_STRING(isolate, "object_statistics")}; - Local page_stats_values[] = { + MaybeLocal page_stats_values[] = { Uint32::NewFromUnsigned( isolate, static_cast(page_stats.committed_size_bytes)), Uint32::NewFromUnsigned( @@ -377,21 +421,17 @@ static MaybeLocal ConvertHeapStatsToJSObject( Array::New(isolate, object_statistics_array.data(), object_statistics_array.size())}; - Local page_stats_object = - Object::New(isolate, - Null(isolate), - page_stats_names, - page_stats_values, - arraysize(page_stats_names)); + Local page_stats_object; + if (!NewDictionaryInstanceNullProto( + context, page_stats_tmpl, page_stats_values) + .ToLocal(&page_stats_object)) { + return MaybeLocal(); + } page_statistics_array.emplace_back(page_stats_object); } // Free List Statistics - Local free_list_statistics_names[] = { - FIXED_ONE_BYTE_STRING(isolate, "bucket_size"), - FIXED_ONE_BYTE_STRING(isolate, "free_count"), - FIXED_ONE_BYTE_STRING(isolate, "free_size")}; - Local free_list_statistics_values[] = { + MaybeLocal free_list_statistics_values[] = { ToV8ValuePrimitiveArray( context, space_stats.free_list_stats.bucket_size, isolate), ToV8ValuePrimitiveArray( @@ -399,28 +439,21 @@ static MaybeLocal ConvertHeapStatsToJSObject( ToV8ValuePrimitiveArray( context, space_stats.free_list_stats.free_size, isolate)}; - Local free_list_statistics_obj = - Object::New(isolate, - Null(isolate), - free_list_statistics_names, - free_list_statistics_values, - arraysize(free_list_statistics_names)); + Local free_list_statistics_obj; + if (!NewDictionaryInstanceNullProto(context, + free_list_statistics_template, + free_list_statistics_values) + .ToLocal(&free_list_statistics_obj)) { + return MaybeLocal(); + } // Set Space Statistics - Local space_stats_names[] = { - FIXED_ONE_BYTE_STRING(isolate, "name"), - FIXED_ONE_BYTE_STRING(isolate, "committed_size_bytes"), - FIXED_ONE_BYTE_STRING(isolate, "resident_size_bytes"), - FIXED_ONE_BYTE_STRING(isolate, "used_size_bytes"), - FIXED_ONE_BYTE_STRING(isolate, "page_stats"), - FIXED_ONE_BYTE_STRING(isolate, "free_list_stats")}; - Local name_value; if (!ToV8Value(context, stats.space_stats[i].name, isolate) .ToLocal(&name_value)) { return MaybeLocal(); } - Local space_stats_values[] = { + MaybeLocal space_stats_values[] = { name_value, Uint32::NewFromUnsigned( isolate, @@ -436,29 +469,21 @@ static MaybeLocal ConvertHeapStatsToJSObject( page_statistics_array.size()), free_list_statistics_obj, }; - Local space_stats_object = - Object::New(isolate, - Null(isolate), - space_stats_names, - space_stats_values, - arraysize(space_stats_names)); + Local space_stats_object; + if (!NewDictionaryInstanceNullProto( + context, space_stats_tmpl, space_stats_values) + .ToLocal(&space_stats_object)) { + return MaybeLocal(); + } space_statistics_array.emplace_back(space_stats_object); } - // Set heap statistics - Local heap_statistics_names[] = { - FIXED_ONE_BYTE_STRING(isolate, "committed_size_bytes"), - FIXED_ONE_BYTE_STRING(isolate, "resident_size_bytes"), - FIXED_ONE_BYTE_STRING(isolate, "used_size_bytes"), - FIXED_ONE_BYTE_STRING(isolate, "space_statistics"), - FIXED_ONE_BYTE_STRING(isolate, "type_names")}; - Local type_names_value; if (!ToV8Value(context, stats.type_names, isolate) .ToLocal(&type_names_value)) { return MaybeLocal(); } - Local heap_statistics_values[] = { + MaybeLocal heap_statistics_values[] = { Uint32::NewFromUnsigned( isolate, static_cast(stats.committed_size_bytes)), Uint32::NewFromUnsigned(isolate, @@ -470,14 +495,8 @@ static MaybeLocal ConvertHeapStatsToJSObject( space_statistics_array.size()), type_names_value}; - Local heap_statistics_object = - Object::New(isolate, - Null(isolate), - heap_statistics_names, - heap_statistics_values, - arraysize(heap_statistics_names)); - - return heap_statistics_object; + return NewDictionaryInstanceNullProto( + context, heap_stats_tmpl, heap_statistics_values); } static void GetCppHeapStatistics(const FunctionCallbackInfo& args) { diff --git a/src/node_worker.cc b/src/node_worker.cc index 0b606092a466e2..9518ab9d812f21 100644 --- a/src/node_worker.cc +++ b/src/node_worker.cc @@ -27,16 +27,18 @@ using v8::Context; using v8::CpuProfile; using v8::CpuProfilingResult; using v8::CpuProfilingStatus; +using v8::DictionaryTemplate; using v8::Float64Array; 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::Maybe; -using v8::Name; +using v8::MaybeLocal; using v8::NewStringType; using v8::Null; using v8::Number; @@ -874,11 +876,17 @@ void Worker::CpuUsage(const FunctionCallbackInfo& args) { argv[0] = UVException( isolate, err, "uv_getrusage_thread", nullptr, nullptr, nullptr); } else { - Local names[] = { - FIXED_ONE_BYTE_STRING(isolate, "user"), - FIXED_ONE_BYTE_STRING(isolate, "system"), - }; - Local values[] = { + auto tmpl = env->cpu_usage_template(); + if (tmpl.IsEmpty()) { + static constexpr std::string_view names[] = { + "user", + "system", + }; + tmpl = DictionaryTemplate::New(isolate, names); + env->set_cpu_usage_template(tmpl); + } + + MaybeLocal values[] = { Number::New(isolate, 1e6 * cpu_usage_stats->ru_utime.tv_sec + cpu_usage_stats->ru_utime.tv_usec), @@ -886,8 +894,10 @@ void Worker::CpuUsage(const FunctionCallbackInfo& args) { 1e6 * cpu_usage_stats->ru_stime.tv_sec + cpu_usage_stats->ru_stime.tv_usec), }; - argv[1] = Object::New( - isolate, Null(isolate), names, values, arraysize(names)); + if (!NewDictionaryInstanceNullProto(env->context(), tmpl, values) + .ToLocal(&argv[1])) { + return; + } } taker->MakeCallback(env->ondone_string(), arraysize(argv), argv); @@ -1056,7 +1066,7 @@ void Worker::GetHeapStatistics(const FunctionCallbackInfo& args) { env](Environment* worker_env) mutable { // We create a unique pointer to HeapStatistics so that the actual object // it's not copied in the lambda, but only the pointer is. - auto heap_stats = std::make_unique(); + auto heap_stats = std::make_unique(); worker_env->isolate()->GetHeapStatistics(heap_stats.get()); // Here, the worker thread temporarily owns the WorkerHeapStatisticsTaker @@ -1071,24 +1081,30 @@ void Worker::GetHeapStatistics(const FunctionCallbackInfo& args) { AsyncHooks::DefaultTriggerAsyncIdScope trigger_id_scope(taker->get()); - Local heap_stats_names[] = { - FIXED_ONE_BYTE_STRING(isolate, "total_heap_size"), - FIXED_ONE_BYTE_STRING(isolate, "total_heap_size_executable"), - FIXED_ONE_BYTE_STRING(isolate, "total_physical_size"), - FIXED_ONE_BYTE_STRING(isolate, "total_available_size"), - FIXED_ONE_BYTE_STRING(isolate, "used_heap_size"), - FIXED_ONE_BYTE_STRING(isolate, "heap_size_limit"), - FIXED_ONE_BYTE_STRING(isolate, "malloced_memory"), - FIXED_ONE_BYTE_STRING(isolate, "peak_malloced_memory"), - FIXED_ONE_BYTE_STRING(isolate, "does_zap_garbage"), - FIXED_ONE_BYTE_STRING(isolate, "number_of_native_contexts"), - FIXED_ONE_BYTE_STRING(isolate, "number_of_detached_contexts"), - FIXED_ONE_BYTE_STRING(isolate, "total_global_handles_size"), - FIXED_ONE_BYTE_STRING(isolate, "used_global_handles_size"), - FIXED_ONE_BYTE_STRING(isolate, "external_memory")}; + auto tmpl = env->heap_statistics_template(); + if (tmpl.IsEmpty()) { + std::string_view heap_stats_names[] = { + "total_heap_size", + "total_heap_size_executable", + "total_physical_size", + "total_available_size", + "used_heap_size", + "heap_size_limit", + "malloced_memory", + "peak_malloced_memory", + "does_zap_garbage", + "number_of_native_contexts", + "number_of_detached_contexts", + "total_global_handles_size", + "used_global_handles_size", + "external_memory", + }; + tmpl = DictionaryTemplate::New(isolate, heap_stats_names); + env->set_heap_statistics_template(tmpl); + } // Define an array of property values - Local heap_stats_values[] = { + MaybeLocal heap_stats_values[] = { Number::New(isolate, heap_stats->total_heap_size()), Number::New(isolate, heap_stats->total_heap_size_executable()), Number::New(isolate, heap_stats->total_physical_size()), @@ -1104,16 +1120,13 @@ void Worker::GetHeapStatistics(const FunctionCallbackInfo& args) { Number::New(isolate, heap_stats->used_global_handles_size()), Number::New(isolate, heap_stats->external_memory())}; - DCHECK_EQ(arraysize(heap_stats_names), arraysize(heap_stats_values)); - - // Create the object with the property names and values - Local stats = Object::New(isolate, - Null(isolate), - heap_stats_names, - heap_stats_values, - arraysize(heap_stats_names)); - - Local args[] = {stats}; + Local obj; + if (!NewDictionaryInstanceNullProto( + env->context(), tmpl, heap_stats_values) + .ToLocal(&obj)) { + return; + } + Local args[] = {obj}; taker->get()->MakeCallback( env->ondone_string(), arraysize(args), args); // implicitly delete `taker` diff --git a/src/util-inl.h b/src/util-inl.h index 778cc57537a966..fbce06d7cef9c2 100644 --- a/src/util-inl.h +++ b/src/util-inl.h @@ -704,6 +704,31 @@ inline std::wstring ConvertToWideString(const std::string& str, } #endif // _WIN32 +inline v8::MaybeLocal NewDictionaryInstance( + v8::Local context, + v8::Local tmpl, + v8::MemorySpan> property_values) { + for (auto& value : property_values) { + if (value.IsEmpty()) return v8::MaybeLocal(); + } + return tmpl->NewInstance(context, property_values); +} + +inline v8::MaybeLocal NewDictionaryInstanceNullProto( + v8::Local context, + v8::Local tmpl, + v8::MemorySpan> property_values) { + for (auto& value : property_values) { + if (value.IsEmpty()) return v8::MaybeLocal(); + } + v8::Local obj = tmpl->NewInstance(context, property_values); + if (obj->SetPrototypeV2(context, v8::Null(context->GetIsolate())) + .IsNothing()) { + return v8::MaybeLocal(); + } + return obj; +} + } // namespace node #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS diff --git a/src/util.h b/src/util.h index e7f35b678f85af..b6f49bcf8e7eab 100644 --- a/src/util.h +++ b/src/util.h @@ -1040,6 +1040,20 @@ inline bool IsWindowsBatchFile(const char* filename); inline std::wstring ConvertToWideString(const std::string& str, UINT code_page); #endif // _WIN32 +// A helper to create a new instance of the dictionary template. +// Unlike v8::DictionaryTemplate::NewInstance, this method will +// check that all properties have been set (are not empty MaybeLocals) +// or will return early with an empty MaybeLocal under the assumption +// that an error has been thrown. +inline v8::MaybeLocal NewDictionaryInstance( + v8::Local context, + v8::Local tmpl, + v8::MemorySpan> property_values); +inline v8::MaybeLocal NewDictionaryInstanceNullProto( + v8::Local context, + v8::Local tmpl, + v8::MemorySpan> property_values); + } // namespace node #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS