81c14eb85d
When the failed access callback is configured but it doesn't throw, we should return instead of expecting an exception, otherwise it would crash because there isn't one. This patch also adds --throw-on-failed-access-check and --noop-on-failed-access-check in d8 to mimic the behavior of the failed access check callback in chromium. Bug: chromium:1339722 Change-Id: Ie1db9d2fb364c6f8259eb9b8d81a21071c280a80 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3737305 Commit-Queue: Joyee Cheung <joyee@igalia.com> Reviewed-by: Toon Verwaest <verwaest@chromium.org> Cr-Commit-Position: refs/heads/main@{#81557}
173 lines
6.1 KiB
JavaScript
173 lines
6.1 KiB
JavaScript
// Copyright 2022 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.
|
|
|
|
class A {
|
|
constructor(arg) {
|
|
return arg;
|
|
}
|
|
}
|
|
|
|
class B extends A {
|
|
#b = 1; // ACCESS_CHECK -> DATA
|
|
constructor(arg) {
|
|
super(arg);
|
|
}
|
|
static setField(obj) {
|
|
obj.#b = 'b'; // KeyedStoreIC
|
|
}
|
|
static getField(obj) {
|
|
return obj.#b;
|
|
}
|
|
static hasField(obj) {
|
|
return #b in obj;
|
|
}
|
|
}
|
|
|
|
class C extends A {
|
|
#c; // DefineKeyedOwnIC: ACCESS_CHECK -> NOT_FOUND
|
|
constructor(arg) {
|
|
super(arg);
|
|
}
|
|
static setField(obj) {
|
|
obj.#c = 'c'; // KeyedStoreIC
|
|
}
|
|
static getField(obj) {
|
|
return obj.#c;
|
|
}
|
|
static hasField(obj) {
|
|
return #c in obj;
|
|
}
|
|
}
|
|
|
|
let d = 0;
|
|
class D extends A {
|
|
get #d() { return d; }
|
|
set #d(val) { d = val; }
|
|
constructor(arg) {
|
|
super(arg); // KeyedStoreIC for private brand
|
|
}
|
|
static setAccessor(obj) {
|
|
obj.#d = 'd'; // KeyedLoadIC for private brand
|
|
}
|
|
static getAccessor(obj) {
|
|
return obj.#d; // KeyedLoadIC for private brand
|
|
}
|
|
static hasAccessor(obj) {
|
|
return #d in obj;
|
|
}
|
|
}
|
|
|
|
class E extends A {
|
|
#e() { return 0; }
|
|
constructor(arg) {
|
|
super(arg); // KeyedStoreIC for private brand
|
|
}
|
|
static setMethod(obj) {
|
|
obj.#e = 'e'; // KeyedLoadIC for private brand
|
|
}
|
|
static getMethod(obj) {
|
|
return obj.#e; // KeyedLoadIC for private brand
|
|
}
|
|
static hasMethod(obj) {
|
|
return #e in obj;
|
|
}
|
|
}
|
|
|
|
function checkHasAccess(object) {
|
|
assertThrows(() => B.setField(globalProxy), TypeError, /Cannot write private member #b to an object whose class did not declare it/);
|
|
assertThrows(() => B.getField(globalProxy), TypeError, /Cannot read private member #b from an object whose class did not declare it/);
|
|
assertFalse(B.hasField(globalProxy));
|
|
|
|
new B(globalProxy);
|
|
assertEquals(B.getField(globalProxy), 1);
|
|
B.setField(globalProxy);
|
|
assertEquals(B.getField(globalProxy), 'b'); // Fast case
|
|
B.setField(globalProxy); // Fast case
|
|
assertEquals(B.getField(globalProxy), 'b'); // Fast case
|
|
assertThrows(() => new B(globalProxy), TypeError, /Cannot initialize #b twice on the same object/);
|
|
assertTrue(B.hasField(globalProxy));
|
|
assertTrue(B.hasField(globalProxy)); // Fast case
|
|
|
|
assertThrows(() => C.setField(globalProxy), TypeError, /Cannot write private member #c to an object whose class did not declare it/);
|
|
assertThrows(() => C.getField(globalProxy), TypeError, /Cannot read private member #c from an object whose class did not declare it/);
|
|
assertFalse(C.hasField(globalProxy));
|
|
|
|
new C(globalProxy);
|
|
assertEquals(C.getField(globalProxy), undefined);
|
|
C.setField(globalProxy);
|
|
assertEquals(C.getField(globalProxy), 'c'); // Fast case
|
|
C.setField(globalProxy); // Fast case
|
|
assertEquals(C.getField(globalProxy), 'c'); // Fast case
|
|
assertThrows(() => new C(globalProxy), TypeError, /Cannot initialize #c twice on the same object/);
|
|
assertTrue(C.hasField(globalProxy));
|
|
assertTrue(C.hasField(globalProxy)); // Fast case
|
|
|
|
assertThrows(() => D.setAccessor(globalProxy), TypeError, /Receiver must be an instance of class D/);
|
|
assertThrows(() => D.getAccessor(globalProxy), TypeError, /Receiver must be an instance of class D/);
|
|
assertFalse(D.hasAccessor(globalProxy));
|
|
|
|
new D(globalProxy);
|
|
assertEquals(D.getAccessor(globalProxy), 0);
|
|
D.setAccessor(globalProxy);
|
|
assertEquals(D.getAccessor(globalProxy), 'd'); // Fast case
|
|
D.setAccessor(globalProxy); // Fast case
|
|
assertEquals(D.getAccessor(globalProxy), 'd'); // Fast case
|
|
assertThrows(() => new D(globalProxy), TypeError, /Cannot initialize private methods of class D twice on the same object/);
|
|
assertTrue(D.hasAccessor(globalProxy));
|
|
assertTrue(D.hasAccessor(globalProxy)); // Fast case
|
|
|
|
assertThrows(() => E.setMethod(globalProxy), TypeError, /Receiver must be an instance of class E/);
|
|
assertThrows(() => E.getMethod(globalProxy), TypeError, /Receiver must be an instance of class E/);
|
|
assertFalse(E.hasMethod(globalProxy));
|
|
|
|
new E(globalProxy);
|
|
assertEquals(E.getMethod(globalProxy)(), 0);
|
|
assertThrows(() => E.setMethod(globalProxy), TypeError, /Private method '#e' is not writable/);
|
|
assertEquals(E.getMethod(globalProxy)(), 0); // Fast case
|
|
assertThrows(() => new E(globalProxy), TypeError, /Cannot initialize private methods of class E twice on the same object/);
|
|
assertTrue(E.hasMethod(globalProxy));
|
|
assertTrue(E.hasMethod(globalProxy)); // Fast case
|
|
}
|
|
|
|
function checkNoAccess(object, message) {
|
|
assertThrows(() => new B(object), Error, message);
|
|
assertThrows(() => new C(object), Error, message);
|
|
assertThrows(() => new D(object), Error, message);
|
|
assertThrows(() => new E(object), Error, message);
|
|
assertThrows(() => B.setField(object), Error, message);
|
|
assertThrows(() => B.getField(object), Error, message);
|
|
assertThrows(() => B.hasField(object), Error, message);
|
|
assertThrows(() => C.setField(object), Error, message);
|
|
assertThrows(() => C.getField(object), Error, message);
|
|
assertThrows(() => C.hasField(object), Error, message);
|
|
assertThrows(() => D.setAccessor(object), Error, message);
|
|
assertThrows(() => D.getAccessor(object), Error, message);
|
|
assertThrows(() => D.hasAccessor(object), Error, message);
|
|
assertThrows(() => E.setMethod(object), Error, message);
|
|
assertThrows(() => E.getMethod(object), Error, message);
|
|
assertThrows(() => E.hasMethod(object), Error, message);
|
|
}
|
|
|
|
function checkNoAccessNoThrow(object) {
|
|
// The failed access check callback is supposed to throw.
|
|
// If it doesn't the behavior is quite quirky.
|
|
// This only documents the current behavior.
|
|
new B(object);
|
|
new C(object);
|
|
new D(object);
|
|
new E(object);
|
|
B.setField(object);
|
|
assertEquals(undefined, B.getField(object));
|
|
assertFalse(B.hasField(object));
|
|
C.setField(object);
|
|
assertEquals(undefined, C.getField(object));
|
|
assertFalse(C.hasField(object));
|
|
D.setAccessor(object)
|
|
assertEquals("d", D.getAccessor(object));
|
|
assertFalse(D.hasAccessor(object));
|
|
assertThrows(() => E.setMethod(object), TypeError, /Private method '#e' is not writable/);
|
|
assertEquals(0, E.getMethod(object)());
|
|
assertFalse(E.hasMethod(object));
|
|
}
|