[classes] Implement private brand checks
Notes: https://docs.google.com/document/d/1fEumNPCcOn4X0N5jGlAT7GQ5CEKKnw0YxLPXMoaSK5Q/edit?usp=sharing Bug: v8:11374 Change-Id: I96720c0d69fe28e7229c4c22ed3d291587b73f59 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2667511 Commit-Queue: Marja Hölttä <marja@chromium.org> Reviewed-by: Leszek Swirski <leszeks@chromium.org> Reviewed-by: Igor Sheludko <ishell@chromium.org> Reviewed-by: Shu-yu Guo <syg@chromium.org> Cr-Commit-Position: refs/heads/master@{#72659}
This commit is contained in:
parent
921a4a7293
commit
1105d7ba5f
@ -9267,7 +9267,7 @@ void CodeStubAssembler::TryPrototypeChainLookup(
|
||||
TNode<Object> receiver, TNode<Object> object_arg, TNode<Object> key,
|
||||
const LookupPropertyInHolder& lookup_property_in_holder,
|
||||
const LookupElementInHolder& lookup_element_in_holder, Label* if_end,
|
||||
Label* if_bailout, Label* if_proxy) {
|
||||
Label* if_bailout, Label* if_proxy, bool handle_private_names) {
|
||||
// Ensure receiver is JSReceiver, otherwise bailout.
|
||||
GotoIf(TaggedIsSmi(receiver), if_bailout);
|
||||
TNode<HeapObject> object = CAST(object_arg);
|
||||
@ -9322,6 +9322,11 @@ void CodeStubAssembler::TryPrototypeChainLookup(
|
||||
|
||||
BIND(&next_proto);
|
||||
|
||||
if (handle_private_names) {
|
||||
// Private name lookup doesn't walk the prototype chain.
|
||||
GotoIf(IsPrivateSymbol(CAST(key)), if_end);
|
||||
}
|
||||
|
||||
TNode<HeapObject> proto = LoadMapPrototype(holder_map);
|
||||
|
||||
GotoIf(IsNull(proto), if_end);
|
||||
@ -12392,9 +12397,10 @@ TNode<Oddball> CodeStubAssembler::HasProperty(TNode<Context> context,
|
||||
&return_true, &return_false, next_holder, if_bailout);
|
||||
};
|
||||
|
||||
const bool kHandlePrivateNames = mode == HasPropertyLookupMode::kHasProperty;
|
||||
TryPrototypeChainLookup(object, object, key, lookup_property_in_holder,
|
||||
lookup_element_in_holder, &return_false,
|
||||
&call_runtime, &if_proxy);
|
||||
&call_runtime, &if_proxy, kHandlePrivateNames);
|
||||
|
||||
TVARIABLE(Oddball, result);
|
||||
|
||||
|
@ -3128,7 +3128,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
|
||||
TNode<Object> receiver, TNode<Object> object, TNode<Object> key,
|
||||
const LookupPropertyInHolder& lookup_property_in_holder,
|
||||
const LookupElementInHolder& lookup_element_in_holder, Label* if_end,
|
||||
Label* if_bailout, Label* if_proxy);
|
||||
Label* if_bailout, Label* if_proxy, bool handle_private_names = false);
|
||||
|
||||
// Instanceof helpers.
|
||||
// Returns true if {object} has {prototype} somewhere in it's prototype
|
||||
|
@ -249,7 +249,8 @@ DEFINE_IMPLICATION(harmony_weak_refs_with_cleanup_some, harmony_weak_refs)
|
||||
V(harmony_regexp_sequence, "RegExp Unicode sequence properties") \
|
||||
V(harmony_weak_refs_with_cleanup_some, \
|
||||
"harmony weak references with FinalizationRegistry.prototype.cleanupSome") \
|
||||
V(harmony_import_assertions, "harmony import assertions")
|
||||
V(harmony_import_assertions, "harmony import assertions") \
|
||||
V(harmony_private_brand_checks, "harmony private brand checks")
|
||||
|
||||
#ifdef V8_INTL_SUPPORT
|
||||
#define HARMONY_INPROGRESS(V) \
|
||||
|
@ -432,7 +432,7 @@ MaybeHandle<Object> LoadIC::Load(Handle<Object> object, Handle<Name> name,
|
||||
LookupForRead(&it, IsAnyHas());
|
||||
|
||||
if (name->IsPrivate()) {
|
||||
if (name->IsPrivateName() && !it.IsFound()) {
|
||||
if (!IsAnyHas() && name->IsPrivateName() && !it.IsFound()) {
|
||||
Handle<String> name_string(
|
||||
String::cast(Symbol::cast(*name).description()), isolate());
|
||||
if (name->IsPrivateBrand()) {
|
||||
|
@ -4320,6 +4320,7 @@ EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_regexp_sequence)
|
||||
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_top_level_await)
|
||||
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_logical_assignment)
|
||||
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_import_assertions)
|
||||
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_private_brand_checks)
|
||||
|
||||
#ifdef V8_INTL_SUPPORT
|
||||
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_intl_displaynames_date_types)
|
||||
|
@ -4792,6 +4792,65 @@ void BytecodeGenerator::BuildPrivateSetterAccess(Register object,
|
||||
feedback_index(feedback_spec()->AddCallICSlot()));
|
||||
}
|
||||
|
||||
void BytecodeGenerator::BuildPrivateMethodIn(Variable* private_name,
|
||||
Expression* object_expression) {
|
||||
DCHECK(IsPrivateMethodOrAccessorVariableMode(private_name->mode()));
|
||||
ClassScope* scope = private_name->scope()->AsClassScope();
|
||||
if (private_name->is_static()) {
|
||||
// For static private methods, "#privatemethod in ..." only returns true for
|
||||
// the class constructor.
|
||||
if (scope->class_variable() == nullptr) {
|
||||
// Can only happen via the debugger. See comment in
|
||||
// BuildPrivateBrandCheck.
|
||||
RegisterAllocationScope register_scope(this);
|
||||
RegisterList args = register_allocator()->NewRegisterList(2);
|
||||
builder()
|
||||
->LoadLiteral(Smi::FromEnum(
|
||||
MessageTemplate::
|
||||
kInvalidUnusedPrivateStaticMethodAccessedByDebugger))
|
||||
.StoreAccumulatorInRegister(args[0])
|
||||
.LoadLiteral(private_name->raw_name())
|
||||
.StoreAccumulatorInRegister(args[1])
|
||||
.CallRuntime(Runtime::kNewError, args)
|
||||
.Throw();
|
||||
} else {
|
||||
VisitForAccumulatorValue(object_expression);
|
||||
Register object = register_allocator()->NewRegister();
|
||||
builder()->StoreAccumulatorInRegister(object);
|
||||
|
||||
BytecodeLabel is_object;
|
||||
builder()->JumpIfJSReceiver(&is_object);
|
||||
|
||||
RegisterList args = register_allocator()->NewRegisterList(3);
|
||||
builder()
|
||||
->StoreAccumulatorInRegister(args[2])
|
||||
.LoadLiteral(Smi::FromEnum(MessageTemplate::kInvalidInOperatorUse))
|
||||
.StoreAccumulatorInRegister(args[0])
|
||||
.LoadLiteral(private_name->raw_name())
|
||||
.StoreAccumulatorInRegister(args[1])
|
||||
.CallRuntime(Runtime::kNewTypeError, args)
|
||||
.Throw();
|
||||
|
||||
builder()->Bind(&is_object);
|
||||
BuildVariableLoadForAccumulatorValue(scope->class_variable(),
|
||||
HoleCheckMode::kElided);
|
||||
builder()->CompareReference(object);
|
||||
}
|
||||
} else {
|
||||
BuildVariableLoadForAccumulatorValue(scope->brand(),
|
||||
HoleCheckMode::kElided);
|
||||
Register brand = register_allocator()->NewRegister();
|
||||
builder()->StoreAccumulatorInRegister(brand);
|
||||
|
||||
VisitForAccumulatorValue(object_expression);
|
||||
builder()->SetExpressionPosition(object_expression);
|
||||
|
||||
FeedbackSlot slot = feedback_spec()->AddKeyedHasICSlot();
|
||||
builder()->CompareOperation(Token::IN, brand, feedback_index(slot));
|
||||
execution_result()->SetResultIsBoolean();
|
||||
}
|
||||
}
|
||||
|
||||
void BytecodeGenerator::BuildPrivateBrandCheck(Property* property,
|
||||
Register object,
|
||||
MessageTemplate tmpl) {
|
||||
@ -5660,6 +5719,16 @@ void BytecodeGenerator::VisitCompareOperation(CompareOperation* expr) {
|
||||
builder()->SetExpressionPosition(expr);
|
||||
BuildLiteralCompareNil(expr->op(), BytecodeArrayBuilder::kNullValue);
|
||||
} else {
|
||||
if (expr->op() == Token::IN && expr->left()->IsPrivateName()) {
|
||||
DCHECK(FLAG_harmony_private_brand_checks);
|
||||
Variable* var = expr->left()->AsVariableProxy()->var();
|
||||
if (IsPrivateMethodOrAccessorVariableMode(var->mode())) {
|
||||
BuildPrivateMethodIn(var, expr->right());
|
||||
return;
|
||||
}
|
||||
// For private fields, the code below does the right thing.
|
||||
}
|
||||
|
||||
Register lhs = VisitForRegisterValue(expr->left());
|
||||
VisitForAccumulatorValue(expr->right());
|
||||
builder()->SetExpressionPosition(expr);
|
||||
|
@ -312,6 +312,8 @@ class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> {
|
||||
void BuildInvalidPropertyAccess(MessageTemplate tmpl, Property* property);
|
||||
void BuildPrivateBrandCheck(Property* property, Register object,
|
||||
MessageTemplate tmpl);
|
||||
void BuildPrivateMethodIn(Variable* private_name,
|
||||
Expression* object_expression);
|
||||
void BuildPrivateGetterAccess(Register obj, Register access_pair);
|
||||
void BuildPrivateSetterAccess(Register obj, Register access_pair,
|
||||
Register value);
|
||||
|
@ -3144,7 +3144,20 @@ template <typename Impl>
|
||||
typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseBinaryExpression(
|
||||
int prec) {
|
||||
DCHECK_GE(prec, 4);
|
||||
ExpressionT x = ParseUnaryExpression();
|
||||
ExpressionT x;
|
||||
// "#foo in ShiftExpression" needs to be parsed separately, since private
|
||||
// identifiers are not valid PrimaryExpressions.
|
||||
if (V8_UNLIKELY(FLAG_harmony_private_brand_checks &&
|
||||
peek() == Token::PRIVATE_NAME)) {
|
||||
x = ParsePropertyOrPrivatePropertyName();
|
||||
if (peek() != Token::IN) {
|
||||
ReportUnexpectedToken(peek());
|
||||
return impl()->FailureExpression();
|
||||
}
|
||||
} else {
|
||||
x = ParseUnaryExpression();
|
||||
}
|
||||
|
||||
int prec1 = Token::Precedence(peek(), accept_IN_);
|
||||
if (prec1 >= prec) {
|
||||
return ParseBinaryContinuation(x, prec, prec1);
|
||||
|
567
test/mjsunit/harmony/private-brand-checks.js
Normal file
567
test/mjsunit/harmony/private-brand-checks.js
Normal file
@ -0,0 +1,567 @@
|
||||
// Copyright 2021 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.
|
||||
|
||||
// Flags: --harmony-private-brand-checks --allow-natives-syntax
|
||||
|
||||
// Objects for which all our brand checks return false.
|
||||
const commonFalseCases = [{}, function() {}, []];
|
||||
// Values for which all our brand checks throw.
|
||||
const commonThrowCases = [100, 'foo', undefined, null];
|
||||
|
||||
(function TestReturnValue() {
|
||||
class A {
|
||||
m() {
|
||||
assertEquals(typeof (#x in this), 'boolean');
|
||||
assertEquals(typeof (#x in {}), 'boolean');
|
||||
}
|
||||
#x = 1;
|
||||
}
|
||||
})();
|
||||
|
||||
(function TestPrivateField() {
|
||||
class A {
|
||||
m(other) {
|
||||
return #x in other;
|
||||
}
|
||||
#x = 1;
|
||||
}
|
||||
let a = new A();
|
||||
assertTrue(a.m(a));
|
||||
assertTrue(a.m(new A()));
|
||||
for (o of commonFalseCases) {
|
||||
assertFalse(a.m(o));
|
||||
}
|
||||
for (o of commonThrowCases) {
|
||||
assertThrows(() => { a.m(o) }, TypeError);
|
||||
}
|
||||
|
||||
class B {
|
||||
#x = 5;
|
||||
}
|
||||
assertFalse(a.m(new B()));
|
||||
})();
|
||||
|
||||
(function TestPrivateFieldWithValueUndefined() {
|
||||
class A {
|
||||
m(other) {
|
||||
return #x in other;
|
||||
}
|
||||
#x;
|
||||
}
|
||||
let a = new A();
|
||||
assertTrue(a.m(a));
|
||||
assertTrue(a.m(new A()));
|
||||
for (o of commonFalseCases) {
|
||||
assertFalse(a.m(o));
|
||||
}
|
||||
for (o of commonThrowCases) {
|
||||
assertThrows(() => { a.m(o) }, TypeError);
|
||||
}
|
||||
|
||||
class B {
|
||||
#x;
|
||||
}
|
||||
assertFalse(a.m(new B()));
|
||||
})();
|
||||
|
||||
(function TestPrivateMethod() {
|
||||
class A {
|
||||
#pm() {
|
||||
}
|
||||
m(other) {
|
||||
return #pm in other;
|
||||
}
|
||||
}
|
||||
let a = new A();
|
||||
assertTrue(a.m(a));
|
||||
assertTrue(a.m(new A()));
|
||||
for (o of commonFalseCases) {
|
||||
assertFalse(a.m(o));
|
||||
}
|
||||
for (o of commonThrowCases) {
|
||||
assertThrows(() => { a.m(o) }, TypeError);
|
||||
}
|
||||
|
||||
class B {
|
||||
#pm() {}
|
||||
}
|
||||
assertFalse(a.m(new B()));
|
||||
})();
|
||||
|
||||
(function TestPrivateGetter() {
|
||||
class A {
|
||||
get #foo() {
|
||||
}
|
||||
m(other) {
|
||||
return #foo in other;
|
||||
}
|
||||
}
|
||||
let a = new A();
|
||||
assertTrue(a.m(a));
|
||||
assertTrue(a.m(new A()));
|
||||
for (o of commonFalseCases) {
|
||||
assertFalse(a.m(o));
|
||||
}
|
||||
for (o of commonThrowCases) {
|
||||
assertThrows(() => { a.m(o) }, TypeError);
|
||||
}
|
||||
|
||||
class B {
|
||||
get #foo() {}
|
||||
}
|
||||
assertFalse(a.m(new B()));
|
||||
})();
|
||||
|
||||
(function TestPrivateSetter() {
|
||||
class A {
|
||||
set #foo(a) {
|
||||
}
|
||||
m(other) {
|
||||
return #foo in other;
|
||||
}
|
||||
}
|
||||
let a = new A();
|
||||
assertTrue(a.m(a));
|
||||
assertTrue(a.m(new A()));
|
||||
for (o of commonFalseCases) {
|
||||
assertFalse(a.m(o));
|
||||
}
|
||||
for (o of commonThrowCases) {
|
||||
assertThrows(() => { a.m(o) }, TypeError);
|
||||
}
|
||||
|
||||
class B {
|
||||
set #foo(a) {}
|
||||
}
|
||||
assertFalse(a.m(new B()));
|
||||
})();
|
||||
|
||||
(function TestPrivateGetterAndSetter() {
|
||||
class A {
|
||||
get #foo() {}
|
||||
set #foo(a) {
|
||||
}
|
||||
m(other) {
|
||||
return #foo in other;
|
||||
}
|
||||
}
|
||||
let a = new A();
|
||||
assertTrue(a.m(a));
|
||||
assertTrue(a.m(new A()));
|
||||
for (o of commonFalseCases) {
|
||||
assertFalse(a.m(o));
|
||||
}
|
||||
for (o of commonThrowCases) {
|
||||
assertThrows(() => { a.m(o) }, TypeError);
|
||||
}
|
||||
|
||||
class B {
|
||||
get #foo() {}
|
||||
set #foo(a) {}
|
||||
}
|
||||
assertFalse(a.m(new B()));
|
||||
})();
|
||||
|
||||
(function TestPrivateStaticField() {
|
||||
class A {
|
||||
static m(other) {
|
||||
return #x in other;
|
||||
}
|
||||
static #x = 1;
|
||||
}
|
||||
assertTrue(A.m(A));
|
||||
assertFalse(A.m(new A()));
|
||||
for (o of commonFalseCases) {
|
||||
assertFalse(A.m(o));
|
||||
}
|
||||
for (o of commonThrowCases) {
|
||||
assertThrows(() => { A.m(o) }, TypeError);
|
||||
}
|
||||
|
||||
class B {
|
||||
static #x = 5;
|
||||
}
|
||||
assertFalse(A.m(B));
|
||||
})();
|
||||
|
||||
(function TestPrivateStaticMethod() {
|
||||
class A {
|
||||
static m(other) {
|
||||
return #pm in other;
|
||||
}
|
||||
static #pm() {}
|
||||
}
|
||||
assertTrue(A.m(A));
|
||||
assertFalse(A.m(new A()));
|
||||
for (o of commonFalseCases) {
|
||||
assertFalse(A.m(o));
|
||||
}
|
||||
for (o of commonThrowCases) {
|
||||
assertThrows(() => { A.m(o) }, TypeError);
|
||||
}
|
||||
|
||||
class B {
|
||||
static #pm() {};
|
||||
}
|
||||
assertFalse(A.m(B));
|
||||
})();
|
||||
|
||||
(function TestPrivateStaticGetter() {
|
||||
class A {
|
||||
static m(other) {
|
||||
return #x in other;
|
||||
}
|
||||
static get #x() {}
|
||||
}
|
||||
assertTrue(A.m(A));
|
||||
assertFalse(A.m(new A()));
|
||||
for (o of commonFalseCases) {
|
||||
assertFalse(A.m(o));
|
||||
}
|
||||
for (o of commonThrowCases) {
|
||||
assertThrows(() => { A.m(o) }, TypeError);
|
||||
}
|
||||
|
||||
class B {
|
||||
static get #x() {};
|
||||
}
|
||||
assertFalse(A.m(B));
|
||||
})();
|
||||
|
||||
(function TestPrivateStaticSetter() {
|
||||
class A {
|
||||
static m(other) {
|
||||
return #x in other;
|
||||
}
|
||||
static set #x(x) {}
|
||||
}
|
||||
assertTrue(A.m(A));
|
||||
assertFalse(A.m(new A()));
|
||||
for (o of commonFalseCases) {
|
||||
assertFalse(A.m(o));
|
||||
}
|
||||
for (o of commonThrowCases) {
|
||||
assertThrows(() => { A.m(o) }, TypeError);
|
||||
}
|
||||
|
||||
class B {
|
||||
static set #x(x) {};
|
||||
}
|
||||
assertFalse(A.m(B));
|
||||
})();
|
||||
|
||||
(function TestPrivateStaticGetterAndSetter() {
|
||||
class A {
|
||||
static m(other) {
|
||||
return #x in other;
|
||||
}
|
||||
static get #x() {}
|
||||
static set #x(x) {}
|
||||
}
|
||||
assertTrue(A.m(A));
|
||||
assertFalse(A.m(new A()));
|
||||
for (o of commonFalseCases) {
|
||||
assertFalse(A.m(o));
|
||||
}
|
||||
for (o of commonThrowCases) {
|
||||
assertThrows(() => { A.m(o) }, TypeError);
|
||||
}
|
||||
|
||||
class B {
|
||||
static get #x() {}
|
||||
static set #x(x) {};
|
||||
}
|
||||
assertFalse(A.m(B));
|
||||
})();
|
||||
|
||||
(function TestPrivateIdentifiersAreDistinct() {
|
||||
function GenerateClass() {
|
||||
class A {
|
||||
m(other) {
|
||||
return #x in other;
|
||||
}
|
||||
#x = 0;
|
||||
}
|
||||
return new A();
|
||||
}
|
||||
let a1 = GenerateClass();
|
||||
let a2 = GenerateClass();
|
||||
assertTrue(a1.m(a1));
|
||||
assertFalse(a1.m(a2));
|
||||
assertFalse(a2.m(a1));
|
||||
assertTrue(a2.m(a2));
|
||||
})();
|
||||
|
||||
(function TestSubclasses() {
|
||||
class A {
|
||||
m(other) { return #foo in other; }
|
||||
#foo;
|
||||
}
|
||||
class B extends A {}
|
||||
assertTrue((new A()).m(new B()));
|
||||
})();
|
||||
|
||||
(function TestFakeSubclassesWithPrivateField() {
|
||||
class A {
|
||||
#foo;
|
||||
m() { return #foo in this; }
|
||||
}
|
||||
let a = new A();
|
||||
assertTrue(a.m());
|
||||
|
||||
// Plug an object into the prototype chain; it's not a real instance of the
|
||||
// class.
|
||||
let fake = {__proto__: a};
|
||||
assertFalse(fake.m());
|
||||
})();
|
||||
|
||||
(function TestFakeSubclassesWithPrivateMethod() {
|
||||
class A {
|
||||
#pm() {}
|
||||
m() { return #pm in this; }
|
||||
}
|
||||
let a = new A();
|
||||
assertTrue(a.m());
|
||||
|
||||
// Plug an object into the prototype chain; it's not a real instance of the
|
||||
// class.
|
||||
let fake = {__proto__: a};
|
||||
assertFalse(fake.m());
|
||||
})();
|
||||
|
||||
(function TestPrivateNameUnknown() {
|
||||
assertThrows(() => { eval(`
|
||||
class A {
|
||||
m(other) { return #lol in other; }
|
||||
}
|
||||
new A().m();
|
||||
`)}, SyntaxError, /must be declared in an enclosing class/);
|
||||
})();
|
||||
|
||||
(function TestEvalWithPrivateField() {
|
||||
class A {
|
||||
m(other) {
|
||||
let result;
|
||||
eval('result = #x in other;');
|
||||
return result;
|
||||
}
|
||||
#x = 1;
|
||||
}
|
||||
let a = new A();
|
||||
assertTrue(a.m(a));
|
||||
assertTrue(a.m(new A()));
|
||||
for (o of commonFalseCases) {
|
||||
assertFalse(a.m(o));
|
||||
}
|
||||
for (o of commonThrowCases) {
|
||||
assertThrows(() => { a.m(o) }, TypeError);
|
||||
}
|
||||
})();
|
||||
|
||||
(function TestEvalWithPrivateMethod() {
|
||||
class A {
|
||||
m(other) {
|
||||
let result;
|
||||
eval('result = #pm in other;');
|
||||
return result;
|
||||
}
|
||||
#pm() {}
|
||||
}
|
||||
let a = new A();
|
||||
assertTrue(a.m(a));
|
||||
assertTrue(a.m(new A()));
|
||||
for (o of commonFalseCases) {
|
||||
assertFalse(a.m(o));
|
||||
}
|
||||
for (o of commonThrowCases) {
|
||||
assertThrows(() => { a.m(o) }, TypeError);
|
||||
}
|
||||
})();
|
||||
|
||||
(function TestEvalWithStaticPrivateField() {
|
||||
class A {
|
||||
static m(other) {
|
||||
let result;
|
||||
eval('result = #x in other;');
|
||||
return result;
|
||||
}
|
||||
static #x = 1;
|
||||
}
|
||||
assertTrue(A.m(A));
|
||||
assertFalse(A.m(new A()));
|
||||
for (o of commonFalseCases) {
|
||||
assertFalse(A.m(o));
|
||||
}
|
||||
for (o of commonThrowCases) {
|
||||
assertThrows(() => { A.m(o) }, TypeError);
|
||||
}
|
||||
})();
|
||||
|
||||
(function TestEvalWithStaticPrivateMethod() {
|
||||
class A {
|
||||
static m(other) {
|
||||
let result;
|
||||
eval('result = #pm in other;');
|
||||
return result;
|
||||
}
|
||||
static #pm() {}
|
||||
}
|
||||
assertTrue(A.m(A));
|
||||
assertFalse(A.m(new A()));
|
||||
for (o of commonFalseCases) {
|
||||
assertFalse(A.m(o));
|
||||
}
|
||||
for (o of commonThrowCases) {
|
||||
assertThrows(() => { A.m(o) }, TypeError);
|
||||
}
|
||||
})();
|
||||
|
||||
(function TestCombiningWithOtherExpressions() {
|
||||
class A {
|
||||
m() {
|
||||
assertFalse(#x in {} in {} in {});
|
||||
assertTrue(#x in this in {true: 0});
|
||||
assertTrue(#x in {} < 1 + 1);
|
||||
assertFalse(#x in this < 1);
|
||||
|
||||
assertThrows(() => { eval('#x in {} = 4')});
|
||||
assertThrows(() => { eval('(#x in {}) = 4')});
|
||||
}
|
||||
#x;
|
||||
}
|
||||
new A().m();
|
||||
})();
|
||||
|
||||
(function TestHalfConstructedObjects() {
|
||||
let half_constructed;
|
||||
class A {
|
||||
m() {
|
||||
assertTrue(#x in this);
|
||||
assertFalse(#y in this);
|
||||
}
|
||||
#x = 0;
|
||||
#y = (() => { half_constructed = this; throw 'lol';})();
|
||||
}
|
||||
|
||||
try {
|
||||
new A();
|
||||
} catch {
|
||||
}
|
||||
half_constructed.m();
|
||||
})();
|
||||
|
||||
(function TestPrivateFieldOpt() {
|
||||
class A {
|
||||
m(other) {
|
||||
return #x in other;
|
||||
}
|
||||
#x = 1;
|
||||
}
|
||||
let a = new A();
|
||||
%PrepareFunctionForOptimization(A.prototype.m);
|
||||
assertTrue(a.m(a));
|
||||
assertTrue(a.m(new A()));
|
||||
%OptimizeFunctionOnNextCall(A.prototype.m);
|
||||
assertTrue(a.m(a));
|
||||
assertTrue(a.m(new A()));
|
||||
|
||||
class B {
|
||||
#x = 5;
|
||||
}
|
||||
assertFalse(a.m(new B()));
|
||||
})();
|
||||
|
||||
(function TestPrivateMethodOpt() {
|
||||
class A {
|
||||
#pm() {
|
||||
}
|
||||
m(other) {
|
||||
return #pm in other;
|
||||
}
|
||||
}
|
||||
let a = new A();
|
||||
%PrepareFunctionForOptimization(A.prototype.m);
|
||||
assertTrue(a.m(a));
|
||||
assertTrue(a.m(new A()));
|
||||
%OptimizeFunctionOnNextCall(A.prototype.m);
|
||||
assertTrue(a.m(a));
|
||||
assertTrue(a.m(new A()));
|
||||
|
||||
class B {
|
||||
#pm() {}
|
||||
}
|
||||
assertFalse(a.m(new B()));
|
||||
})();
|
||||
|
||||
(function TestPrivateStaticFieldOpt() {
|
||||
class A {
|
||||
static m(other) {
|
||||
return #x in other;
|
||||
}
|
||||
static #x = 1;
|
||||
}
|
||||
%PrepareFunctionForOptimization(A.m);
|
||||
assertTrue(A.m(A));
|
||||
%OptimizeFunctionOnNextCall(A.m);
|
||||
assertTrue(A.m(A));
|
||||
|
||||
class B {
|
||||
static #x = 5;
|
||||
}
|
||||
assertFalse(A.m(B));
|
||||
})();
|
||||
|
||||
(function TestPrivateStaticMethodOpt() {
|
||||
class A {
|
||||
static m(other) {
|
||||
return #pm in other;
|
||||
}
|
||||
static #pm() {}
|
||||
}
|
||||
%PrepareFunctionForOptimization(A.m);
|
||||
assertTrue(A.m(A));
|
||||
%OptimizeFunctionOnNextCall(A.m);
|
||||
assertTrue(A.m(A));
|
||||
|
||||
class B {
|
||||
static #pm() {};
|
||||
}
|
||||
assertFalse(A.m(B));
|
||||
})();
|
||||
|
||||
(function TestPrivateFieldWithProxy() {
|
||||
class A {
|
||||
m(other) {
|
||||
return #x in other;
|
||||
}
|
||||
#x = 1;
|
||||
}
|
||||
let a = new A();
|
||||
|
||||
const p = new Proxy(a, {get: function() { assertUnreachable(); } });
|
||||
assertFalse(a.m(p));
|
||||
})();
|
||||
|
||||
(function TestHeritagePosition() {
|
||||
class A {
|
||||
#x; // A.#x
|
||||
static C = class C extends (function () {
|
||||
return class D {
|
||||
exfil(obj) { return #x in obj; }
|
||||
exfilEval(obj) { return eval("#x in obj"); }
|
||||
};
|
||||
}) { // C body starts
|
||||
#x; // C.#x
|
||||
} // C body ends
|
||||
} // A ends
|
||||
let c = new A.C();
|
||||
let d = new c();
|
||||
// #x inside D binds to A.#x, so only objects of A pass the check.
|
||||
assertTrue(d.exfil(new A()));
|
||||
assertFalse(d.exfil(c));
|
||||
assertFalse(d.exfil(d));
|
||||
assertTrue(d.exfilEval(new A()));
|
||||
assertFalse(d.exfilEval(c));
|
||||
assertFalse(d.exfilEval(d));
|
||||
})();
|
Loading…
Reference in New Issue
Block a user