| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent a646319 commit 2b59984
8 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -42,7 +42,7 @@ | |||
| 42 | 42 | ||
| 43 | 43 | # Reset this number to 0 on major V8 upgrades. | |
| 44 | 44 | # Increment by one for each non-official patch applied to deps/v8. | |
| 45 | - 'v8_embedder_string': '-node.27', | ||
| 45 | + 'v8_embedder_string': '-node.28', | ||
| 46 | 46 | ||
| 47 | 47 | ##### V8 defaults for Node.js ##### | |
| 48 | 48 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -77,6 +77,7 @@ Artem Kobzar <artem.kobzar@jetbrains.com> | |||
| 77 | 77 | Arthur Islamov <arthur@islamov.ai> | |
| 78 | 78 | Asuka Shikina <shikina.asuka@gmail.com> | |
| 79 | 79 | Aurèle Barrière <aurele.barriere@gmail.com> | |
| 80 | + Aviv Keller <me@aviv.sh> | ||
| 80 | 81 | Bala Avulapati <bavulapati@gmail.com> | |
| 81 | 82 | Bangfu Tao <bangfu.tao@samsung.com> | |
| 82 | 83 | Ben Coe <bencoe@gmail.com> | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -204,8 +204,7 @@ class InjectedScript::ProtocolPromiseHandler { | |||
| 204 | 204 | PromiseHandlerTracker::DiscardReason::kFulfilled); | |
| 205 | 205 | } | |
| 206 | 206 | ||
| 207 | - ProtocolPromiseHandler(PromiseHandlerTracker::Id id, | ||
| 208 | - V8InspectorSessionImpl* session, | ||
| 207 | + ProtocolPromiseHandler(V8InspectorSessionImpl* session, | ||
| 209 | 208 | int executionContextId, const String16& objectGroup, | |
| 210 | 209 | std::unique_ptr<WrapOptions> wrapOptions, | |
| 211 | 210 | bool replMode, bool throwOnSideEffect, | |
@@ -220,7 +219,13 @@ class InjectedScript::ProtocolPromiseHandler { | |||
| 220 | 219 | m_replMode(replMode), | |
| 221 | 220 | m_throwOnSideEffect(throwOnSideEffect), | |
| 222 | 221 | m_callback(std::move(callback)), | |
| 223 | - m_evaluationResult(m_inspector->isolate(), evaluationResult) { | ||
| 222 | + m_evaluationResult(m_inspector->isolate(), evaluationResult) {} | ||
| 223 | + | ||
| 224 | + void makeWeak(PromiseHandlerTracker::Id id) { | ||
| 225 | + if (m_isActive || m_evaluationResult.IsEmpty() || | ||
| 226 | + m_evaluationResult.IsWeak()) { | ||
| 227 | + return; | ||
| 228 | + } | ||
| 224 | 229 | m_evaluationResult.SetWeak(reinterpret_cast<PromiseHandlerTracker::Id*>(id), | |
| 225 | 230 | cleanup, v8::WeakCallbackType::kParameter); | |
| 226 | 231 | } | |
@@ -238,6 +243,7 @@ class InjectedScript::ProtocolPromiseHandler { | |||
| 238 | 243 | } | |
| 239 | 244 | ||
| 240 | 245 | void thenCallback(v8::Local<v8::Value> value) { | |
| 246 | + m_isActive = true; | ||
| 241 | 247 | // We don't need the m_evaluationResult in the `thenCallback`, but we also | |
| 242 | 248 | // don't want `cleanup` running in case we re-enter JS. | |
| 243 | 249 | m_evaluationResult.Reset(); | |
@@ -285,9 +291,10 @@ class InjectedScript::ProtocolPromiseHandler { | |||
| 285 | 291 | } | |
| 286 | 292 | ||
| 287 | 293 | void catchCallback(v8::Local<v8::Value> result) { | |
| 294 | + m_isActive = true; | ||
| 288 | 295 | // Hold strongly onto m_evaluationResult now to prevent `cleanup` from | |
| 289 | 296 | // running in case any code below triggers GC. | |
| 290 | - m_evaluationResult.ClearWeak(); | ||
| 297 | + if (m_evaluationResult.IsWeak()) m_evaluationResult.ClearWeak(); | ||
| 291 | 298 | V8InspectorSessionImpl* session = | |
| 292 | 299 | m_inspector->sessionById(m_contextGroupId, m_sessionId); | |
| 293 | 300 | if (!session) return; | |
@@ -393,6 +400,7 @@ class InjectedScript::ProtocolPromiseHandler { | |||
| 393 | 400 | std::unique_ptr<WrapOptions> m_wrapOptions; | |
| 394 | 401 | bool m_replMode; | |
| 395 | 402 | bool m_throwOnSideEffect; | |
| 403 | + bool m_isActive = false; | ||
| 396 | 404 | std::weak_ptr<EvaluateCallback> m_callback; | |
| 397 | 405 | v8::Global<v8::Promise> m_evaluationResult; | |
| 398 | 406 | }; | |
@@ -1190,8 +1198,7 @@ template <typename... Args> | |||
| 1190 | 1198 | PromiseHandlerTracker::Id PromiseHandlerTracker::create(Args&&... args) { | |
| 1191 | 1199 | Id id = m_lastUsedId++; | |
| 1192 | 1200 | InjectedScript::ProtocolPromiseHandler* handler = | |
| 1193 | - new InjectedScript::ProtocolPromiseHandler(id, | ||
| 1194 | - std::forward<Args>(args)...); | ||
| 1201 | + new InjectedScript::ProtocolPromiseHandler(std::forward<Args>(args)...); | ||
| 1195 | 1202 | m_promiseHandlers.emplace(id, handler); | |
| 1196 | 1203 | return id; | |
| 1197 | 1204 | } | |
@@ -1225,6 +1232,30 @@ InjectedScript::ProtocolPromiseHandler* PromiseHandlerTracker::get( | |||
| 1225 | 1232 | return iter->second.get(); | |
| 1226 | 1233 | } | |
| 1227 | 1234 | ||
| 1235 | + void PromiseHandlerTracker::makeWeakForContext(int executionContextId) { | ||
| 1236 | + for (auto& [id, handler] : m_promiseHandlers) { | ||
| 1237 | + if (handler->m_executionContextId == executionContextId) { | ||
| 1238 | + handler->makeWeak(id); | ||
| 1239 | + } | ||
| 1240 | + } | ||
| 1241 | + } | ||
| 1242 | + | ||
| 1243 | + void PromiseHandlerTracker::makeWeakForObjectGroup( | ||
| 1244 | + int sessionId, const String16& objectGroup) { | ||
| 1245 | + for (auto& [id, handler] : m_promiseHandlers) { | ||
| 1246 | + if (handler->m_sessionId == sessionId && | ||
| 1247 | + handler->m_objectGroup == objectGroup) { | ||
| 1248 | + handler->makeWeak(id); | ||
| 1249 | + } | ||
| 1250 | + } | ||
| 1251 | + } | ||
| 1252 | + | ||
| 1253 | + void PromiseHandlerTracker::makeWeakForSession(int sessionId) { | ||
| 1254 | + for (auto& [id, handler] : m_promiseHandlers) { | ||
| 1255 | + if (handler->m_sessionId == sessionId) handler->makeWeak(id); | ||
| 1256 | + } | ||
| 1257 | + } | ||
| 1258 | + | ||
| 1228 | 1259 | void PromiseHandlerTracker::sendFailure( | |
| 1229 | 1260 | InjectedScript::ProtocolPromiseHandler* handler, | |
| 1230 | 1261 | const protocol::DispatchResponse& response) const { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -298,6 +298,9 @@ class PromiseHandlerTracker { | |||
| 298 | 298 | Id create(Args&&... args); | |
| 299 | 299 | void discard(Id id, DiscardReason reason); | |
| 300 | 300 | InjectedScript::ProtocolPromiseHandler* get(Id id) const; | |
| 301 | + void makeWeakForContext(int executionContextId); | ||
| 302 | + void makeWeakForObjectGroup(int sessionId, const String16& objectGroup); | ||
| 303 | + void makeWeakForSession(int sessionId); | ||
| 301 | 304 | ||
| 302 | 305 | private: | |
| 303 | 306 | void sendFailure(InjectedScript::ProtocolPromiseHandler* handler, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -319,6 +319,7 @@ void V8InspectorImpl::contextCollected(int groupId, int contextId) { | |||
| 319 | 319 | session->runtimeAgent()->reportExecutionContextDestroyed(inspectedContext); | |
| 320 | 320 | }); | |
| 321 | 321 | discardInspectedContext(groupId, contextId); | |
| 322 | + m_promiseHandlerTracker.makeWeakForContext(contextId); | ||
| 322 | 323 | } | |
| 323 | 324 | ||
| 324 | 325 | void V8InspectorImpl::resetContextGroup(int contextGroupId) { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -224,6 +224,7 @@ void V8InspectorSessionImpl::discardInjectedScripts() { | |||
| 224 | 224 | [&sessionId](InspectedContext* context) { | |
| 225 | 225 | context->discardInjectedScript(sessionId); | |
| 226 | 226 | }); | |
| 227 | + m_inspector->promiseHandlerTracker().makeWeakForSession(sessionId); | ||
| 227 | 228 | } | |
| 228 | 229 | ||
| 229 | 230 | Response V8InspectorSessionImpl::findInjectedScript( | |
@@ -260,6 +261,10 @@ void V8InspectorSessionImpl::releaseObjectGroup(const String16& objectGroup) { | |||
| 260 | 261 | InjectedScript* injectedScript = context->getInjectedScript(sessionId); | |
| 261 | 262 | if (injectedScript) injectedScript->releaseObjectGroup(objectGroup); | |
| 262 | 263 | }); | |
| 264 | + if (!objectGroup.isEmpty()) { | ||
| 265 | + m_inspector->promiseHandlerTracker().makeWeakForObjectGroup(m_sessionId, | ||
| 266 | + objectGroup); | ||
| 267 | + } | ||
| 263 | 268 | } | |
| 264 | 269 | ||
| 265 | 270 | bool V8InspectorSessionImpl::unwrapObject( | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,65 @@ | |||
| 1 | + Tests the lifetime of pending Runtime.evaluate requests. | ||
| 2 | + | ||
| 3 | + Running test: testPromiseIsKeptAlive | ||
| 4 | + Using replMode: | ||
| 5 | + { | ||
| 6 | + id : <messageId> | ||
| 7 | + result : { | ||
| 8 | + result : { | ||
| 9 | + description : 42 | ||
| 10 | + type : number | ||
| 11 | + value : 42 | ||
| 12 | + } | ||
| 13 | + } | ||
| 14 | + } | ||
| 15 | + Using awaitPromise: | ||
| 16 | + { | ||
| 17 | + id : <messageId> | ||
| 18 | + result : { | ||
| 19 | + result : { | ||
| 20 | + description : 42 | ||
| 21 | + type : number | ||
| 22 | + value : 42 | ||
| 23 | + } | ||
| 24 | + } | ||
| 25 | + } | ||
| 26 | + | ||
| 27 | + Running test: testObjectGroupReleaseMakesPromiseCollectible | ||
| 28 | + Using replMode: | ||
| 29 | + { | ||
| 30 | + error : { | ||
| 31 | + code : -32000 | ||
| 32 | + message : Promise was collected | ||
| 33 | + } | ||
| 34 | + id : <messageId> | ||
| 35 | + } | ||
| 36 | + Using awaitPromise: | ||
| 37 | + { | ||
| 38 | + error : { | ||
| 39 | + code : -32000 | ||
| 40 | + message : Promise was collected | ||
| 41 | + } | ||
| 42 | + id : <messageId> | ||
| 43 | + } | ||
| 44 | + | ||
| 45 | + Running test: testContextDestructionDiscardsPromise | ||
| 46 | + Using replMode: | ||
| 47 | + { | ||
| 48 | + error : { | ||
| 49 | + code : -32000 | ||
| 50 | + message : Execution context was destroyed. | ||
| 51 | + } | ||
| 52 | + id : <messageId> | ||
| 53 | + } | ||
| 54 | + Using awaitPromise: | ||
| 55 | + { | ||
| 56 | + error : { | ||
| 57 | + code : -32000 | ||
| 58 | + message : Execution context was destroyed. | ||
| 59 | + } | ||
| 60 | + id : <messageId> | ||
| 61 | + } | ||
| 62 | + | ||
| 63 | + Running test: testSessionDestructionMakesPromiseCollectible | ||
| 64 | + Promise is alive before disconnect: true | ||
| 65 | + Promise is alive after disconnect: false | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,105 @@ | |||
| 1 | + // Copyright 2026 the V8 project authors. All rights reserved. | ||
| 2 | + // Use of this source code is governed by a BSD-style license that can be | ||
| 3 | + // found in the LICENSE file. | ||
| 4 | + | ||
| 5 | + // Flags: --no-stress-incremental-marking | ||
| 6 | + | ||
| 7 | + let {Protocol} = InspectorTest.start( | ||
| 8 | + 'Tests the lifetime of pending Runtime.evaluate requests.'); | ||
| 9 | + | ||
| 10 | + const evaluationModes = [ | ||
| 11 | + { | ||
| 12 | + name: 'replMode', | ||
| 13 | + arguments: {replMode: true}, | ||
| 14 | + expression: | ||
| 15 | + 'await new Promise(resolve => globalThis.resolve = resolve); 42', | ||
| 16 | + resolveExpression: 'resolve()', | ||
| 17 | + pendingExpression: 'await new Promise(() => {})', | ||
| 18 | + }, | ||
| 19 | + { | ||
| 20 | + name: 'awaitPromise', | ||
| 21 | + arguments: {awaitPromise: true}, | ||
| 22 | + expression: `(() => { | ||
| 23 | + let resolve; | ||
| 24 | + const promise = new Promise(r => resolve = r); | ||
| 25 | + promise.resolve = resolve; | ||
| 26 | + globalThis.weak = new WeakRef(promise); | ||
| 27 | + return promise; | ||
| 28 | + })()`, | ||
| 29 | + resolveExpression: 'weak.deref().resolve(42)', | ||
| 30 | + pendingExpression: 'new Promise(() => {})', | ||
| 31 | + }, | ||
| 32 | + ]; | ||
| 33 | + | ||
| 34 | + function evaluate(Protocol, mode, expression, extraArguments = {}) { | ||
| 35 | + return Protocol.Runtime.evaluate( | ||
| 36 | + {...mode.arguments, ...extraArguments, expression}); | ||
| 37 | + } | ||
| 38 | + | ||
| 39 | + InspectorTest.runAsyncTestSuite([ | ||
| 40 | + async function testPromiseIsKeptAlive() { | ||
| 41 | + for (const mode of evaluationModes) { | ||
| 42 | + InspectorTest.log(`Using ${mode.name}:`); | ||
| 43 | + const evaluation = evaluate(Protocol, mode, mode.expression); | ||
| 44 | + | ||
| 45 | + await Protocol.HeapProfiler.collectGarbage(); | ||
| 46 | + await Protocol.Runtime.evaluate({expression: mode.resolveExpression}); | ||
| 47 | + | ||
| 48 | + InspectorTest.logMessage(await evaluation); | ||
| 49 | + } | ||
| 50 | + }, | ||
| 51 | + | ||
| 52 | + async function testObjectGroupReleaseMakesPromiseCollectible() { | ||
| 53 | + for (const mode of evaluationModes) { | ||
| 54 | + InspectorTest.log(`Using ${mode.name}:`); | ||
| 55 | + const evaluation = evaluate( | ||
| 56 | + Protocol, mode, mode.pendingExpression, | ||
| 57 | + {objectGroup: 'evaluation'}); | ||
| 58 | + | ||
| 59 | + await Protocol.Runtime.releaseObjectGroup({objectGroup: 'evaluation'}); | ||
| 60 | + await Protocol.HeapProfiler.collectGarbage(); | ||
| 61 | + | ||
| 62 | + InspectorTest.logMessage(await evaluation); | ||
| 63 | + } | ||
| 64 | + }, | ||
| 65 | + | ||
| 66 | + async function testContextDestructionDiscardsPromise() { | ||
| 67 | + for (const mode of evaluationModes) { | ||
| 68 | + InspectorTest.log(`Using ${mode.name}:`); | ||
| 69 | + const contextGroup = new InspectorTest.ContextGroup(); | ||
| 70 | + const session = contextGroup.connect(); | ||
| 71 | + const evaluation = evaluate( | ||
| 72 | + session.Protocol, mode, mode.pendingExpression); | ||
| 73 | + | ||
| 74 | + await session.Protocol.Runtime.evaluate( | ||
| 75 | + {expression: 'inspector.fireContextDestroyed()'}); | ||
| 76 | + | ||
| 77 | + InspectorTest.logMessage(await evaluation); | ||
| 78 | + session.disconnect(); | ||
| 79 | + } | ||
| 80 | + }, | ||
| 81 | + | ||
| 82 | + async function testSessionDestructionMakesPromiseCollectible() { | ||
| 83 | + const contextGroup = new InspectorTest.ContextGroup(); | ||
| 84 | + const session1 = contextGroup.connect(); | ||
| 85 | + const session2 = contextGroup.connect(); | ||
| 86 | + session1.Protocol.Runtime.evaluate({ | ||
| 87 | + expression: evaluationModes[1].expression, | ||
| 88 | + awaitPromise: true, | ||
| 89 | + }); | ||
| 90 | + | ||
| 91 | + await session2.Protocol.HeapProfiler.collectGarbage(); | ||
| 92 | + let result = await session2.Protocol.Runtime.evaluate( | ||
| 93 | + {expression: 'weak.deref() !== undefined'}); | ||
| 94 | + InspectorTest.log( | ||
| 95 | + `Promise is alive before disconnect: ${result.result.result.value}`); | ||
| 96 | + | ||
| 97 | + session1.disconnect(); | ||
| 98 | + await session2.Protocol.HeapProfiler.collectGarbage(); | ||
| 99 | + result = await session2.Protocol.Runtime.evaluate( | ||
| 100 | + {expression: 'weak.deref() !== undefined'}); | ||
| 101 | + InspectorTest.log( | ||
| 102 | + `Promise is alive after disconnect: ${result.result.result.value}`); | ||
| 103 | + session2.disconnect(); | ||
| 104 | + }, | ||
| 105 | + ]); | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments