[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();
+ }]
+);