This uses a runtime function to set up the the constructor and its
prototype. This does not add the methods/accessors to the prototype or the constructor. BUG=v8:3330 LOG=Y R=dslomov@chromium.org Review URL: https://codereview.chromium.org/631433002 git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@24442 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
be69c78e6d
commit
583868288a
@ -813,7 +813,6 @@ void AstGraphBuilder::VisitFunctionLiteral(FunctionLiteral* expr) {
|
||||
|
||||
|
||||
void AstGraphBuilder::VisitClassLiteral(ClassLiteral* expr) {
|
||||
// TODO(arv): Implement.
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
|
@ -145,7 +145,7 @@ class AstGraphBuilderWithPositions : public AstGraphBuilder {
|
||||
}
|
||||
|
||||
#define DEF_VISIT(type) \
|
||||
virtual void Visit##type(type* node) OVERRIDE { \
|
||||
virtual void Visit##type(type* node) OVERRIDE { \
|
||||
SourcePositionTable::Scope pos(source_positions_, \
|
||||
SourcePosition(node->position())); \
|
||||
AstGraphBuilder::Visit##type(node); \
|
||||
@ -173,7 +173,7 @@ Handle<Code> Pipeline::GenerateCode() {
|
||||
info()->function()->dont_optimize_reason() == kForOfStatement ||
|
||||
// TODO(turbofan): Make super work and remove this bailout.
|
||||
info()->function()->dont_optimize_reason() == kSuperReference ||
|
||||
// TODO(turbofan): Make classliterals work and remove this bailout.
|
||||
// TODO(turbofan): Make class literals work and remove this bailout.
|
||||
info()->function()->dont_optimize_reason() == kClassLiteral ||
|
||||
// TODO(turbofan): Make OSR work and remove this bailout.
|
||||
info()->is_osr()) {
|
||||
|
@ -1542,12 +1542,30 @@ void FullCodeGenerator::VisitFunctionLiteral(FunctionLiteral* expr) {
|
||||
|
||||
|
||||
void FullCodeGenerator::VisitClassLiteral(ClassLiteral* expr) {
|
||||
// TODO(arv): Implement
|
||||
Comment cmnt(masm_, "[ ClassLiteral");
|
||||
if (expr->extends() != NULL) {
|
||||
VisitForEffect(expr->extends());
|
||||
|
||||
if (expr->raw_name() != NULL) {
|
||||
__ Push(expr->name());
|
||||
} else {
|
||||
__ Push(isolate()->factory()->undefined_value());
|
||||
}
|
||||
context()->Plug(isolate()->factory()->undefined_value());
|
||||
|
||||
if (expr->extends() != NULL) {
|
||||
VisitForStackValue(expr->extends());
|
||||
} else {
|
||||
__ Push(isolate()->factory()->the_hole_value());
|
||||
}
|
||||
|
||||
if (expr->constructor() != NULL) {
|
||||
VisitForStackValue(expr->constructor());
|
||||
} else {
|
||||
__ Push(isolate()->factory()->undefined_value());
|
||||
}
|
||||
|
||||
// TODO(arv): Process methods
|
||||
|
||||
__ CallRuntime(Runtime::kDefineClass, 3);
|
||||
context()->Plug(result_register());
|
||||
}
|
||||
|
||||
|
||||
|
@ -174,7 +174,9 @@ var kMessages = {
|
||||
invalid_module_path: ["Module does not export '", "%0", "', or export is not itself a module"],
|
||||
module_type_error: ["Module '", "%0", "' used improperly"],
|
||||
module_export_undefined: ["Export '", "%0", "' is not defined in module"],
|
||||
unexpected_super: ["'super' keyword unexpected here"]
|
||||
unexpected_super: ["'super' keyword unexpected here"],
|
||||
extends_value_not_a_function: ["Class extends value ", "%0", " is not a function or null"],
|
||||
prototype_parent_not_an_object: ["Class extends value does not have valid prototype property ", "%0"]
|
||||
};
|
||||
|
||||
|
||||
|
@ -646,7 +646,7 @@ Expression* ParserTraits::SuperReference(
|
||||
pos);
|
||||
}
|
||||
|
||||
Expression* ParserTraits::ClassLiteral(
|
||||
Expression* ParserTraits::ClassExpression(
|
||||
const AstRawString* name, Expression* extends, Expression* constructor,
|
||||
ZoneList<ObjectLiteral::Property*>* properties, int pos,
|
||||
AstNodeFactory<AstConstructionVisitor>* factory) {
|
||||
@ -1956,21 +1956,18 @@ Statement* Parser::ParseClassDeclaration(ZoneList<const AstRawString*>* names,
|
||||
Expression* value = ParseClassLiteral(name, scanner()->location(),
|
||||
is_strict_reserved, pos, CHECK_OK);
|
||||
|
||||
Block* block = factory()->NewBlock(NULL, 1, true, pos);
|
||||
VariableMode mode = LET;
|
||||
VariableProxy* proxy = NewUnresolved(name, mode, Interface::NewValue());
|
||||
VariableProxy* proxy = NewUnresolved(name, LET, Interface::NewValue());
|
||||
Declaration* declaration =
|
||||
factory()->NewVariableDeclaration(proxy, mode, scope_, pos);
|
||||
factory()->NewVariableDeclaration(proxy, LET, scope_, pos);
|
||||
Declare(declaration, true, CHECK_OK);
|
||||
proxy->var()->set_initializer_position(pos);
|
||||
|
||||
Token::Value init_op = Token::INIT_LET;
|
||||
Assignment* assignment = factory()->NewAssignment(init_op, proxy, value, pos);
|
||||
block->AddStatement(
|
||||
factory()->NewExpressionStatement(assignment, RelocInfo::kNoPosition),
|
||||
zone());
|
||||
|
||||
Statement* assignment_statement =
|
||||
factory()->NewExpressionStatement(assignment, RelocInfo::kNoPosition);
|
||||
if (names) names->Add(name, zone());
|
||||
return block;
|
||||
return assignment_statement;
|
||||
}
|
||||
|
||||
|
||||
|
19
src/parser.h
19
src/parser.h
@ -417,6 +417,15 @@ class ParserTraits {
|
||||
return string->AsArrayIndex(index);
|
||||
}
|
||||
|
||||
bool IsConstructorProperty(ObjectLiteral::Property* property) {
|
||||
return property->key()->raw_value()->EqualsString(
|
||||
ast_value_factory()->constructor_string());
|
||||
}
|
||||
|
||||
static Expression* GetPropertyValue(ObjectLiteral::Property* property) {
|
||||
return property->value();
|
||||
}
|
||||
|
||||
// Functions for encapsulating the differences between parsing and preparsing;
|
||||
// operations interleaved with the recursive descent.
|
||||
static void PushLiteralName(FuncNameInferrer* fni, const AstRawString* id) {
|
||||
@ -546,11 +555,11 @@ class ParserTraits {
|
||||
Expression* SuperReference(Scope* scope,
|
||||
AstNodeFactory<AstConstructionVisitor>* factory,
|
||||
int pos = RelocInfo::kNoPosition);
|
||||
Expression* ClassLiteral(const AstRawString* name, Expression* extends,
|
||||
Expression* constructor,
|
||||
ZoneList<ObjectLiteral::Property*>* properties,
|
||||
int pos,
|
||||
AstNodeFactory<AstConstructionVisitor>* factory);
|
||||
Expression* ClassExpression(const AstRawString* name, Expression* extends,
|
||||
Expression* constructor,
|
||||
ZoneList<ObjectLiteral::Property*>* properties,
|
||||
int pos,
|
||||
AstNodeFactory<AstConstructionVisitor>* factory);
|
||||
|
||||
Literal* ExpressionFromLiteral(
|
||||
Token::Value token, int pos, Scanner* scanner,
|
||||
|
@ -150,6 +150,13 @@ class ParserBase : public Traits {
|
||||
scope_(scope) {
|
||||
*scope_stack_ = scope_;
|
||||
}
|
||||
BlockState(typename Traits::Type::Scope** scope_stack,
|
||||
typename Traits::Type::Scope** scope)
|
||||
: scope_stack_(scope_stack),
|
||||
outer_scope_(*scope_stack),
|
||||
scope_(*scope) {
|
||||
*scope_stack_ = scope_;
|
||||
}
|
||||
~BlockState() { *scope_stack_ = outer_scope_; }
|
||||
|
||||
private:
|
||||
@ -1195,6 +1202,13 @@ class PreParserTraits {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsConstructorProperty(PreParserExpression property) { return false; }
|
||||
|
||||
static PreParserExpression GetPropertyValue(PreParserExpression property) {
|
||||
UNREACHABLE();
|
||||
return PreParserExpression::Default();
|
||||
}
|
||||
|
||||
// Functions for encapsulating the differences between parsing and preparsing;
|
||||
// operations interleaved with the recursive descent.
|
||||
static void PushLiteralName(FuncNameInferrer* fni, PreParserIdentifier id) {
|
||||
@ -1320,12 +1334,12 @@ class PreParserTraits {
|
||||
return PreParserExpression::Super();
|
||||
}
|
||||
|
||||
static PreParserExpression ClassLiteral(PreParserIdentifier name,
|
||||
PreParserExpression extends,
|
||||
PreParserExpression constructor,
|
||||
PreParserExpressionList properties,
|
||||
int position,
|
||||
PreParserFactory* factory) {
|
||||
static PreParserExpression ClassExpression(PreParserIdentifier name,
|
||||
PreParserExpression extends,
|
||||
PreParserExpression constructor,
|
||||
PreParserExpressionList properties,
|
||||
int position,
|
||||
PreParserFactory* factory) {
|
||||
return PreParserExpression::Default();
|
||||
}
|
||||
|
||||
@ -1978,16 +1992,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();
|
||||
|
||||
FunctionKind kind = is_generator ? FunctionKind::kConciseGeneratorMethod
|
||||
: FunctionKind::kConciseMethod;
|
||||
|
||||
if (in_class && !is_static && this->IsConstructor(name)) {
|
||||
if (is_generator) {
|
||||
ReportMessageAt(scanner()->location(), "constructor_special_method");
|
||||
*ok = false;
|
||||
return this->EmptyObjectLiteralProperty();
|
||||
}
|
||||
|
||||
kind = FunctionKind::kNormalFunction;
|
||||
}
|
||||
|
||||
checker->CheckProperty(name_token, kValueProperty,
|
||||
CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
|
||||
FunctionKind kind = is_generator ? FunctionKind::kConciseGeneratorMethod
|
||||
: FunctionKind::kConciseMethod;
|
||||
|
||||
value = this->ParseFunctionLiteral(
|
||||
name, scanner()->location(),
|
||||
@ -2745,25 +2765,24 @@ typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseClassLiteral(
|
||||
return this->EmptyExpression();
|
||||
}
|
||||
|
||||
// TODO(arv): Implement scopes and name binding in class body only.
|
||||
// TODO(arv): Maybe add CLASS_SCOPE?
|
||||
typename Traits::Type::ScopePtr extends_scope =
|
||||
this->NewScope(scope_, BLOCK_SCOPE);
|
||||
FunctionState extends_function_state(
|
||||
&function_state_, &scope_, &extends_scope, zone(),
|
||||
this->ast_value_factory(), ast_node_id_gen_);
|
||||
scope_->SetStrictMode(STRICT);
|
||||
scope_->SetScopeName(name);
|
||||
|
||||
ExpressionT extends = this->EmptyExpression();
|
||||
if (Check(Token::EXTENDS)) {
|
||||
typename Traits::Type::ScopePtr scope = this->NewScope(scope_, BLOCK_SCOPE);
|
||||
BlockState block_state(&scope_, &scope);
|
||||
scope_->SetStrictMode(STRICT);
|
||||
extends = this->ParseLeftHandSideExpression(CHECK_OK);
|
||||
}
|
||||
|
||||
// TODO(arv): Implement scopes and name binding in class body only.
|
||||
typename Traits::Type::ScopePtr scope = this->NewScope(scope_, BLOCK_SCOPE);
|
||||
BlockState block_state(&scope_, &scope);
|
||||
scope_->SetStrictMode(STRICT);
|
||||
scope_->SetScopeName(name);
|
||||
|
||||
ObjectLiteralChecker checker(this, STRICT);
|
||||
typename Traits::Type::PropertyList properties =
|
||||
this->NewPropertyList(4, zone_);
|
||||
FunctionLiteralT constructor = this->EmptyFunctionLiteral();
|
||||
ExpressionT constructor = this->EmptyExpression();
|
||||
|
||||
Expect(Token::LBRACE, CHECK_OK);
|
||||
while (peek() != Token::RBRACE) {
|
||||
@ -2775,7 +2794,11 @@ typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseClassLiteral(
|
||||
ObjectLiteralPropertyT property =
|
||||
this->ParsePropertyDefinition(&checker, in_class, is_static, CHECK_OK);
|
||||
|
||||
properties->Add(property, zone());
|
||||
if (this->IsConstructorProperty(property)) {
|
||||
constructor = this->GetPropertyValue(property);
|
||||
} else {
|
||||
properties->Add(property, zone());
|
||||
}
|
||||
|
||||
if (fni_ != NULL) {
|
||||
fni_->Infer();
|
||||
@ -2784,8 +2807,8 @@ typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseClassLiteral(
|
||||
}
|
||||
Expect(Token::RBRACE, CHECK_OK);
|
||||
|
||||
return this->ClassLiteral(name, extends, constructor, properties, pos,
|
||||
factory());
|
||||
return this->ClassExpression(name, extends, constructor, properties, pos,
|
||||
factory());
|
||||
}
|
||||
|
||||
|
||||
|
@ -151,5 +151,84 @@ RUNTIME_FUNCTION(Runtime_StoreToSuper_Sloppy) {
|
||||
|
||||
return StoreToSuper(isolate, home_object, receiver, name, value, SLOPPY);
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_DefineClass) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 3);
|
||||
CONVERT_ARG_HANDLE_CHECKED(Object, name, 0);
|
||||
CONVERT_ARG_HANDLE_CHECKED(Object, super_class, 1);
|
||||
CONVERT_ARG_HANDLE_CHECKED(Object, constructor, 2);
|
||||
|
||||
Handle<Object> prototype_parent;
|
||||
Handle<Object> constructor_parent;
|
||||
|
||||
if (super_class->IsTheHole()) {
|
||||
prototype_parent = isolate->initial_object_prototype();
|
||||
} else {
|
||||
if (super_class->IsNull()) {
|
||||
prototype_parent = isolate->factory()->null_value();
|
||||
} else if (super_class->IsSpecFunction()) {
|
||||
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
|
||||
isolate, prototype_parent,
|
||||
Runtime::GetObjectProperty(isolate, super_class,
|
||||
isolate->factory()->prototype_string()));
|
||||
if (!prototype_parent->IsNull() && !prototype_parent->IsSpecObject()) {
|
||||
Handle<Object> args[1] = {prototype_parent};
|
||||
THROW_NEW_ERROR_RETURN_FAILURE(
|
||||
isolate, NewTypeError("prototype_parent_not_an_object",
|
||||
HandleVector(args, 1)));
|
||||
}
|
||||
constructor_parent = super_class;
|
||||
} else {
|
||||
// TODO(arv): Should be IsConstructor.
|
||||
Handle<Object> args[1] = {super_class};
|
||||
THROW_NEW_ERROR_RETURN_FAILURE(
|
||||
isolate,
|
||||
NewTypeError("extends_value_not_a_function", HandleVector(args, 1)));
|
||||
}
|
||||
}
|
||||
|
||||
Handle<Map> map =
|
||||
isolate->factory()->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
|
||||
map->set_prototype(*prototype_parent);
|
||||
Handle<JSObject> prototype = isolate->factory()->NewJSObjectFromMap(map);
|
||||
|
||||
Handle<String> name_string = name->IsString()
|
||||
? Handle<String>::cast(name)
|
||||
: isolate->factory()->empty_string();
|
||||
|
||||
Handle<JSFunction> ctor;
|
||||
if (constructor->IsSpecFunction()) {
|
||||
ctor = Handle<JSFunction>::cast(constructor);
|
||||
JSFunction::SetPrototype(ctor, prototype);
|
||||
PropertyAttributes attribs =
|
||||
static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE | READ_ONLY);
|
||||
RETURN_FAILURE_ON_EXCEPTION(
|
||||
isolate,
|
||||
JSObject::SetOwnPropertyIgnoreAttributes(
|
||||
ctor, isolate->factory()->prototype_string(), prototype, attribs));
|
||||
} else {
|
||||
// TODO(arv): This should not use an empty function but a function that
|
||||
// calls super.
|
||||
Handle<Code> code(isolate->builtins()->builtin(Builtins::kEmptyFunction));
|
||||
ctor = isolate->factory()->NewFunction(name_string, code, prototype, true);
|
||||
}
|
||||
|
||||
Handle<Symbol> home_object_symbol(isolate->heap()->home_object_symbol());
|
||||
RETURN_FAILURE_ON_EXCEPTION(
|
||||
isolate, JSObject::SetOwnPropertyIgnoreAttributes(
|
||||
ctor, home_object_symbol, prototype, DONT_ENUM));
|
||||
|
||||
if (!constructor_parent.is_null()) {
|
||||
RETURN_FAILURE_ON_EXCEPTION(
|
||||
isolate, JSObject::SetPrototype(ctor, constructor_parent, false));
|
||||
}
|
||||
|
||||
JSObject::AddProperty(prototype, isolate->factory()->constructor_string(),
|
||||
ctor, DONT_ENUM);
|
||||
|
||||
return *ctor;
|
||||
}
|
||||
}
|
||||
} // namespace v8::internal
|
||||
|
@ -192,7 +192,8 @@ namespace internal {
|
||||
F(LoadFromSuper, 3, 1) \
|
||||
F(LoadKeyedFromSuper, 3, 1) \
|
||||
F(StoreToSuper_Strict, 4, 1) \
|
||||
F(StoreToSuper_Sloppy, 4, 1)
|
||||
F(StoreToSuper_Sloppy, 4, 1) \
|
||||
F(DefineClass, 3, 1)
|
||||
|
||||
|
||||
#define RUNTIME_FUNCTION_LIST_ALWAYS_2(F) \
|
||||
|
167
test/mjsunit/harmony/classes.js
Normal file
167
test/mjsunit/harmony/classes.js
Normal file
@ -0,0 +1,167 @@
|
||||
// Copyright 2014 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
|
||||
|
||||
(function TestBasics() {
|
||||
var C = class C {}
|
||||
assertEquals(typeof C, 'function');
|
||||
assertEquals(C.__proto__, Function.prototype);
|
||||
assertEquals(Object.prototype, Object.getPrototypeOf(C.prototype));
|
||||
assertEquals(Function.prototype, Object.getPrototypeOf(C));
|
||||
assertEquals('C', C.name);
|
||||
|
||||
class D {}
|
||||
assertEquals(typeof D, 'function');
|
||||
assertEquals(D.__proto__, Function.prototype);
|
||||
assertEquals(Object.prototype, Object.getPrototypeOf(D.prototype));
|
||||
assertEquals(Function.prototype, Object.getPrototypeOf(D));
|
||||
assertEquals('D', D.name);
|
||||
|
||||
var E = class {}
|
||||
assertEquals('', E.name);
|
||||
})();
|
||||
|
||||
|
||||
(function TestBasicsExtends() {
|
||||
class C extends null {}
|
||||
assertEquals(typeof C, 'function');
|
||||
assertEquals(C.__proto__, Function.prototype);
|
||||
assertEquals(null, Object.getPrototypeOf(C.prototype));
|
||||
|
||||
class D extends C {}
|
||||
assertEquals(typeof D, 'function');
|
||||
assertEquals(D.__proto__, C);
|
||||
assertEquals(C.prototype, Object.getPrototypeOf(D.prototype));
|
||||
})();
|
||||
|
||||
|
||||
(function TestSideEffectInExtends() {
|
||||
var calls = 0;
|
||||
class C {}
|
||||
class D extends (calls++, C) {}
|
||||
assertEquals(1, calls);
|
||||
assertEquals(typeof D, 'function');
|
||||
assertEquals(D.__proto__, C);
|
||||
assertEquals(C.prototype, Object.getPrototypeOf(D.prototype));
|
||||
})();
|
||||
|
||||
|
||||
(function TestInvalidExtends() {
|
||||
assertThrows(function() {
|
||||
class C extends 42 {}
|
||||
}, TypeError);
|
||||
|
||||
assertThrows(function() {
|
||||
// Function but its .prototype is not null or a function.
|
||||
class C extends Math.abs {}
|
||||
}, TypeError);
|
||||
|
||||
assertThrows(function() {
|
||||
Math.abs.prototype = 42;
|
||||
class C extends Math.abs {}
|
||||
}, TypeError);
|
||||
delete Math.abs.prototype;
|
||||
})();
|
||||
|
||||
|
||||
(function TestConstructorProperty() {
|
||||
class C {}
|
||||
assertEquals(C, C.prototype.constructor);
|
||||
var descr = Object.getOwnPropertyDescriptor(C.prototype, 'constructor');
|
||||
assertTrue(descr.configurable);
|
||||
assertFalse(descr.enumerable);
|
||||
assertTrue(descr.writable);
|
||||
})();
|
||||
|
||||
|
||||
(function TestPrototypeProperty() {
|
||||
class C {}
|
||||
var descr = Object.getOwnPropertyDescriptor(C, 'prototype');
|
||||
assertFalse(descr.configurable);
|
||||
assertFalse(descr.enumerable);
|
||||
assertFalse(descr.writable);
|
||||
})();
|
||||
|
||||
|
||||
(function TestConstructor() {
|
||||
var count = 0;
|
||||
class C {
|
||||
constructor() {
|
||||
assertEquals(Object.getPrototypeOf(this), C.prototype);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
assertEquals(C, C.prototype.constructor);
|
||||
var descr = Object.getOwnPropertyDescriptor(C.prototype, 'constructor');
|
||||
assertTrue(descr.configurable);
|
||||
assertFalse(descr.enumerable);
|
||||
assertTrue(descr.writable);
|
||||
|
||||
var c = new C();
|
||||
assertEquals(1, count);
|
||||
assertEquals(Object.getPrototypeOf(c), C.prototype);
|
||||
})();
|
||||
|
||||
|
||||
(function TestImplicitConstructor() {
|
||||
class C {}
|
||||
var c = new C();
|
||||
assertEquals(Object.getPrototypeOf(c), C.prototype);
|
||||
})();
|
||||
|
||||
|
||||
(function TestConstructorStrict() {
|
||||
class C {
|
||||
constructor() {
|
||||
assertThrows(function() {
|
||||
nonExistingBinding = 42;
|
||||
}, ReferenceError);
|
||||
}
|
||||
}
|
||||
new C();
|
||||
})();
|
||||
|
||||
|
||||
(function TestSuperInConstructor() {
|
||||
var calls = 0;
|
||||
class B {}
|
||||
B.prototype.x = 42;
|
||||
|
||||
class C extends B {
|
||||
constructor() {
|
||||
calls++;
|
||||
assertEquals(42, super.x);
|
||||
}
|
||||
}
|
||||
|
||||
new C;
|
||||
assertEquals(1, calls);
|
||||
})();
|
||||
|
||||
|
||||
(function TestStrictMode() {
|
||||
class C {}
|
||||
|
||||
with ({a: 1}) {
|
||||
assertEquals(1, a);
|
||||
}
|
||||
|
||||
assertThrows('class C extends function B() { with ({}); return B; }() {}',
|
||||
SyntaxError);
|
||||
|
||||
})();
|
||||
|
||||
/* TODO(arv): Implement
|
||||
(function TestNameBindingInConstructor() {
|
||||
class C {
|
||||
constructor() {
|
||||
assertThrows(function() {
|
||||
C = 42;
|
||||
}, ReferenceError);
|
||||
}
|
||||
}
|
||||
new C();
|
||||
})();
|
||||
*/
|
Loading…
Reference in New Issue
Block a user