Commit 8d946b9c authored by arv's avatar arv Committed by Commit bot

[es6] Throw TypeError for computed static prototype property name

The prototype of a class constructor function is read only. When we set
computed property names we were ignoring this and we were overriding the
property.

Since the prototype is the only possible own read only property on the
constructor function object we special case this so we do not have to
check this for every property in the class literal.

BUG=v8:3945
LOG=N
R=mstarzinger@chromium.org, dslomov@chromium.org

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

Cr-Commit-Position: refs/heads/master@{#27106}
parent 02ce4453
......@@ -2583,6 +2583,16 @@ void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) {
}
__ push(scratch);
EmitPropertyKey(property, lit->GetIdForProperty(i));
// The static prototype property is read only. We handle the non computed
// property name case in the parser. Since this is the only case where we
// need to check for an own read only property we special case this so we do
// not need to do this for every property.
if (property->is_static() && property->is_computed_name()) {
__ CallRuntime(Runtime::kThrowIfStaticPrototype, 1);
__ push(r0);
}
VisitForStackValue(value);
EmitSetHomeObjectIfNeeded(value, 2);
......
......@@ -2278,6 +2278,16 @@ void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) {
}
__ Push(scratch);
EmitPropertyKey(property, lit->GetIdForProperty(i));
// The static prototype property is read only. We handle the non computed
// property name case in the parser. Since this is the only case where we
// need to check for an own read only property we special case this so we do
// not need to do this for every property.
if (property->is_static() && property->is_computed_name()) {
__ CallRuntime(Runtime::kThrowIfStaticPrototype, 1);
__ Push(x0);
}
VisitForStackValue(value);
EmitSetHomeObjectIfNeeded(value, 2);
......
......@@ -1404,6 +1404,17 @@ void AstGraphBuilder::VisitClassLiteralContents(ClassLiteral* expr) {
VisitForValue(property->key());
environment()->Push(
BuildToName(environment()->Pop(), expr->GetIdForProperty(i)));
// The static prototype property is read only. We handle the non computed
// property name case in the parser. Since this is the only case where we
// need to check for an own read only property we special case this so we do
// not need to do this for every property.
if (property->is_static() && property->is_computed_name()) {
Node* name = environment()->Pop();
environment()->Push(
BuildThrowIfStaticPrototype(name, expr->GetIdForProperty(i)));
}
VisitForValue(property->value());
Node* value = environment()->Pop();
Node* key = environment()->Pop();
......@@ -2584,6 +2595,28 @@ Node* AstGraphBuilder::BuildHoleCheckThrow(Node* value, Variable* variable,
}
Node* AstGraphBuilder::BuildThrowIfStaticPrototype(Node* name,
BailoutId bailout_id) {
IfBuilder prototype_check(this);
Node* prototype_string =
jsgraph()->Constant(isolate()->factory()->prototype_string());
Node* check = NewNode(javascript()->StrictEqual(), name, prototype_string);
prototype_check.If(check);
prototype_check.Then();
{
const Operator* op =
javascript()->CallRuntime(Runtime::kThrowStaticPrototypeError, 0);
Node* call = NewNode(op);
PrepareFrameState(call, bailout_id);
environment()->Push(call);
}
prototype_check.Else();
environment()->Push(name);
prototype_check.End();
return environment()->Pop();
}
Node* AstGraphBuilder::BuildVariableLoad(Variable* variable,
BailoutId bailout_id,
const VectorSlotPair& feedback,
......
......@@ -253,6 +253,9 @@ class AstGraphBuilder : public AstVisitor {
Node* BuildHoleCheckThrow(Node* value, Variable* var, Node* not_hole,
BailoutId bailout_id);
// Builders for conditional errors.
Node* BuildThrowIfStaticPrototype(Node* name, BailoutId bailout_id);
// Builders for non-local control flow.
Node* BuildReturn(Node* return_value);
Node* BuildThrow(Node* exception_value);
......
......@@ -2495,6 +2495,16 @@ void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) {
__ push(Operand(esp, 0)); // prototype
}
EmitPropertyKey(property, lit->GetIdForProperty(i));
// The static prototype property is read only. We handle the non computed
// property name case in the parser. Since this is the only case where we
// need to check for an own read only property we special case this so we do
// not need to do this for every property.
if (property->is_static() && property->is_computed_name()) {
__ CallRuntime(Runtime::kThrowIfStaticPrototype, 1);
__ push(eax);
}
VisitForStackValue(value);
EmitSetHomeObjectIfNeeded(value, 2);
......
......@@ -56,6 +56,30 @@ RUNTIME_FUNCTION(Runtime_ThrowArrayNotSubclassableError) {
}
static Object* ThrowStaticPrototypeError(Isolate* isolate) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError("static_prototype", HandleVector<Object>(NULL, 0)));
}
RUNTIME_FUNCTION(Runtime_ThrowStaticPrototypeError) {
HandleScope scope(isolate);
DCHECK(args.length() == 0);
return ThrowStaticPrototypeError(isolate);
}
RUNTIME_FUNCTION(Runtime_ThrowIfStaticPrototype) {
HandleScope scope(isolate);
DCHECK(args.length() == 1);
CONVERT_ARG_HANDLE_CHECKED(Name, name, 0);
if (Name::Equals(name, isolate->factory()->prototype_string())) {
return ThrowStaticPrototypeError(isolate);
}
return *name;
}
RUNTIME_FUNCTION(Runtime_ToMethod) {
HandleScope scope(isolate);
DCHECK(args.length() == 2);
......
......@@ -184,22 +184,24 @@ namespace internal {
F(IsValidSmi, 1, 1) \
\
/* Classes support */ \
F(ToMethod, 2, 1) \
F(HomeObjectSymbol, 0, 1) \
F(ClassGetSourceCode, 1, 1) \
F(DefineClass, 6, 1) \
F(DefineClassMethod, 3, 1) \
F(ClassGetSourceCode, 1, 1) \
F(HandleStepInForDerivedConstructors, 1, 1) \
F(HomeObjectSymbol, 0, 1) \
F(LoadFromSuper, 3, 1) \
F(LoadKeyedFromSuper, 3, 1) \
F(ThrowConstructorNonCallableError, 0, 1) \
F(StoreKeyedToSuper_Sloppy, 4, 1) \
F(StoreKeyedToSuper_Strict, 4, 1) \
F(StoreToSuper_Sloppy, 4, 1) \
F(StoreToSuper_Strict, 4, 1) \
F(ThrowArrayNotSubclassableError, 0, 1) \
F(ThrowConstructorNonCallableError, 0, 1) \
F(ThrowIfStaticPrototype, 1, 1) \
F(ThrowNonMethodError, 0, 1) \
F(ThrowStaticPrototypeError, 0, 1) \
F(ThrowUnsupportedSuperError, 0, 1) \
F(HandleStepInForDerivedConstructors, 1, 1) \
F(StoreToSuper_Strict, 4, 1) \
F(StoreToSuper_Sloppy, 4, 1) \
F(StoreKeyedToSuper_Strict, 4, 1) \
F(StoreKeyedToSuper_Sloppy, 4, 1)
F(ToMethod, 2, 1)
#define RUNTIME_FUNCTION_LIST_ALWAYS_2(F) \
......
......@@ -2495,6 +2495,16 @@ void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) {
__ Push(Operand(rsp, 0)); // prototype
}
EmitPropertyKey(property, lit->GetIdForProperty(i));
// The static prototype property is read only. We handle the non computed
// property name case in the parser. Since this is the only case where we
// need to check for an own read only property we special case this so we do
// not need to do this for every property.
if (property->is_static() && property->is_computed_name()) {
__ CallRuntime(Runtime::kThrowIfStaticPrototype, 1);
__ Push(rax);
}
VisitForStackValue(value);
EmitSetHomeObjectIfNeeded(value, 2);
......
......@@ -312,41 +312,74 @@ function assertIteratorResult(value, done, result) {
(function TestPrototype() {
// Normally a static prototype property is not allowed.
class C {
static ['prototype']() {
return 1;
assertThrows(function() {
class C {
static ['prototype']() {
return 1;
}
}
}
assertEquals(1, C.prototype());
}, TypeError);
class C2 {
static get ['prototype']() {
return 2;
assertThrows(function() {
class C2 {
static get ['prototype']() {
return 2;
}
}
}
assertEquals(2, C2.prototype);
}, TypeError);
var calls = 0;
class C3 {
static set ['prototype'](x) {
assertEquals(3, x);
calls++;
assertThrows(function() {
class C3 {
static set ['prototype'](x) {
assertEquals(3, x);
}
}
}
C3.prototype = 3;
assertEquals(1, calls);
}, TypeError);
class C4 {
static *['prototype']() {
yield 1;
yield 2;
assertThrows(function() {
class C4 {
static *['prototype']() {
yield 1;
yield 2;
}
}
}
var iter = C4.prototype();
assertIteratorResult(1, false, iter.next());
assertIteratorResult(2, false, iter.next());
assertIteratorResult(undefined, true, iter.next());
}, TypeError);
})();
(function TestPrototypeConcat() {
assertThrows(function() {
class C {
static ['pro' + 'tot' + 'ype']() {
return 1;
}
}
}, TypeError);
assertThrows(function() {
class C2 {
static get ['pro' + 'tot' + 'ype']() {
return 2;
}
}
}, TypeError);
assertThrows(function() {
class C3 {
static set ['pro' + 'tot' + 'ype'](x) {
assertEquals(3, x);
}
}
}, TypeError);
assertThrows(function() {
class C4 {
static *['pro' + 'tot' + 'ype']() {
yield 1;
yield 2;
}
}
}, TypeError);
})();
......
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