[go: up one dir, main page]

[runtime] handle more invalid receivers in Set/GetPrivateMember

Previously these assume that non null or undefined receivers
are JSReceiver which would lead a crash when being used on
other types.

Change-Id: I99a1642b59ae145f433c62d04912791a9d2de03c
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/6333622
Commit-Queue: Joyee Cheung <joyee@igalia.com>
Reviewed-by: Jakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/main@{#99129}
diff --git a/src/runtime/runtime-object.cc b/src/runtime/runtime-object.cc
index 3f15b30..59a1c8d 100644
--- a/src/runtime/runtime-object.cc
+++ b/src/runtime/runtime-object.cc
@@ -1542,14 +1542,14 @@
   DCHECK_EQ(args.length(), 2);
   DirectHandle<Object> receiver = args.at<Object>(0);
   Handle<String> desc = args.at<String>(1);
-  if (IsNullOrUndefined(*receiver, isolate)) {
-    THROW_NEW_ERROR_RETURN_FAILURE(
-        isolate, NewTypeError(MessageTemplate::kNonObjectPrivateNameAccess,
-                              desc, receiver));
+  if (IsJSReceiver(*receiver)) {
+    RETURN_RESULT_OR_FAILURE(
+        isolate,
+        Runtime::GetPrivateMember(isolate, Cast<JSReceiver>(receiver), desc));
   }
-  RETURN_RESULT_OR_FAILURE(
-      isolate,
-      Runtime::GetPrivateMember(isolate, Cast<JSReceiver>(receiver), desc));
+  THROW_NEW_ERROR_RETURN_FAILURE(
+      isolate, NewTypeError(MessageTemplate::kNonObjectPrivateNameAccess, desc,
+                            receiver));
 }
 
 RUNTIME_FUNCTION(Runtime_SetPrivateMember) {
@@ -1559,15 +1559,15 @@
   DCHECK_EQ(args.length(), 3);
   DirectHandle<Object> receiver = args.at<Object>(0);
   Handle<String> desc = args.at<String>(1);
-  if (IsNullOrUndefined(*receiver, isolate)) {
-    THROW_NEW_ERROR_RETURN_FAILURE(
-        isolate, NewTypeError(MessageTemplate::kNonObjectPrivateNameAccess,
-                              desc, receiver));
+  if (IsJSReceiver(*receiver)) {
+    DirectHandle<Object> value = args.at<Object>(2);
+    RETURN_RESULT_OR_FAILURE(
+        isolate, Runtime::SetPrivateMember(isolate, Cast<JSReceiver>(receiver),
+                                           desc, value));
   }
-  DirectHandle<Object> value = args.at<Object>(2);
-  RETURN_RESULT_OR_FAILURE(
-      isolate, Runtime::SetPrivateMember(isolate, Cast<JSReceiver>(receiver),
-                                         desc, value));
+  THROW_NEW_ERROR_RETURN_FAILURE(
+      isolate, NewTypeError(MessageTemplate::kNonObjectPrivateNameAccess, desc,
+                            receiver));
 }
 
 RUNTIME_FUNCTION(Runtime_LoadPrivateSetter) {
diff --git a/test/inspector/runtime/evaluate-private-class-member-invalid-receiver-expected.txt b/test/inspector/runtime/evaluate-private-class-member-invalid-receiver-expected.txt
new file mode 100644
index 0000000..0fe7b29
--- /dev/null
+++ b/test/inspector/runtime/evaluate-private-class-member-invalid-receiver-expected.txt
@@ -0,0 +1,99 @@
+Evaluate getting private member from Smi
+
+Running test: evaluatePrivateMember
+Debugger.evaluateOnCallFrame: `(1).#test = 1`
+{
+    className : TypeError
+    description : TypeError: Cannot access private name #test from 1     at eval (eval at <anonymous> (:1:1), <anonymous>:1:11)     at <anonymous>:1:1
+    objectId : <objectId>
+    subtype : error
+    type : object
+}
+Debugger.evaluateOnCallFrame: `(1).#test`
+{
+    className : TypeError
+    description : TypeError: Cannot access private name #test from 1     at eval (eval at <anonymous> (:1:1), <anonymous>:1:1)     at <anonymous>:1:1
+    objectId : <objectId>
+    subtype : error
+    type : object
+}
+Debugger.evaluateOnCallFrame: `(null).#test = 1`
+{
+    className : TypeError
+    description : TypeError: Cannot access private name #test from null     at eval (eval at <anonymous> (:1:1), <anonymous>:1:14)     at <anonymous>:1:1
+    objectId : <objectId>
+    subtype : error
+    type : object
+}
+Debugger.evaluateOnCallFrame: `(null).#test`
+{
+    className : TypeError
+    description : TypeError: Cannot access private name #test from null     at eval (eval at <anonymous> (:1:1), <anonymous>:1:1)     at <anonymous>:1:1
+    objectId : <objectId>
+    subtype : error
+    type : object
+}
+Debugger.evaluateOnCallFrame: `(undefined).#test = 1`
+{
+    className : TypeError
+    description : TypeError: Cannot access private name #test from undefined     at eval (eval at <anonymous> (:1:1), <anonymous>:1:19)     at <anonymous>:1:1
+    objectId : <objectId>
+    subtype : error
+    type : object
+}
+Debugger.evaluateOnCallFrame: `(undefined).#test`
+{
+    className : TypeError
+    description : TypeError: Cannot access private name #test from undefined     at eval (eval at <anonymous> (:1:1), <anonymous>:1:1)     at <anonymous>:1:1
+    objectId : <objectId>
+    subtype : error
+    type : object
+}
+Debugger.evaluateOnCallFrame: `(true).#test = 1`
+{
+    className : TypeError
+    description : TypeError: Cannot access private name #test from true     at eval (eval at <anonymous> (:1:1), <anonymous>:1:14)     at <anonymous>:1:1
+    objectId : <objectId>
+    subtype : error
+    type : object
+}
+Debugger.evaluateOnCallFrame: `(true).#test`
+{
+    className : TypeError
+    description : TypeError: Cannot access private name #test from true     at eval (eval at <anonymous> (:1:1), <anonymous>:1:1)     at <anonymous>:1:1
+    objectId : <objectId>
+    subtype : error
+    type : object
+}
+Debugger.evaluateOnCallFrame: `("str").#test = 1`
+{
+    className : TypeError
+    description : TypeError: Cannot access private name #test from str     at eval (eval at <anonymous> (:1:1), <anonymous>:1:15)     at <anonymous>:1:1
+    objectId : <objectId>
+    subtype : error
+    type : object
+}
+Debugger.evaluateOnCallFrame: `("str").#test`
+{
+    className : TypeError
+    description : TypeError: Cannot access private name #test from str     at eval (eval at <anonymous> (:1:1), <anonymous>:1:1)     at <anonymous>:1:1
+    objectId : <objectId>
+    subtype : error
+    type : object
+}
+Debugger.evaluateOnCallFrame: `Symbol("str").#test = 1`
+{
+    className : TypeError
+    description : TypeError: Cannot access private name #test from Symbol(str)     at eval (eval at <anonymous> (:1:1), <anonymous>:1:21)     at <anonymous>:1:1
+    objectId : <objectId>
+    subtype : error
+    type : object
+}
+Debugger.evaluateOnCallFrame: `Symbol("str").#test`
+{
+    className : TypeError
+    description : TypeError: Cannot access private name #test from Symbol(str)     at eval (eval at <anonymous> (:1:1), <anonymous>:1:1)     at <anonymous>:1:1
+    objectId : <objectId>
+    subtype : error
+    type : object
+}
diff --git a/test/inspector/runtime/evaluate-private-class-member-invalid-receiver.js b/test/inspector/runtime/evaluate-private-class-member-invalid-receiver.js
new file mode 100644
index 0000000..3823734
--- /dev/null
+++ b/test/inspector/runtime/evaluate-private-class-member-invalid-receiver.js
@@ -0,0 +1,41 @@
+// Copyright 2025 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+const { contextGroup, Protocol } = InspectorTest.start(`Evaluate getting private member from Smi`);
+
+async function runAndLog(expression) {
+  Protocol.Runtime.evaluate({expression: 'debugger;'});
+  const { params: { callFrames } } = await Protocol.Debugger.oncePaused();
+  const frame = callFrames[0];
+  InspectorTest.log(`Debugger.evaluateOnCallFrame: \`${expression}\``);
+  const { result: { result } } =
+    await Protocol.Debugger.evaluateOnCallFrame({
+      callFrameId: frame.callFrameId,
+      expression
+    });
+  InspectorTest.logMessage(result);
+  Protocol.Debugger.resume();
+}
+
+InspectorTest.runAsyncTestSuite([
+  async function evaluatePrivateMember() {
+    Protocol.Debugger.enable();
+    Protocol.Runtime.enable();
+
+    await runAndLog('(1).#test = 1');
+    await runAndLog('(1).#test');
+    await runAndLog('(null).#test = 1');
+    await runAndLog('(null).#test');
+    await runAndLog('(undefined).#test = 1');
+    await runAndLog('(undefined).#test');
+    await runAndLog('(true).#test = 1');
+    await runAndLog('(true).#test');
+    await runAndLog('("str").#test = 1');
+    await runAndLog('("str").#test');
+    await runAndLog('Symbol("str").#test = 1');
+    await runAndLog('Symbol("str").#test');
+
+    InspectorTest.completeTest();
+  }]
+);