Commit 714f5f40 authored by arv@chromium.org's avatar arv@chromium.org

ES6: Implement generator method shorthand

https://people.mozilla.org/~jorendorff/es6-draft.html#sec-method-definitions

BUG=v8:3516
LOG=Y
R=dslomov@chromium.org

Review URL: https://codereview.chromium.org/577973002

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@24048 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent a479b2cb
......@@ -761,17 +761,17 @@ enum FunctionKind {
kNormalFunction = 0,
kArrowFunction = 1,
kGeneratorFunction = 2,
kConciseMethod = 4
kConciseMethod = 4,
kConciseGeneratorMethod = kGeneratorFunction | kConciseMethod
};
inline bool IsValidFunctionKind(FunctionKind kind) {
// At the moment these are mutually exclusive but in the future that wont be
// the case since ES6 allows concise generator methods.
return kind == FunctionKind::kNormalFunction ||
kind == FunctionKind::kArrowFunction ||
kind == FunctionKind::kGeneratorFunction ||
kind == FunctionKind::kConciseMethod;
kind == FunctionKind::kConciseMethod ||
kind == FunctionKind::kConciseGeneratorMethod;
}
......
......@@ -1936,11 +1936,12 @@ template <class Traits>
typename ParserBase<Traits>::ObjectLiteralPropertyT ParserBase<
Traits>::ParsePropertyDefinition(ObjectLiteralChecker* checker,
bool in_class, bool is_static, bool* ok) {
// TODO(arv): Add support for concise generator methods.
ExpressionT value = this->EmptyExpression();
bool is_get = false;
bool is_set = false;
bool name_is_static = false;
bool is_generator = allow_harmony_object_literals_ && Check(Token::MUL);
Token::Value name_token = peek();
int next_pos = peek_position();
IdentifierT name =
......@@ -1949,7 +1950,7 @@ typename ParserBase<Traits>::ObjectLiteralPropertyT ParserBase<
if (fni_ != NULL) this->PushLiteralName(fni_, name);
if (!in_class && peek() == Token::COLON) {
if (!in_class && !is_generator && peek() == Token::COLON) {
// PropertyDefinition : PropertyName ':' AssignmentExpression
checker->CheckProperty(name_token, kValueProperty,
CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
......@@ -1957,7 +1958,8 @@ typename ParserBase<Traits>::ObjectLiteralPropertyT ParserBase<
value = this->ParseAssignmentExpression(
true, CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
} else if (allow_harmony_object_literals_ && peek() == Token::LPAREN) {
} else if (is_generator ||
(allow_harmony_object_literals_ && peek() == Token::LPAREN)) {
// Concise Method
if (is_static && this->IsPrototype(name)) {
......@@ -1965,14 +1967,22 @@ typename ParserBase<Traits>::ObjectLiteralPropertyT ParserBase<
*ok = false;
return this->EmptyObjectLiteralProperty();
}
if (is_generator && in_class && !is_static && this->IsConstructor(name)) {
ReportMessageAt(scanner()->location(), "constructor_special_method");
*ok = false;
return this->EmptyObjectLiteralProperty();
}
checker->CheckProperty(name_token, kValueProperty,
CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
FunctionKind kind = is_generator ? FunctionKind::kConciseGeneratorMethod
: FunctionKind::kConciseMethod;
value = this->ParseFunctionLiteral(
name, scanner()->location(),
false, // reserved words are allowed here
FunctionKind::kConciseMethod, RelocInfo::kNoPosition,
FunctionLiteral::ANONYMOUS_EXPRESSION, FunctionLiteral::NORMAL_ARITY,
kind, RelocInfo::kNoPosition, FunctionLiteral::ANONYMOUS_EXPRESSION,
FunctionLiteral::NORMAL_ARITY,
CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
} else if (in_class && name_is_static && !is_static) {
......
......@@ -1758,11 +1758,10 @@ function FunctionSourceString(func) {
? 'anonymous'
: %FunctionGetName(func);
// TODO(arv): Handle concise generator methods.
var isGenerator = %FunctionIsGenerator(func);
var head = %FunctionIsConciseMethod(func)
? ''
: %FunctionIsGenerator(func) ? 'function* ' : 'function ';
? (isGenerator ? '*' : '')
: (isGenerator ? 'function* ' : 'function ');
return head + name + source;
}
......
......@@ -3411,6 +3411,8 @@ TEST(ErrorsSuper) {
TEST(NoErrorsMethodDefinition) {
const char* context_data[][2] = {{"({", "});"},
{"'use strict'; ({", "});"},
{"({*", "});"},
{"'use strict'; ({*", "});"},
{NULL, NULL}};
const char* object_literal_body_data[] = {
......@@ -3431,6 +3433,8 @@ TEST(NoErrorsMethodDefinition) {
TEST(MethodDefinitionNames) {
const char* context_data[][2] = {{"({", "(x, y) {}});"},
{"'use strict'; ({", "(x, y) {}});"},
{"({*", "(x, y) {}});"},
{"'use strict'; ({*", "(x, y) {}});"},
{NULL, NULL}};
const char* name_data[] = {
......@@ -3505,6 +3509,8 @@ TEST(MethodDefinitionNames) {
TEST(MethodDefinitionStrictFormalParamereters) {
const char* context_data[][2] = {{"({method(", "){}});"},
{"'use strict'; ({method(", "){}});"},
{"({*method(", "){}});"},
{"'use strict'; ({*method(", "){}});"},
{NULL, NULL}};
const char* params_data[] = {
......@@ -3540,6 +3546,18 @@ TEST(MethodDefinitionDuplicateProperty) {
"x() {}, 'x'() {}",
"0() {}, '0'() {}",
"1.0() {}, 1: 1",
"x: 1, *x() {}",
"*x() {}, x: 1",
"*x() {}, get x() {}",
"*x() {}, set x(_) {}",
"*x() {}, *x() {}",
"*x() {}, y() {}, *x() {}",
"*x() {}, *\"x\"() {}",
"*x() {}, *'x'() {}",
"*0() {}, *'0'() {}",
"*1.0() {}, 1: 1",
NULL
};
......@@ -3549,7 +3567,7 @@ TEST(MethodDefinitionDuplicateProperty) {
}
TEST(NoErrorsClassExpression) {
TEST(ClassExpressionNoErrors) {
const char* context_data[][2] = {{"(", ");"},
{"var C = ", ";"},
{"bar, ", ";"},
......@@ -3573,7 +3591,7 @@ TEST(NoErrorsClassExpression) {
}
TEST(NoErrorsClassDeclaration) {
TEST(ClassDeclarationNoErrors) {
const char* context_data[][2] = {{"", ""},
{"{", "}"},
{"if (true) {", "}"},
......@@ -3592,7 +3610,7 @@ TEST(NoErrorsClassDeclaration) {
}
TEST(NoErrorsClassBody) {
TEST(ClassBodyNoErrors) {
// Tests that parser and preparser accept valid class syntax.
const char* context_data[][2] = {{"(class {", "});"},
{"(class extends Base {", "});"},
......@@ -3604,12 +3622,16 @@ TEST(NoErrorsClassBody) {
";;",
"m() {}",
"m() {};",
";m() {}",
"; m() {}",
"m() {}; n(x) {}",
"get x() {}",
"set x(v) {}",
"get() {}",
"set() {}",
"*g() {}",
"*g() {};",
"; *g() {}",
"*g() {}; *h(x) {}",
"static() {}",
"static m() {}",
"static get x() {}",
......@@ -3619,6 +3641,10 @@ TEST(NoErrorsClassBody) {
"static static() {}",
"static get static() {}",
"static set static(v) {}",
"*static() {}",
"*get() {}",
"*set() {}",
"static *g() {}",
NULL};
static const ParserFlag always_flags[] = {
......@@ -3630,39 +3656,23 @@ TEST(NoErrorsClassBody) {
}
TEST(MethodDefinitionstrictFormalParamereters) {
const char* context_data[][2] = {{"({method(", "){}});"},
{NULL, NULL}};
const char* params_data[] = {
"x, x",
"x, y, x",
"eval",
"arguments",
"var",
"const",
NULL
};
static const ParserFlag always_flags[] = {kAllowHarmonyObjectLiterals};
RunParserSyncTest(context_data, params_data, kError, NULL, 0,
always_flags, arraysize(always_flags));
}
TEST(NoErrorsClassPropertyName) {
TEST(ClassPropertyNameNoErrors) {
const char* context_data[][2] = {{"(class {", "() {}});"},
{"(class { get ", "() {}});"},
{"(class { set ", "(v) {}});"},
{"(class { static ", "() {}});"},
{"(class { static get ", "() {}});"},
{"(class { static set ", "(v) {}});"},
{"(class { *", "() {}});"},
{"(class { static *", "() {}});"},
{"class C {", "() {}}"},
{"class C { get ", "() {}}"},
{"class C { set ", "(v) {}}"},
{"class C { static ", "() {}}"},
{"class C { static get ", "() {}}"},
{"class C { static set ", "(v) {}}"},
{"class C { *", "() {}}"},
{"class C { static *", "() {}}"},
{NULL, NULL}};
const char* name_data[] = {
"42",
......@@ -3704,7 +3714,7 @@ TEST(NoErrorsClassPropertyName) {
}
TEST(ErrorsClassExpression) {
TEST(ClassExpressionErrors) {
const char* context_data[][2] = {{"(", ");"},
{"var C = ", ";"},
{"bar, ", ";"},
......@@ -3735,7 +3745,7 @@ TEST(ErrorsClassExpression) {
}
TEST(ErrorsClassDeclaration) {
TEST(ClassDeclarationErrors) {
const char* context_data[][2] = {{"", ""},
{"{", "}"},
{"if (true) {", "}"},
......@@ -3750,11 +3760,17 @@ TEST(ErrorsClassDeclaration) {
"class name { m; n }",
"class name { m: 1 }",
"class name { m(); n() }",
"class name { get m }",
"class name { get m() }",
"class name { set m() {) }", // missing required param
"class name { get x }",
"class name { get x() }",
"class name { set x() {) }", // missing required param
"class {}", // Name is required for declaration
"class extends base {}",
"class name { *",
"class name { * }",
"class name { *; }",
"class name { *get x() {} }",
"class name { *set x(_) {} }",
"class name { *static m() {} }",
NULL};
static const ParserFlag always_flags[] = {
......@@ -3766,7 +3782,7 @@ TEST(ErrorsClassDeclaration) {
}
TEST(ErrorsClassName) {
TEST(ClassNameErrors) {
const char* context_data[][2] = {{"class ", "{}"},
{"(class ", "{});"},
{"'use strict'; class ", "{}"},
......@@ -3796,7 +3812,7 @@ TEST(ErrorsClassName) {
}
TEST(ErrorsClassGetterParamName) {
TEST(ClassGetterParamNameErrors) {
const char* context_data[][2] = {
{"class C { get name(", ") {} }"},
{"(class { get name(", ") {} });"},
......@@ -3829,7 +3845,7 @@ TEST(ErrorsClassGetterParamName) {
}
TEST(ErrorsClassStaticPrototype) {
TEST(ClassStaticPrototypeErrors) {
const char* context_data[][2] = {{"class C {", "}"},
{"(class {", "});"},
{NULL, NULL}};
......@@ -3838,6 +3854,7 @@ TEST(ErrorsClassStaticPrototype) {
"static prototype() {}",
"static get prototype() {}",
"static set prototype(_) {}",
"static *prototype() {}",
NULL};
static const ParserFlag always_flags[] = {
......@@ -3849,7 +3866,7 @@ TEST(ErrorsClassStaticPrototype) {
}
TEST(ErrorsClassSpecialConstructor) {
TEST(ClassSpecialConstructorErrors) {
const char* context_data[][2] = {{"class C {", "}"},
{"(class {", "});"},
{NULL, NULL}};
......@@ -3857,6 +3874,7 @@ TEST(ErrorsClassSpecialConstructor) {
const char* class_body_data[] = {
"get constructor() {}",
"get constructor(_) {}",
"*constructor() {}",
NULL};
static const ParserFlag always_flags[] = {
......@@ -3868,7 +3886,7 @@ TEST(ErrorsClassSpecialConstructor) {
}
TEST(NoErrorsClassConstructor) {
TEST(ClassConstructorNoErrors) {
const char* context_data[][2] = {{"class C {", "}"},
{"(class {", "});"},
{NULL, NULL}};
......@@ -3878,6 +3896,7 @@ TEST(NoErrorsClassConstructor) {
"static constructor() {}",
"static get constructor() {}",
"static set constructor(_) {}",
"static *constructor() {}",
NULL};
static const ParserFlag always_flags[] = {
......@@ -3889,7 +3908,7 @@ TEST(NoErrorsClassConstructor) {
}
TEST(ErrorsClassMultipleConstructor) {
TEST(ClassMultipleConstructorErrors) {
// We currently do not allow any duplicate properties in class bodies. This
// test ensures that when we change that we still throw on duplicate
// constructors.
......@@ -3912,7 +3931,7 @@ TEST(ErrorsClassMultipleConstructor) {
// TODO(arv): We should allow duplicate property names.
// https://code.google.com/p/v8/issues/detail?id=3570
DISABLED_TEST(NoErrorsClassMultiplePropertyNames) {
DISABLED_TEST(ClassMultiplePropertyNamesNoErrors) {
const char* context_data[][2] = {{"class C {", "}"},
{"(class {", "});"},
{NULL, NULL}};
......@@ -3932,7 +3951,7 @@ DISABLED_TEST(NoErrorsClassMultiplePropertyNames) {
}
TEST(ErrorsClassesAreStrict) {
TEST(ClassesAreStrictErrors) {
const char* context_data[][2] = {{"", ""},
{"(", ");"},
{NULL, NULL}};
......@@ -3940,6 +3959,7 @@ TEST(ErrorsClassesAreStrict) {
const char* class_body_data[] = {
"class C { method() { with ({}) {} } }",
"class C extends function() { with ({}) {} } {}",
"class C { *method() { with ({}) {} } }",
NULL};
static const ParserFlag always_flags[] = {
......
......@@ -5,7 +5,7 @@
// Flags: --harmony-object-literals --allow-natives-syntax
(function TestDescriptor() {
(function TestBasics() {
var object = {
method() {
return 42;
......@@ -15,6 +15,16 @@
})();
(function TestThis() {
var object = {
method() {
assertEquals(object, this);
}
};
object.method();
})();
(function TestDescriptor() {
var object = {
method() {
......@@ -34,9 +44,7 @@
(function TestProto() {
var object = {
method() {
return 42;
}
method() {}
};
assertEquals(Function.prototype, Object.getPrototypeOf(object.method));
......@@ -45,9 +53,7 @@
(function TestNotConstructable() {
var object = {
method() {
return 42;
}
method() {}
};
assertThrows(function() {
......@@ -58,9 +64,7 @@
(function TestFunctionName() {
var object = {
method() {
return 42;
},
method() {},
1() {},
2.0() {}
};
......@@ -68,7 +72,6 @@
assertEquals('method', f.name);
var g = object[1];
assertEquals('1', g.name);
var h = object[2];
assertEquals('2', h.name);
})();
......@@ -90,9 +93,7 @@
(function TestNoPrototype() {
var object = {
method() {
return 42;
}
method() {}
};
var f = object.method;
assertFalse(f.hasOwnProperty('prototype'));
......@@ -121,3 +122,127 @@
assertEquals(42, object.method());
assertFalse(object.method.hasOwnProperty('prototype'));
})();
///////////////////////////////////////////////////////////////////////////////
var GeneratorFunction = function*() {}.__proto__.constructor;
function assertIteratorResult(value, done, result) {
assertEquals({value: value, done: done}, result);
}
(function TestGeneratorBasics() {
var object = {
*method() {
yield 1;
}
};
var g = object.method();
assertIteratorResult(1, false, g.next());
assertIteratorResult(undefined, true, g.next());
})();
(function TestGeneratorThis() {
var object = {
*method() {
yield this;
}
};
var g = object.method();
assertIteratorResult(object, false, g.next());
assertIteratorResult(undefined, true, g.next());
})();
(function TestGeneratorSymbolIterator() {
var object = {
*method() {}
};
var g = object.method();
assertEquals(g, g[Symbol.iterator]());
})();
(function TestGeneratorDescriptor() {
var object = {
*method() {
yield 1;
}
};
var desc = Object.getOwnPropertyDescriptor(object, 'method');
assertTrue(desc.enumerable);
assertTrue(desc.configurable);
assertTrue(desc.writable);
assertEquals('function', typeof desc.value);
var g = desc.value();
assertIteratorResult(1, false, g.next());
assertIteratorResult(undefined, true, g.next());
})();
(function TestGeneratorProto() {
var object = {
*method() {}
};
assertEquals(GeneratorFunction.prototype,
Object.getPrototypeOf(object.method));
})();
(function TestGeneratorConstructable() {
var object = {
*method() {
yield 1;
}
};
var g = new object.method();
assertIteratorResult(1, false, g.next());
assertIteratorResult(undefined, true, g.next());
})();
(function TestGeneratorName() {
var object = {
*method() {},
*1() {},
*2.0() {}
};
var f = object.method;
assertEquals('method', f.name);
var g = object[1];
assertEquals('1', g.name);
var h = object[2];
assertEquals('2', h.name);
})();
(function TestGeneratorNoBinding() {
var method = 'local';
var calls = 0;
var object = {
*method() {
calls++;
assertEquals('local', method);
}
};
var g = object.method();
assertIteratorResult(undefined, true, g.next());
assertEquals(1, calls);
})();
(function TestGeneratorToString() {
var object = {
*method() { yield 1; }
};
assertEquals('*method() { yield 1; }', object.method.toString());
})();
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