Commit 94d53d87 authored by Daniel Ehrenberg's avatar Daniel Ehrenberg Committed by Commit Bot

[class] Split out static fields into a separate flag

This patch implements https://github.com/tc39/proposal-class-fields/pull/65
and https://github.com/tc39/proposal-static-class-features/ by
splitting out instance and static field declarations into separate
flags for the separate proposals. Instance class fields is currently
at Stage 3 whereas static class fields is currently at Stage 2.

Bug: v8:5367
Change-Id: I133c945fd0b22dc5718c7bb61b10f22348087acd
Reviewed-on: https://chromium-review.googlesource.com/839778
Commit-Queue: Daniel Ehrenberg <littledan@chromium.org>
Reviewed-by: 's avatarSathya Gunasekaran <gsathya@chromium.org>
Cr-Commit-Position: refs/heads/master@{#50293}
parent 34235169
......@@ -4312,6 +4312,7 @@ EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_regexp_property)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_function_sent)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_function_tostring)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_public_fields)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_static_fields)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_class_fields)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_dynamic_import)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_import_meta)
......
......@@ -197,6 +197,7 @@ DEFINE_IMPLICATION(es_staging, harmony)
// Enabling import.meta requires to also enable import()
DEFINE_IMPLICATION(harmony_import_meta, harmony_dynamic_import)
DEFINE_IMPLICATION(harmony_class_fields, harmony_public_fields)
DEFINE_IMPLICATION(harmony_class_fields, harmony_static_fields)
// Features that are still work in progress (behind individual flags).
#define HARMONY_INPROGRESS(V) \
......@@ -206,6 +207,7 @@ DEFINE_IMPLICATION(harmony_class_fields, harmony_public_fields)
V(harmony_do_expressions, "harmony do-expressions") \
V(harmony_class_fields, "harmony fields in class literals") \
V(harmony_public_fields, "harmony public fields in class literals") \
V(harmony_static_fields, "harmony static fields in class literals") \
V(harmony_bigint, "harmony arbitrary precision integers")
// Features that are complete (but still behind --harmony/es-staging flag).
......
......@@ -279,6 +279,7 @@ class ParserBase {
allow_harmony_do_expressions_(false),
allow_harmony_function_sent_(false),
allow_harmony_public_fields_(false),
allow_harmony_static_fields_(false),
allow_harmony_dynamic_import_(false),
allow_harmony_import_meta_(false),
allow_harmony_async_iteration_(false) {}
......@@ -291,6 +292,7 @@ class ParserBase {
ALLOW_ACCESSORS(harmony_do_expressions);
ALLOW_ACCESSORS(harmony_function_sent);
ALLOW_ACCESSORS(harmony_public_fields);
ALLOW_ACCESSORS(harmony_static_fields);
ALLOW_ACCESSORS(harmony_dynamic_import);
ALLOW_ACCESSORS(harmony_import_meta);
ALLOW_ACCESSORS(harmony_async_iteration);
......@@ -1529,6 +1531,7 @@ class ParserBase {
bool allow_harmony_do_expressions_;
bool allow_harmony_function_sent_;
bool allow_harmony_public_fields_;
bool allow_harmony_static_fields_;
bool allow_harmony_dynamic_import_;
bool allow_harmony_import_meta_;
bool allow_harmony_async_iteration_;
......@@ -2295,6 +2298,11 @@ ParserBase<Impl>::ParseClassPropertyDefinition(
case PropertyKind::kValueProperty:
if (allow_harmony_public_fields()) {
*property_kind = ClassLiteralProperty::FIELD;
if (*is_static && !allow_harmony_static_fields()) {
ReportUnexpectedToken(Next());
*ok = false;
return impl()->NullLiteralProperty();
}
if (!*is_computed_name) {
checker->CheckClassFieldName(*is_static,
CHECK_OK_CUSTOM(NullLiteralProperty));
......
......@@ -543,6 +543,7 @@ Parser::Parser(ParseInfo* info)
set_allow_harmony_do_expressions(FLAG_harmony_do_expressions);
set_allow_harmony_function_sent(FLAG_harmony_function_sent);
set_allow_harmony_public_fields(FLAG_harmony_public_fields);
set_allow_harmony_static_fields(FLAG_harmony_static_fields);
set_allow_harmony_dynamic_import(FLAG_harmony_dynamic_import);
set_allow_harmony_import_meta(FLAG_harmony_import_meta);
set_allow_harmony_async_iteration(FLAG_harmony_async_iteration);
......@@ -3318,6 +3319,7 @@ void Parser::DeclareClassProperty(const AstRawString* class_name,
DCHECK(allow_harmony_public_fields());
if (is_static) {
DCHECK(allow_harmony_static_fields());
class_info->static_fields->Add(property, zone());
} else {
class_info->instance_fields->Add(property, zone());
......
......@@ -301,6 +301,7 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
SET_ALLOW(harmony_do_expressions);
SET_ALLOW(harmony_function_sent);
SET_ALLOW(harmony_public_fields);
SET_ALLOW(harmony_static_fields);
SET_ALLOW(harmony_dynamic_import);
SET_ALLOW(harmony_import_meta);
SET_ALLOW(harmony_async_iteration);
......
......@@ -45,6 +45,7 @@ class ProgramOptions final {
do_expressions_(false),
async_iteration_(false),
public_fields_(false),
static_fields_(false),
verbose_(false) {}
bool Validate() const;
......@@ -65,6 +66,7 @@ class ProgramOptions final {
bool do_expressions() const { return do_expressions_; }
bool async_iteration() const { return async_iteration_; }
bool public_fields() const { return public_fields_; }
bool static_fields() const { return static_fields_; }
bool verbose() const { return verbose_; }
bool suppress_runtime_errors() const { return rebaseline_ && !verbose_; }
std::vector<std::string> input_filenames() const { return input_filenames_; }
......@@ -83,6 +85,7 @@ class ProgramOptions final {
bool do_expressions_;
bool async_iteration_;
bool public_fields_;
bool static_fields_;
bool verbose_;
std::vector<std::string> input_filenames_;
std::string output_filename_;
......@@ -174,6 +177,8 @@ ProgramOptions ProgramOptions::FromCommandLine(int argc, char** argv) {
options.async_iteration_ = true;
} else if (strcmp(argv[i], "--public-fields") == 0) {
options.public_fields_ = true;
} else if (strcmp(argv[i], "--static-fields") == 0) {
options.static_fields_ = true;
} else if (strcmp(argv[i], "--verbose") == 0) {
options.verbose_ = true;
} else if (strncmp(argv[i], "--output=", 9) == 0) {
......@@ -280,6 +285,8 @@ void ProgramOptions::UpdateFromHeader(std::istream& stream) {
async_iteration_ = ParseBoolean(line.c_str() + 17);
} else if (line.compare(0, 15, "public fields: ") == 0) {
public_fields_ = ParseBoolean(line.c_str() + 15);
} else if (line.compare(0, 15, "static fields: ") == 0) {
static_fields_ = ParseBoolean(line.c_str() + 15);
} else if (line == "---") {
break;
} else if (line.empty()) {
......@@ -304,6 +311,7 @@ void ProgramOptions::PrintHeader(std::ostream& stream) const { // NOLINT
if (do_expressions_) stream << "\ndo expressions: yes";
if (async_iteration_) stream << "\nasync iteration: yes";
if (public_fields_) stream << "\npublic fields: yes";
if (static_fields_) stream << "\nstatic fields: yes";
stream << "\n\n";
}
......@@ -409,6 +417,7 @@ void GenerateExpectationsFile(std::ostream& stream, // NOLINT
if (options.do_expressions()) i::FLAG_harmony_do_expressions = true;
if (options.async_iteration()) i::FLAG_harmony_async_iteration = true;
if (options.public_fields()) i::FLAG_harmony_public_fields = true;
if (options.static_fields()) i::FLAG_harmony_static_fields = true;
stream << "#\n# Autogenerated by generate-bytecode-expectations.\n#\n\n";
options.PrintHeader(stream);
......@@ -419,6 +428,7 @@ void GenerateExpectationsFile(std::ostream& stream, // NOLINT
i::FLAG_harmony_do_expressions = false;
i::FLAG_harmony_async_iteration = false;
i::FLAG_harmony_public_fields = false;
i::FLAG_harmony_static_fields = false;
}
bool WriteExpectationsFile(const std::vector<std::string>& snippet_list,
......@@ -467,6 +477,7 @@ void PrintUsage(const char* exec_path) {
" --do-expressions Enable harmony_do_expressions flag.\n"
" --async-iteration Enable harmony_async_iteration flag.\n"
" --public-fields Enable harmony_public_fields flag.\n"
" --static-fields Enable harmony_static_fields flag.\n"
" --output=file.name\n"
" Specify the output file. If not specified, output goes to "
"stdout.\n"
......
......@@ -2239,6 +2239,62 @@ TEST(ClassFields) {
InitializedIgnitionHandleScope scope;
BytecodeExpectationsPrinter printer(CcTest::isolate());
const char* snippets[] = {
"{\n"
" class A {\n"
" a;\n"
" ['b'];\n"
" }\n"
"\n"
" class B {\n"
" a = 1;\n"
" ['b'] = this.a;\n"
" }\n"
" new A;\n"
" new B;\n"
"}\n",
"{\n"
" class A extends class {} {\n"
" a;\n"
" ['b'];\n"
" }\n"
"\n"
" class B extends class {} {\n"
" a = 1;\n"
" ['b'] = this.a;\n"
" foo() { return 1; }\n"
" constructor() {\n"
" super();\n"
" }\n"
" }\n"
"\n"
" class C extends B {\n"
" a = 1;\n"
" ['b'] = this.a;\n"
" constructor() {\n"
" (() => super())();\n"
" }\n"
" }\n"
"\n"
" new A;\n"
" new B;\n"
" new C;\n"
"}\n"};
CHECK(CompareTexts(BuildActual(printer, snippets),
LoadGolden("ClassFields.golden")));
i::FLAG_harmony_public_fields = old_flag;
}
TEST(StaticClassFields) {
bool old_flag = i::FLAG_harmony_public_fields;
bool old_static_flag = i::FLAG_harmony_static_fields;
i::FLAG_harmony_public_fields = true;
i::FLAG_harmony_static_fields = true;
InitializedIgnitionHandleScope scope;
BytecodeExpectationsPrinter printer(CcTest::isolate());
const char* snippets[] = {
"{\n"
" class A {\n"
......@@ -2293,8 +2349,9 @@ TEST(ClassFields) {
"}\n"};
CHECK(CompareTexts(BuildActual(printer, snippets),
LoadGolden("ClassFields.golden")));
LoadGolden("StaticClassFields.golden")));
i::FLAG_harmony_public_fields = old_flag;
i::FLAG_harmony_static_fields = old_static_flag;
}
TEST(Generators) {
......
......@@ -660,11 +660,23 @@ TEST(PreParserScopeAnalysis) {
[] { i::FLAG_harmony_public_fields = true; },
[] { i::FLAG_harmony_public_fields = false; }},
{"class X { static ['foo'] = 2; }; new X;",
[] { i::FLAG_harmony_public_fields = true; },
[] { i::FLAG_harmony_public_fields = false; }},
[] {
i::FLAG_harmony_public_fields = true;
i::FLAG_harmony_static_fields = true;
},
[] {
i::FLAG_harmony_public_fields = false;
i::FLAG_harmony_static_fields = false;
}},
{"class X { ['bar'] = 1; static ['foo'] = 2; }; new X;",
[] { i::FLAG_harmony_public_fields = true; },
[] { i::FLAG_harmony_public_fields = false; }},
[] {
i::FLAG_harmony_public_fields = true;
i::FLAG_harmony_static_fields = true;
},
[] {
i::FLAG_harmony_public_fields = false;
i::FLAG_harmony_static_fields = false;
}},
};
for (unsigned outer_ix = 0; outer_ix < arraysize(outers); ++outer_ix) {
......
......@@ -1315,6 +1315,7 @@ enum ParserFlag {
kAllowNatives,
kAllowHarmonyFunctionSent,
kAllowHarmonyPublicFields,
kAllowHarmonyStaticFields,
kAllowHarmonyDynamicImport,
kAllowHarmonyAsyncIteration,
kAllowHarmonyImportMeta,
......@@ -1330,6 +1331,7 @@ void SetGlobalFlags(i::EnumSet<ParserFlag> flags) {
i::FLAG_allow_natives_syntax = flags.Contains(kAllowNatives);
i::FLAG_harmony_function_sent = flags.Contains(kAllowHarmonyFunctionSent);
i::FLAG_harmony_public_fields = flags.Contains(kAllowHarmonyPublicFields);
i::FLAG_harmony_static_fields = flags.Contains(kAllowHarmonyStaticFields);
i::FLAG_harmony_dynamic_import = flags.Contains(kAllowHarmonyDynamicImport);
i::FLAG_harmony_import_meta = flags.Contains(kAllowHarmonyImportMeta);
i::FLAG_harmony_async_iteration = flags.Contains(kAllowHarmonyAsyncIteration);
......@@ -1341,6 +1343,8 @@ void SetParserFlags(i::PreParser* parser, i::EnumSet<ParserFlag> flags) {
flags.Contains(kAllowHarmonyFunctionSent));
parser->set_allow_harmony_public_fields(
flags.Contains(kAllowHarmonyPublicFields));
parser->set_allow_harmony_static_fields(
flags.Contains(kAllowHarmonyStaticFields));
parser->set_allow_harmony_dynamic_import(
flags.Contains(kAllowHarmonyDynamicImport));
parser->set_allow_harmony_import_meta(
......@@ -4693,6 +4697,93 @@ TEST(ClassPropertyNameNoErrors) {
RunParserSyncTest(context_data, name_data, kSuccess);
}
TEST(StaticClassFieldsNoErrors) {
// clang-format off
// Tests proposed class fields syntax.
const char* context_data[][2] = {{"(class {", "});"},
{"(class extends Base {", "});"},
{"class C {", "}"},
{"class C extends Base {", "}"},
{nullptr, nullptr}};
const char* class_body_data[] = {
// Basic syntax
"static a = 0;",
"static a = 0; b",
"static a = 0; b(){}",
"static a = 0; *b(){}",
"static a = 0; ['b'](){}",
"static a;",
"static a; b;",
"static a; b(){}",
"static a; *b(){}",
"static a; ['b'](){}",
"static ['a'] = 0;",
"static ['a'] = 0; b",
"static ['a'] = 0; b(){}",
"static ['a'] = 0; *b(){}",
"static ['a'] = 0; ['b'](){}",
"static ['a'];",
"static ['a']; b;",
"static ['a']; b(){}",
"static ['a']; *b(){}",
"static ['a']; ['b'](){}",
"static 0 = 0;",
"static 0;",
"static 'a' = 0;",
"static 'a';",
// ASI
"static a = 0\n",
"static a = 0\n b",
"static a = 0\n b(){}",
"static a\n",
"static a\n b\n",
"static a\n b(){}",
"static a\n *b(){}",
"static a\n ['b'](){}",
"static ['a'] = 0\n",
"static ['a'] = 0\n b",
"static ['a'] = 0\n b(){}",
"static ['a']\n",
"static ['a']\n b\n",
"static ['a']\n b(){}",
"static ['a']\n *b(){}",
"static ['a']\n ['b'](){}",
// ASI edge cases
"static a\n get",
"static get\n *a(){}",
"static a\n static",
// Misc edge cases
"static yield",
"static yield = 0",
"static yield\n a",
"static async;",
"static async = 0;",
"static async",
"static async = 0",
"static async\n a(){}", // a field named async, and a method named a.
"static async\n a",
"static await;",
"static await = 0;",
"static await\n a",
nullptr
};
// clang-format on
static const ParserFlag always_flags[] = {kAllowHarmonyPublicFields,
kAllowHarmonyStaticFields};
RunParserSyncTest(context_data, class_body_data, kSuccess, nullptr, 0,
always_flags, arraysize(always_flags));
// Without the static flag, all of these are errors
static const ParserFlag no_static_flags[] = {kAllowHarmonyPublicFields};
RunParserSyncTest(context_data, class_body_data, kError, nullptr, 0,
no_static_flags, arraysize(no_static_flags));
}
TEST(ClassFieldsNoErrors) {
// clang-format off
// Tests proposed class fields syntax.
......@@ -4729,15 +4820,6 @@ TEST(ClassFieldsNoErrors) {
"'a' = 0;",
"'a';",
"static a = 0;",
"static a;",
"static ['a'] = 0",
"static ['a']",
"static 0 = 0;",
"static 0;",
"static 'a' = 0;",
"static 'a';",
// ASI
"a = 0\n",
"a = 0\n b",
......@@ -4767,10 +4849,8 @@ TEST(ClassFieldsNoErrors) {
"yield\n a",
"async;",
"async = 0;",
"static async;"
"async",
"async = 0",
"static async",
"async\n a(){}", // a field named async, and a method named a.
"async\n a",
"await;",
......@@ -4783,6 +4863,53 @@ TEST(ClassFieldsNoErrors) {
static const ParserFlag always_flags[] = {kAllowHarmonyPublicFields};
RunParserSyncTest(context_data, class_body_data, kSuccess, nullptr, 0,
always_flags, arraysize(always_flags));
static const ParserFlag static_flags[] = {kAllowHarmonyPublicFields,
kAllowHarmonyStaticFields};
RunParserSyncTest(context_data, class_body_data, kSuccess, nullptr, 0,
static_flags, arraysize(static_flags));
}
TEST(StaticClassFieldsErrors) {
// clang-format off
// Tests proposed class fields syntax.
const char* context_data[][2] = {{"(class {", "});"},
{"(class extends Base {", "});"},
{"class C {", "}"},
{"class C extends Base {", "}"},
{nullptr, nullptr}};
const char* class_body_data[] = {
"static a : 0",
"static a =",
"static constructor",
"static prototype",
"static *a = 0",
"static *a",
"static get a",
"static get\n a",
"static yield a",
"static async a = 0",
"static async a",
// ASI requires a linebreak
"static a b",
"static a = 0 b",
// ASI requires that the next token is not part of any legal production
"static a = 0\n *b(){}",
"static a = 0\n ['b'](){}",
nullptr
};
// clang-format on
static const ParserFlag no_static_flags[] = {kAllowHarmonyPublicFields};
RunParserSyncTest(context_data, class_body_data, kError, nullptr, 0,
no_static_flags, arraysize(no_static_flags));
static const ParserFlag always_flags[] = {kAllowHarmonyPublicFields,
kAllowHarmonyStaticFields};
RunParserSyncTest(context_data, class_body_data, kError, nullptr, 0,
always_flags, arraysize(always_flags));
}
TEST(ClassFieldsErrors) {
......@@ -4796,19 +4923,11 @@ TEST(ClassFieldsErrors) {
const char* class_body_data[] = {
"a : 0",
"a =",
"static constructor",
"static prototype",
"constructor",
"*a = 0",
"*a",
"get a",
"yield a",
"a : 0;",
"a =;",
"*a = 0;",
"*a;",
"get a;",
"yield a;",
"async a = 0",
"async a",
......@@ -4827,6 +4946,11 @@ TEST(ClassFieldsErrors) {
static const ParserFlag always_flags[] = {kAllowHarmonyPublicFields};
RunParserSyncTest(context_data, class_body_data, kError, nullptr, 0,
always_flags, arraysize(always_flags));
static const ParserFlag static_flags[] = {kAllowHarmonyPublicFields,
kAllowHarmonyStaticFields};
RunParserSyncTest(context_data, class_body_data, kError, nullptr, 0,
static_flags, arraysize(static_flags));
}
TEST(ClassExpressionErrors) {
......
......@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --harmony-public-fields
// Flags: --harmony-public-fields --harmony-static-fields
class X {
static constructor = function() {};
......
......@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --harmony-public-fields
// Flags: --harmony-public-fields --harmony-static-fields
class X {
static prototype = function() {};
......
......@@ -52,16 +52,8 @@
b = x;
c = 1;
hasOwnProperty() { return 1;}
static [x] = 2;
static b = 3;
static d;
}
assertEquals(2, C.a);
assertEquals(3, C.b);
assertEquals(undefined, C.d);
assertEquals(undefined, C.c);
let c = new C;
assertEquals(undefined, c.a);
assertEquals('a', c.b);
......@@ -281,10 +273,10 @@
}
class C {
[run(1)] = run(7);
[run(2)] = run(8);
[run(1)] = run(6);
[run(2)] = run(7);
[run(3)]() { run(9);}
static [run(4)] = run(6);
[run(4)] = run(8);
[run(5)]() { throw new Error('should not execute');};
}
......@@ -303,10 +295,10 @@ function x() {
}
class C {
[run(1)] = run(7);
[run(2)] = run(8);
[run(1)] = run(6);
[run(2)] = run(7);
[run(3)]() { run(9);}
static [run(4)] = run(6);
[run(4)] = run(8);
[run(5)]() { throw new Error('should not execute');};
}
......@@ -636,20 +628,6 @@ x()();
assertEquals(1, obj.x);
}
{
function t() {
return class {
['x'] = 1;
static ['x'] = 2;
}
}
let klass = t();
let obj = new klass;
assertEquals(1, obj.x);
assertEquals(2, klass.x);
}
{
new class {
t = 1;
......
......@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --harmony-public-fields
// Flags: --harmony-public-fields --harmony-static-fields
"use strict";
......@@ -312,6 +312,50 @@ function x() {
}
x()();
{
let log = [];
function run(i) {
log.push(i);
return i;
}
class C {
[run(1)] = run(7);
[run(2)] = run(8);
[run(3)]() { run(9);}
static [run(4)] = run(6);
[run(5)]() { throw new Error('should not execute');};
}
let c = new C;
c[3]();
assertEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], log);
}
function y() {
// This tests lazy parsing.
return function() {
let log = [];
function run(i) {
log.push(i);
return i;
}
class C {
[run(1)] = run(7);
[run(2)] = run(8);
[run(3)]() { run(9);}
static [run(4)] = run(6);
[run(5)]() { throw new Error('should not execute');};
}
let c = new C;
c[3]();
assertEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], log);
}
}
y()();
{
class C {}
class D {
......@@ -333,3 +377,42 @@ x()();
let obj = new klass;
assertEquals(2, klass.x);
}
{
let x = 'a';
class C {
a;
b = x;
c = 1;
hasOwnProperty() { return 1;}
static [x] = 2;
static b = 3;
static d;
}
assertEquals(2, C.a);
assertEquals(3, C.b);
assertEquals(undefined, C.d);
assertEquals(undefined, C.c);
let c = new C;
assertEquals(undefined, c.a);
assertEquals('a', c.b);
assertEquals(1, c.c);
assertEquals(undefined, c.d);
assertEquals(1, c.hasOwnProperty());
}
{
function t() {
return class {
['x'] = 1;
static ['x'] = 2;
}
}
let klass = t();
let obj = new klass;
assertEquals(1, obj.x);
assertEquals(2, klass.x);
}
......@@ -47,7 +47,7 @@ FEATURE_FLAGS = {
'regexp-named-groups': '--harmony-regexp-named-captures',
'regexp-unicode-property-escapes': '--harmony-regexp-property',
'Promise.prototype.finally': '--harmony-promise-finally',
'class-fields-public': '--harmony-public-fields',
'class-fields-public': '--harmony-class-fields',
}
SKIPPED_FEATURES = set(['class-fields-private', 'optional-catch-binding'])
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment