[class] Ban arguments in class field initializers

Create a new function kind for initializer functions and ban arguments
if used in such a function.

Bug: v8:5367, v8:7183
Cq-Include-Trybots: luci.v8.try:v8_linux_noi18n_rel_ng
Change-Id: Id3089e587b3d6a25f27224045f250e032b831818
Reviewed-on: https://chromium-review.googlesource.com/850547
Commit-Queue: Sathya Gunasekaran <gsathya@chromium.org>
Reviewed-by: Adam Klein <adamk@chromium.org>
Cr-Commit-Position: refs/heads/master@{#50369}
This commit is contained in:
Sathya Gunasekaran 2018-01-04 14:36:27 -08:00 committed by Commit Bot
parent b822d3e17f
commit 3828ce0cae
24 changed files with 345 additions and 49 deletions

View File

@ -1437,6 +1437,10 @@ bool Scope::NeedsScopeInfo() const {
return NeedsContext();
}
bool Scope::ShouldBanArguments() {
return GetReceiverScope()->should_ban_arguments();
}
DeclarationScope* Scope::GetReceiverScope() {
Scope* scope = this;
while (!scope->is_script_scope() &&

View File

@ -396,6 +396,8 @@ class V8_EXPORT_PRIVATE Scope : public NON_EXPORTED_BASE(ZoneObject) {
return static_cast<Variable*>(variables_.Start()->value);
}
bool ShouldBanArguments();
// ---------------------------------------------------------------------------
// Variable allocation.
@ -696,6 +698,10 @@ class V8_EXPORT_PRIVATE DeclarationScope : public Scope {
bool asm_module() const { return asm_module_; }
void set_asm_module();
bool should_ban_arguments() const {
return IsClassFieldsInitializerFunction(function_kind());
}
void DeclareThis(AstValueFactory* ast_value_factory);
void DeclareArguments(AstValueFactory* ast_value_factory);
void DeclareDefaultFunctionVariables(AstValueFactory* ast_value_factory);

View File

@ -1095,7 +1095,6 @@ enum FunctionKind : uint16_t {
kArrowFunction = 1 << 0,
kGeneratorFunction = 1 << 1,
kConciseMethod = 1 << 2,
kConciseGeneratorMethod = kGeneratorFunction | kConciseMethod,
kDefaultConstructor = 1 << 3,
kDerivedConstructor = 1 << 4,
kBaseConstructor = 1 << 5,
@ -1103,6 +1102,10 @@ enum FunctionKind : uint16_t {
kSetterFunction = 1 << 7,
kAsyncFunction = 1 << 8,
kModule = 1 << 9,
kClassFieldsInitializerFunction = 1 << 10 | kConciseMethod,
kLastFunctionKind = kClassFieldsInitializerFunction,
kConciseGeneratorMethod = kGeneratorFunction | kConciseMethod,
kAccessorFunction = kGetterFunction | kSetterFunction,
kDefaultBaseConstructor = kDefaultConstructor | kBaseConstructor,
kDefaultDerivedConstructor = kDefaultConstructor | kDerivedConstructor,
@ -1134,7 +1137,8 @@ inline bool IsValidFunctionKind(FunctionKind kind) {
kind == FunctionKind::kAsyncArrowFunction ||
kind == FunctionKind::kAsyncConciseMethod ||
kind == FunctionKind::kAsyncConciseGeneratorMethod ||
kind == FunctionKind::kAsyncGeneratorFunction;
kind == FunctionKind::kAsyncGeneratorFunction ||
kind == FunctionKind::kClassFieldsInitializerFunction;
}
@ -1212,6 +1216,11 @@ inline bool IsClassConstructor(FunctionKind kind) {
return (kind & FunctionKind::kClassConstructor) != 0;
}
inline bool IsClassFieldsInitializerFunction(FunctionKind kind) {
DCHECK(IsValidFunctionKind(kind));
return kind == FunctionKind::kClassFieldsInitializerFunction;
}
inline bool IsConstructable(FunctionKind kind) {
if (IsAccessorFunction(kind)) return false;
if (IsConciseMethod(kind)) return false;

View File

@ -267,6 +267,8 @@ class ErrorUtils : public AllStatic {
T(ApplyNonFunction, \
"Function.prototype.apply was called on %, which is a % and not a " \
"function") \
T(ArgumentsDisallowedInInitializer, \
"'arguments' is not allowed in class field initializer") \
T(ArrayBufferTooShort, \
"Derived ArrayBuffer constructor created a buffer which was too small") \
T(ArrayBufferSpeciesThis, \

View File

@ -307,12 +307,14 @@ class ScopeInfo : public FixedArray {
class HasSimpleParametersField
: public BitField<bool, AsmModuleField::kNext, 1> {};
class FunctionKindField
: public BitField<FunctionKind, HasSimpleParametersField::kNext, 10> {};
: public BitField<FunctionKind, HasSimpleParametersField::kNext, 11> {};
class HasOuterScopeInfoField
: public BitField<bool, FunctionKindField::kNext, 1> {};
class IsDebugEvaluateScopeField
: public BitField<bool, HasOuterScopeInfoField::kNext, 1> {};
STATIC_ASSERT(kLastFunctionKind <= FunctionKindField::kMax);
// Properties of variables.
class VariableModeField : public BitField<VariableMode, 0, 3> {};
class InitFlagField : public BitField<InitializationFlag, 3, 1> {};

View File

@ -469,7 +469,7 @@ class SharedFunctionInfo : public HeapObject {
V(IsNativeBit, bool, 1, _) \
V(IsStrictBit, bool, 1, _) \
V(IsWrappedBit, bool, 1, _) \
V(FunctionKindBits, FunctionKind, 10, _) \
V(FunctionKindBits, FunctionKind, 11, _) \
V(HasDuplicateParametersBit, bool, 1, _) \
V(AllowLazyCompilationBit, bool, 1, _) \
V(NeedsHomeObjectBit, bool, 1, _) \
@ -486,6 +486,7 @@ class SharedFunctionInfo : public HeapObject {
STATIC_ASSERT(BailoutReason::kLastErrorMessage <=
DisabledOptimizationReasonBits::kMax);
STATIC_ASSERT(kLastFunctionKind <= FunctionKindBits::kMax);
// Masks for checking if certain FunctionKind bits are set without fully
// decoding of the FunctionKind bit field.
static const int kClassConstructorMask = FunctionKind::kClassConstructor

View File

@ -1663,6 +1663,13 @@ ParserBase<Impl>::ParseAndClassifyIdentifier(bool* ok) {
if (next == Token::IDENTIFIER || next == Token::ASYNC ||
(next == Token::AWAIT && !parsing_module_ && !is_async_function())) {
IdentifierT name = impl()->GetSymbol();
if (impl()->IsArguments(name) && scope()->ShouldBanArguments()) {
ReportMessage(MessageTemplate::kArgumentsDisallowedInInitializer);
*ok = false;
return impl()->NullIdentifier();
}
// When this function is used to read a formal parameter, we don't always
// know whether the function is going to be strict or sloppy. Indeed for
// arrow functions we don't always know that the identifier we are reading
@ -2416,7 +2423,8 @@ ParserBase<Impl>::ParseClassFieldInitializer(ClassInfo* class_info,
: class_info->instance_fields_scope;
if (initializer_scope == nullptr) {
initializer_scope = NewFunctionScope(FunctionKind::kConciseMethod);
initializer_scope =
NewFunctionScope(FunctionKind::kClassFieldsInitializerFunction);
// TODO(gsathya): Make scopes be non contiguous.
initializer_scope->set_start_position(scanner()->location().end_pos);
initializer_scope->SetLanguageMode(LanguageMode::kStrict);

View File

@ -3339,6 +3339,8 @@ void Parser::DeclareClassProperty(const AstRawString* class_name,
FunctionLiteral* Parser::CreateInitializerFunction(
DeclarationScope* scope, ZoneList<ClassLiteral::Property*>* fields) {
DCHECK_EQ(scope->function_kind(),
FunctionKind::kClassFieldsInitializerFunction);
// function() { .. class fields initializer .. }
ZoneList<Statement*>* statements = NewStatementList(1);
InitializeClassFieldsStatement* static_fields =

View File

@ -428,7 +428,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(143),
B(Wide), B(LdaSmi), I16(144),
B(Star), R(18),
B(LdaConstant), U8(16),
B(Star), R(19),

View File

@ -143,7 +143,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(143),
B(Wide), B(LdaSmi), I16(144),
B(Star), R(19),
B(LdaConstant), U8(13),
B(Star), R(20),
@ -432,7 +432,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(143),
B(Wide), B(LdaSmi), I16(144),
B(Star), R(19),
B(LdaConstant), U8(13),
B(Star), R(20),
@ -743,7 +743,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(143),
B(Wide), B(LdaSmi), I16(144),
B(Star), R(19),
B(LdaConstant), U8(13),
B(Star), R(20),
@ -991,7 +991,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(143),
B(Wide), B(LdaSmi), I16(144),
B(Star), R(16),
B(LdaConstant), U8(10),
B(Star), R(17),

View File

@ -86,7 +86,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(143),
B(Wide), B(LdaSmi), I16(144),
B(Star), R(11),
B(LdaConstant), U8(8),
B(Star), R(12),
@ -227,7 +227,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(143),
B(Wide), B(LdaSmi), I16(144),
B(Star), R(12),
B(LdaConstant), U8(8),
B(Star), R(13),
@ -380,7 +380,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(143),
B(Wide), B(LdaSmi), I16(144),
B(Star), R(11),
B(LdaConstant), U8(8),
B(Star), R(12),
@ -523,7 +523,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(143),
B(Wide), B(LdaSmi), I16(144),
B(Star), R(10),
B(LdaConstant), U8(10),
B(Star), R(11),

View File

@ -90,7 +90,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(143),
B(Wide), B(LdaSmi), I16(144),
B(Star), R(13),
B(LdaConstant), U8(7),
B(Star), R(14),
@ -268,7 +268,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(143),
B(Wide), B(LdaSmi), I16(144),
B(Star), R(13),
B(LdaConstant), U8(11),
B(Star), R(14),
@ -422,7 +422,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(143),
B(Wide), B(LdaSmi), I16(144),
B(Star), R(11),
B(LdaConstant), U8(9),
B(Star), R(12),
@ -524,7 +524,7 @@ bytecodes: [
B(JumpIfUndefined), U8(6),
B(Ldar), R(6),
B(JumpIfNotNull), U8(16),
B(LdaSmi), I8(73),
B(LdaSmi), I8(74),
B(Star), R(17),
B(LdaConstant), U8(4),
B(Star), R(18),
@ -580,7 +580,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(143),
B(Wide), B(LdaSmi), I16(144),
B(Star), R(16),
B(LdaConstant), U8(9),
B(Star), R(17),
@ -754,7 +754,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(143),
B(Wide), B(LdaSmi), I16(144),
B(Star), R(16),
B(LdaConstant), U8(10),
B(Star), R(17),
@ -953,7 +953,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(143),
B(Wide), B(LdaSmi), I16(144),
B(Star), R(15),
B(LdaConstant), U8(14),
B(Star), R(16),
@ -1116,7 +1116,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(143),
B(Wide), B(LdaSmi), I16(144),
B(Star), R(20),
B(LdaConstant), U8(7),
B(Star), R(21),
@ -1358,7 +1358,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(143),
B(Wide), B(LdaSmi), I16(144),
B(Star), R(20),
B(LdaConstant), U8(9),
B(Star), R(21),

View File

@ -257,7 +257,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(143),
B(Wide), B(LdaSmi), I16(144),
B(Star), R(14),
B(LdaConstant), U8(15),
B(Star), R(15),

View File

@ -231,7 +231,7 @@ bytecodes: [
B(JumpIfUndefined), U8(6),
B(Ldar), R(3),
B(JumpIfNotNull), U8(16),
B(LdaSmi), I8(73),
B(LdaSmi), I8(74),
B(Star), R(4),
B(LdaConstant), U8(1),
B(Star), R(5),

View File

@ -4751,6 +4751,9 @@ TEST(StaticClassFieldsNoErrors) {
"static ['a']\n *b(){}",
"static ['a']\n ['b'](){}",
"static a = function t() { arguments; }",
"static a = () => function t() { arguments; }",
// ASI edge cases
"static a\n get",
"static get\n *a(){}",
@ -4843,6 +4846,9 @@ TEST(ClassFieldsNoErrors) {
"get\n *a(){}",
"a\n static",
"a = function t() { arguments; }",
"a = () => function() { arguments; }",
// Misc edge cases
"yield",
"yield = 0",
@ -4891,6 +4897,14 @@ TEST(StaticClassFieldsErrors) {
"static async a = 0",
"static async a",
"static a = arguments",
"static a = () => arguments",
"static a = () => { arguments }",
"static a = arguments[0]",
"static a = delete arguments[0]",
"static a = f(arguments)",
"static a = () => () => arguments",
// ASI requires a linebreak
"static a b",
"static a = 0 b",
@ -4931,6 +4945,14 @@ TEST(ClassFieldsErrors) {
"async a = 0",
"async a",
"a = arguments",
"a = () => arguments",
"a = () => { arguments }",
"a = arguments[0]",
"a = delete arguments[0]",
"a = f(arguments)",
"a = () => () => arguments",
// ASI requires a linebreak
"a b",
"a = 0 b",

View File

@ -652,3 +652,47 @@ x()();
}
}, ReferenceError);
}
{
class X {
p = function() { return arguments[0]; }
}
let x = new X;
assertEquals(1, x.p(1));
}
{
class X {
t = () => {
function p() { return arguments[0]; };
return p;
}
}
let x = new X;
let p = x.t();
assertEquals(1, p(1));
}
{
class X {
t = () => {
function p() { return eval("arguments[0]"); };
return p;
}
}
let x = new X;
let p = x.t();
assertEquals(1, p(1));
}
{
class X {
p = eval("(function() { return arguments[0]; })(1)");
}
let x = new X;
assertEquals(1, x.p);
}

View File

@ -416,3 +416,44 @@ y()();
assertEquals(1, obj.x);
assertEquals(2, klass.x);
}
{
class X {
static p = function() { return arguments[0]; }
}
assertEquals(1, X.p(1));
}
{
class X {
static t = () => {
function p() { return arguments[0]; };
return p;
}
}
let p = X.t();
assertEquals(1, p(1));
}
{
class X {
static t = () => {
function p() { return eval("arguments[0]"); };
return p;
}
}
let p = X.t();
assertEquals(1, p(1));
}
{
class X {
static p = eval("(function() { return arguments[0]; })(1)");
}
assertEquals(1, X.p);
}

View File

@ -0,0 +1,26 @@
/*---
description: Syntax error if `arguments` used in class field (arrow function expression)
esid: sec-class-definitions-static-semantics-early-errors
features: [class, class-fields-public, arrow-function]
flags: [generated]
negative:
phase: early
type: SyntaxError
info: |
Static Semantics: Early Errors
FieldDefinition:
PropertyNameInitializeropt
- It is a Syntax Error if ContainsArguments of Initializer is true.
Static Semantics: ContainsArguments
IdentifierReference : Identifier
1. If the StringValue of Identifier is "arguments", return true.
...
For all other grammatical productions, recurse on all nonterminals. If any piece returns true, then return true. Otherwise return false.
---*/
throw "Test262: This statement should not be evaluated.";
var C = class {
x = () => {
var t = () => { arguments; };
t();
}
}

View File

@ -0,0 +1,33 @@
/*---
description: error if `arguments` in StatementList of eval (direct eval)
esid: sec-performeval-rules-in-initializer
features: [class, class-fields-public, arrow-function]
flags: [generated]
info: |
Static Semantics: Early Errors
FieldDefinition:
PropertyNameInitializeropt
- It is a Syntax Error if ContainsArguments of Initializer is true.
Static Semantics: ContainsArguments
IdentifierReference : Identifier
1. If the StringValue of Identifier is "arguments", return true.
...
For all other grammatical productions, recurse on all nonterminals. If any piece returns true, then return true. Otherwise return false.
---*/
var C = class {
x = () => {
var t = () => { eval("arguments"); };
t();
}
}
assert.throws(SyntaxError, function() {
var c = new C();
c.x();
});

View File

@ -0,0 +1,30 @@
/*---
description: error if `arguments` in StatementList of eval (direct eval)
esid: sec-performeval-rules-in-initializer
features: [class, class-fields-public, arrow-function]
flags: [generated]
info: |
Static Semantics: Early Errors
FieldDefinition:
PropertyNameInitializeropt
- It is a Syntax Error if ContainsArguments of Initializer is true.
Static Semantics: ContainsArguments
IdentifierReference : Identifier
1. If the StringValue of Identifier is "arguments", return true.
...
For all other grammatical productions, recurse on all nonterminals. If any piece returns true, then return true. Otherwise return false.
---*/
var C = class {
x = eval("() => arguments");
}
assert.throws(SyntaxError, function() {
var c = new C();
c.x();
});

View File

@ -0,0 +1,26 @@
/*---
description: Syntax error if `arguments` used in class field (arrow function expression)
esid: sec-class-definitions-static-semantics-early-errors
features: [class, class-fields-public, arrow-function]
flags: [generated]
negative:
phase: early
type: SyntaxError
info: |
Static Semantics: Early Errors
FieldDefinition:
PropertyNameInitializeropt
- It is a Syntax Error if ContainsArguments of Initializer is true.
Static Semantics: ContainsArguments
IdentifierReference : Identifier
1. If the StringValue of Identifier is "arguments", return true.
...
For all other grammatical productions, recurse on all nonterminals. If any piece returns true, then return true. Otherwise return false.
---*/
throw "Test262: This statement should not be evaluated.";
class C {
x = () => {
var t = () => { arguments; };
t();
}
}

View File

@ -0,0 +1,34 @@
/*---
description: error if `arguments` in StatementList of eval (direct eval)
esid: sec-performeval-rules-in-initializer
features: [class, class-fields-public, arrow-function]
flags: [generated]
info: |
Static Semantics: Early Errors
FieldDefinition:
PropertyNameInitializeropt
- It is a Syntax Error if ContainsArguments of Initializer is true.
Static Semantics: ContainsArguments
IdentifierReference : Identifier
1. If the StringValue of Identifier is "arguments", return true.
...
For all other grammatical productions, recurse on all nonterminals. If any piece returns true, then return true. Otherwise return false.
---*/
class C {
x = () => {
var t = () => { eval("arguments"); };
t();
}
}
assert.throws(SyntaxError, function() {
var c = new C();
c.x();
});

View File

@ -0,0 +1,30 @@
/*---
description: error if `arguments` in StatementList of eval (direct eval)
esid: sec-performeval-rules-in-initializer
features: [class, class-fields-public, arrow-function]
flags: [generated]
info: |
Static Semantics: Early Errors
FieldDefinition:
PropertyNameInitializeropt
- It is a Syntax Error if ContainsArguments of Initializer is true.
Static Semantics: ContainsArguments
IdentifierReference : Identifier
1. If the StringValue of Identifier is "arguments", return true.
...
For all other grammatical productions, recurse on all nonterminals. If any piece returns true, then return true. Otherwise return false.
---*/
class C {
x = eval("() => arguments");
}
assert.throws(SyntaxError, function() {
var c = new C();
c.x();
});

View File

@ -455,30 +455,6 @@
'built-ins/TypedArrays/BigInt64Array/*': [SKIP],
'built-ins/TypedArrays/BigUint64Array/*': [SKIP],
# https://bugs.chromium.org/p/v8/issues/detail?id=7183
'language/expressions/class/fields-arrow-fnc-init-err-contains-arguments': [FAIL],
'language/expressions/class/fields-comp-name-init-err-contains-arguments': [FAIL],
'language/expressions/class/fields-direct-eval-err-contains-arguments': [FAIL],
'language/expressions/class/fields-equality-init-err-contains-arguments': [FAIL],
'language/expressions/class/fields-literal-name-init-err-contains-arguments': [FAIL],
'language/expressions/class/fields-static-literal-init-err-contains-arguments': [FAIL],
'language/expressions/class/fields-static-string-literal-name-init-err-contains-arguments': [FAIL],
'language/expressions/class/fields-string-literal-name-init-err-contains-arguments': [FAIL],
'language/expressions/class/fields-ternary-init-err-contains-arguments': [FAIL],
'language/expressions/class/fields-typeof-init-err-contains-arguments': [FAIL],
'language/statements/class/fields-arrow-fnc-init-err-contains-arguments': [FAIL],
'language/statements/class/fields-direct-eval-err-contains-arguments': [FAIL],
'language/statements/class/fields-equality-init-err-contains-arguments': [FAIL],
'language/statements/class/fields-literal-name-init-err-contains-arguments': [FAIL],
'language/statements/class/fields-static-comp-name-init-err-contains-arguments': [FAIL],
'language/statements/class/fields-static-literal-init-err-contains-arguments': [FAIL],
'language/statements/class/fields-static-string-literal-name-init-err-contains-arguments': [FAIL],
'language/statements/class/fields-string-literal-name-init-err-contains-arguments': [FAIL],
'language/statements/class/fields-typeof-init-err-contains-arguments': [FAIL],
'language/expressions/class/fields-static-comp-name-init-err-contains-arguments': [FAIL],
'language/statements/class/fields-ternary-init-err-contains-arguments': [FAIL],
'language/statements/class/fields-comp-name-init-err-contains-arguments': [FAIL],
# https://bugs.chromium.org/p/v8/issues/detail?id=7184
'annexB/language/expressions/yield/star-iterable-return-emulates-undefined-throws-when-called': [FAIL],
'annexB/language/statements/for-await-of/iterator-close-return-emulates-undefined-throws-when-called': [FAIL],