This CL simplifies var / const by ensuring the behavior is consistent in itself, and with regular JS semantics; between regular var/const and eval-ed var/const.
Legacy const is changed so that a declaration declares a configurable, but non-writable, slot, and the initializer reconfigures it (when possible) to non-configurable non-writable. This avoids the need for "the hole" as marker value in JSContextExtensionObjects and GlobalObjects. Undefined is used instead. BUG= R=rossberg@chromium.org Review URL: https://codereview.chromium.org/379893002 git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@22379 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
942fe1914f
commit
aa7198dfdd
@ -855,7 +855,7 @@ void FullCodeGenerator::VisitVariableDeclaration(
|
||||
__ mov(r0, Operand(Smi::FromInt(0))); // Indicates no initial value.
|
||||
__ Push(cp, r2, r1, r0);
|
||||
}
|
||||
__ CallRuntime(Runtime::kDeclareContextSlot, 4);
|
||||
__ CallRuntime(Runtime::kDeclareLookupSlot, 4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -911,7 +911,7 @@ void FullCodeGenerator::VisitFunctionDeclaration(
|
||||
__ Push(cp, r2, r1);
|
||||
// Push initial value for function declaration.
|
||||
VisitForStackValue(declaration->fun());
|
||||
__ CallRuntime(Runtime::kDeclareContextSlot, 4);
|
||||
__ CallRuntime(Runtime::kDeclareLookupSlot, 4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -2458,16 +2458,6 @@ void FullCodeGenerator::EmitStoreToStackLocalOrContextSlot(
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::EmitCallStoreContextSlot(
|
||||
Handle<String> name, StrictMode strict_mode) {
|
||||
__ push(r0); // Value.
|
||||
__ mov(r1, Operand(name));
|
||||
__ mov(r0, Operand(Smi::FromInt(strict_mode)));
|
||||
__ Push(cp, r1, r0); // Context, name, strict mode.
|
||||
__ CallRuntime(Runtime::kStoreContextSlot, 4);
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::EmitVariableAssignment(Variable* var, Token::Value op) {
|
||||
if (var->IsUnallocated()) {
|
||||
// Global var, const, or let.
|
||||
@ -2482,7 +2472,7 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, Token::Value op) {
|
||||
__ push(r0);
|
||||
__ mov(r0, Operand(var->name()));
|
||||
__ Push(cp, r0); // Context and name.
|
||||
__ CallRuntime(Runtime::kInitializeConstContextSlot, 3);
|
||||
__ CallRuntime(Runtime::kInitializeLegacyConstLookupSlot, 3);
|
||||
} else {
|
||||
ASSERT(var->IsStackAllocated() || var->IsContextSlot());
|
||||
Label skip;
|
||||
@ -2496,29 +2486,33 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, Token::Value op) {
|
||||
|
||||
} else if (var->mode() == LET && op != Token::INIT_LET) {
|
||||
// Non-initializing assignment to let variable needs a write barrier.
|
||||
if (var->IsLookupSlot()) {
|
||||
EmitCallStoreContextSlot(var->name(), strict_mode());
|
||||
} else {
|
||||
ASSERT(var->IsStackAllocated() || var->IsContextSlot());
|
||||
Label assign;
|
||||
MemOperand location = VarOperand(var, r1);
|
||||
__ ldr(r3, location);
|
||||
__ CompareRoot(r3, Heap::kTheHoleValueRootIndex);
|
||||
__ b(ne, &assign);
|
||||
__ mov(r3, Operand(var->name()));
|
||||
__ push(r3);
|
||||
__ CallRuntime(Runtime::kThrowReferenceError, 1);
|
||||
// Perform the assignment.
|
||||
__ bind(&assign);
|
||||
EmitStoreToStackLocalOrContextSlot(var, location);
|
||||
}
|
||||
ASSERT(!var->IsLookupSlot());
|
||||
ASSERT(var->IsStackAllocated() || var->IsContextSlot());
|
||||
Label assign;
|
||||
MemOperand location = VarOperand(var, r1);
|
||||
__ ldr(r3, location);
|
||||
__ CompareRoot(r3, Heap::kTheHoleValueRootIndex);
|
||||
__ b(ne, &assign);
|
||||
__ mov(r3, Operand(var->name()));
|
||||
__ push(r3);
|
||||
__ CallRuntime(Runtime::kThrowReferenceError, 1);
|
||||
// Perform the assignment.
|
||||
__ bind(&assign);
|
||||
EmitStoreToStackLocalOrContextSlot(var, location);
|
||||
|
||||
} else if (!var->is_const_mode() || op == Token::INIT_CONST) {
|
||||
// Assignment to var or initializing assignment to let/const
|
||||
// in harmony mode.
|
||||
if (var->IsLookupSlot()) {
|
||||
EmitCallStoreContextSlot(var->name(), strict_mode());
|
||||
ASSERT(op == Token::ASSIGN || op == Token::INIT_VAR ||
|
||||
op == Token::ASSIGN_ADD);
|
||||
// Assignment to var.
|
||||
__ push(r0); // Value.
|
||||
__ mov(r1, Operand(var->name()));
|
||||
__ mov(r0, Operand(Smi::FromInt(strict_mode())));
|
||||
__ Push(cp, r1, r0); // Context, name, strict mode.
|
||||
__ CallRuntime(Runtime::kStoreLookupSlot, 4);
|
||||
} else {
|
||||
// Assignment to var or initializing assignment to let/const in harmony
|
||||
// mode.
|
||||
ASSERT((var->IsStackAllocated() || var->IsContextSlot()));
|
||||
MemOperand location = VarOperand(var, r1);
|
||||
if (generate_debug_code_ && op == Token::INIT_LET) {
|
||||
|
@ -859,7 +859,7 @@ void FullCodeGenerator::VisitVariableDeclaration(
|
||||
// Pushing 0 (xzr) indicates no initial value.
|
||||
__ Push(cp, x2, x1, xzr);
|
||||
}
|
||||
__ CallRuntime(Runtime::kDeclareContextSlot, 4);
|
||||
__ CallRuntime(Runtime::kDeclareLookupSlot, 4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -915,7 +915,7 @@ void FullCodeGenerator::VisitFunctionDeclaration(
|
||||
__ Push(cp, x2, x1);
|
||||
// Push initial value for function declaration.
|
||||
VisitForStackValue(declaration->fun());
|
||||
__ CallRuntime(Runtime::kDeclareContextSlot, 4);
|
||||
__ CallRuntime(Runtime::kDeclareLookupSlot, 4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -2142,19 +2142,6 @@ void FullCodeGenerator::EmitStoreToStackLocalOrContextSlot(
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::EmitCallStoreContextSlot(
|
||||
Handle<String> name, StrictMode strict_mode) {
|
||||
__ Mov(x11, Operand(name));
|
||||
__ Mov(x10, Smi::FromInt(strict_mode));
|
||||
// jssp[0] : mode.
|
||||
// jssp[8] : name.
|
||||
// jssp[16] : context.
|
||||
// jssp[24] : value.
|
||||
__ Push(x0, cp, x11, x10);
|
||||
__ CallRuntime(Runtime::kStoreContextSlot, 4);
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::EmitVariableAssignment(Variable* var,
|
||||
Token::Value op) {
|
||||
ASM_LOCATION("FullCodeGenerator::EmitVariableAssignment");
|
||||
@ -2170,7 +2157,7 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var,
|
||||
if (var->IsLookupSlot()) {
|
||||
__ Mov(x1, Operand(var->name()));
|
||||
__ Push(x0, cp, x1);
|
||||
__ CallRuntime(Runtime::kInitializeConstContextSlot, 3);
|
||||
__ CallRuntime(Runtime::kInitializeLegacyConstLookupSlot, 3);
|
||||
} else {
|
||||
ASSERT(var->IsStackLocal() || var->IsContextSlot());
|
||||
Label skip;
|
||||
@ -2183,28 +2170,35 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var,
|
||||
|
||||
} else if (var->mode() == LET && op != Token::INIT_LET) {
|
||||
// Non-initializing assignment to let variable needs a write barrier.
|
||||
if (var->IsLookupSlot()) {
|
||||
EmitCallStoreContextSlot(var->name(), strict_mode());
|
||||
} else {
|
||||
ASSERT(var->IsStackAllocated() || var->IsContextSlot());
|
||||
Label assign;
|
||||
MemOperand location = VarOperand(var, x1);
|
||||
__ Ldr(x10, location);
|
||||
__ JumpIfNotRoot(x10, Heap::kTheHoleValueRootIndex, &assign);
|
||||
__ Mov(x10, Operand(var->name()));
|
||||
__ Push(x10);
|
||||
__ CallRuntime(Runtime::kThrowReferenceError, 1);
|
||||
// Perform the assignment.
|
||||
__ Bind(&assign);
|
||||
EmitStoreToStackLocalOrContextSlot(var, location);
|
||||
}
|
||||
ASSERT(!var->IsLookupSlot());
|
||||
ASSERT(var->IsStackAllocated() || var->IsContextSlot());
|
||||
Label assign;
|
||||
MemOperand location = VarOperand(var, x1);
|
||||
__ Ldr(x10, location);
|
||||
__ JumpIfNotRoot(x10, Heap::kTheHoleValueRootIndex, &assign);
|
||||
__ Mov(x10, Operand(var->name()));
|
||||
__ Push(x10);
|
||||
__ CallRuntime(Runtime::kThrowReferenceError, 1);
|
||||
// Perform the assignment.
|
||||
__ Bind(&assign);
|
||||
EmitStoreToStackLocalOrContextSlot(var, location);
|
||||
|
||||
} else if (!var->is_const_mode() || op == Token::INIT_CONST) {
|
||||
// Assignment to var or initializing assignment to let/const
|
||||
// in harmony mode.
|
||||
if (var->IsLookupSlot()) {
|
||||
EmitCallStoreContextSlot(var->name(), strict_mode());
|
||||
ASSERT(op == Token::ASSIGN || op == Token::INIT_VAR ||
|
||||
op == Token::ASSIGN_ADD);
|
||||
// Assignment to var.
|
||||
__ Mov(x11, Operand(var->name()));
|
||||
__ Mov(x10, Smi::FromInt(strict_mode()));
|
||||
// jssp[0] : mode.
|
||||
// jssp[8] : name.
|
||||
// jssp[16] : context.
|
||||
// jssp[24] : value.
|
||||
__ Push(x0, cp, x11, x10);
|
||||
__ CallRuntime(Runtime::kStoreLookupSlot, 4);
|
||||
} else {
|
||||
// Assignment to var or initializing assignment to let/const in harmony
|
||||
// mode.
|
||||
ASSERT(var->IsStackAllocated() || var->IsContextSlot());
|
||||
MemOperand location = VarOperand(var, x1);
|
||||
if (FLAG_debug_code && op == Token::INIT_LET) {
|
||||
|
@ -550,7 +550,6 @@ class FullCodeGenerator: public AstVisitor {
|
||||
// Helper functions to EmitVariableAssignment
|
||||
void EmitStoreToStackLocalOrContextSlot(Variable* var,
|
||||
MemOperand location);
|
||||
void EmitCallStoreContextSlot(Handle<String> name, StrictMode strict_mode);
|
||||
|
||||
// Complete a named property assignment. The receiver is expected on top
|
||||
// of the stack and the right-hand-side value in the accumulator.
|
||||
|
@ -802,7 +802,7 @@ void FullCodeGenerator::VisitVariableDeclaration(
|
||||
} else {
|
||||
__ push(Immediate(Smi::FromInt(0))); // Indicates no initial value.
|
||||
}
|
||||
__ CallRuntime(Runtime::kDeclareContextSlot, 4);
|
||||
__ CallRuntime(Runtime::kDeclareLookupSlot, 4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -855,7 +855,7 @@ void FullCodeGenerator::VisitFunctionDeclaration(
|
||||
__ push(Immediate(variable->name()));
|
||||
__ push(Immediate(Smi::FromInt(NONE)));
|
||||
VisitForStackValue(declaration->fun());
|
||||
__ CallRuntime(Runtime::kDeclareContextSlot, 4);
|
||||
__ CallRuntime(Runtime::kDeclareLookupSlot, 4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -2385,16 +2385,6 @@ void FullCodeGenerator::EmitStoreToStackLocalOrContextSlot(
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::EmitCallStoreContextSlot(
|
||||
Handle<String> name, StrictMode strict_mode) {
|
||||
__ push(eax); // Value.
|
||||
__ push(esi); // Context.
|
||||
__ push(Immediate(name));
|
||||
__ push(Immediate(Smi::FromInt(strict_mode)));
|
||||
__ CallRuntime(Runtime::kStoreContextSlot, 4);
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::EmitVariableAssignment(Variable* var,
|
||||
Token::Value op) {
|
||||
if (var->IsUnallocated()) {
|
||||
@ -2410,7 +2400,7 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var,
|
||||
__ push(eax);
|
||||
__ push(esi);
|
||||
__ push(Immediate(var->name()));
|
||||
__ CallRuntime(Runtime::kInitializeConstContextSlot, 3);
|
||||
__ CallRuntime(Runtime::kInitializeLegacyConstLookupSlot, 3);
|
||||
} else {
|
||||
ASSERT(var->IsStackLocal() || var->IsContextSlot());
|
||||
Label skip;
|
||||
@ -2424,27 +2414,31 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var,
|
||||
|
||||
} else if (var->mode() == LET && op != Token::INIT_LET) {
|
||||
// Non-initializing assignment to let variable needs a write barrier.
|
||||
if (var->IsLookupSlot()) {
|
||||
EmitCallStoreContextSlot(var->name(), strict_mode());
|
||||
} else {
|
||||
ASSERT(var->IsStackAllocated() || var->IsContextSlot());
|
||||
Label assign;
|
||||
MemOperand location = VarOperand(var, ecx);
|
||||
__ mov(edx, location);
|
||||
__ cmp(edx, isolate()->factory()->the_hole_value());
|
||||
__ j(not_equal, &assign, Label::kNear);
|
||||
__ push(Immediate(var->name()));
|
||||
__ CallRuntime(Runtime::kThrowReferenceError, 1);
|
||||
__ bind(&assign);
|
||||
EmitStoreToStackLocalOrContextSlot(var, location);
|
||||
}
|
||||
ASSERT(!var->IsLookupSlot());
|
||||
ASSERT(var->IsStackAllocated() || var->IsContextSlot());
|
||||
Label assign;
|
||||
MemOperand location = VarOperand(var, ecx);
|
||||
__ mov(edx, location);
|
||||
__ cmp(edx, isolate()->factory()->the_hole_value());
|
||||
__ j(not_equal, &assign, Label::kNear);
|
||||
__ push(Immediate(var->name()));
|
||||
__ CallRuntime(Runtime::kThrowReferenceError, 1);
|
||||
__ bind(&assign);
|
||||
EmitStoreToStackLocalOrContextSlot(var, location);
|
||||
|
||||
} else if (!var->is_const_mode() || op == Token::INIT_CONST) {
|
||||
// Assignment to var or initializing assignment to let/const
|
||||
// in harmony mode.
|
||||
if (var->IsLookupSlot()) {
|
||||
EmitCallStoreContextSlot(var->name(), strict_mode());
|
||||
ASSERT(op == Token::ASSIGN || op == Token::INIT_VAR ||
|
||||
op == Token::ASSIGN_ADD);
|
||||
// Assignment to var.
|
||||
__ push(eax); // Value.
|
||||
__ push(esi); // Context.
|
||||
__ push(Immediate(var->name()));
|
||||
__ push(Immediate(Smi::FromInt(strict_mode())));
|
||||
__ CallRuntime(Runtime::kStoreLookupSlot, 4);
|
||||
} else {
|
||||
// Assignment to var or initializing assignment to let/const in harmony
|
||||
// mode.
|
||||
ASSERT(var->IsStackAllocated() || var->IsContextSlot());
|
||||
MemOperand location = VarOperand(var, ecx);
|
||||
if (generate_debug_code_ && op == Token::INIT_LET) {
|
||||
|
@ -1704,7 +1704,7 @@ void Parser::Declare(Declaration* declaration, bool resolve, bool* ok) {
|
||||
// same variable if it is declared several times. This is not a
|
||||
// semantic issue as long as we keep the source order, but it may be
|
||||
// a performance issue since it may lead to repeated
|
||||
// RuntimeHidden_DeclareContextSlot calls.
|
||||
// RuntimeHidden_DeclareLookupSlot calls.
|
||||
declaration_scope->AddDeclaration(declaration);
|
||||
|
||||
if (mode == CONST_LEGACY && declaration_scope->is_global_scope()) {
|
||||
@ -1718,7 +1718,7 @@ void Parser::Declare(Declaration* declaration, bool resolve, bool* ok) {
|
||||
declaration_scope->strict_mode() == SLOPPY) {
|
||||
// For variable declarations in a sloppy eval scope the proxy is bound
|
||||
// to a lookup variable to force a dynamic declaration using the
|
||||
// DeclareContextSlot runtime function.
|
||||
// DeclareLookupSlot runtime function.
|
||||
Variable::Kind kind = Variable::NORMAL;
|
||||
var = new(zone()) Variable(
|
||||
declaration_scope, name, mode, true, kind,
|
||||
@ -2189,21 +2189,22 @@ Block* Parser::ParseVariableDeclarations(
|
||||
if (value != NULL && !inside_with()) {
|
||||
arguments->Add(value, zone());
|
||||
value = NULL; // zap the value to avoid the unnecessary assignment
|
||||
// Construct the call to Runtime_InitializeVarGlobal
|
||||
// and add it to the initialization statement block.
|
||||
initialize = factory()->NewCallRuntime(
|
||||
ast_value_factory_->initialize_var_global_string(),
|
||||
Runtime::FunctionForId(Runtime::kInitializeVarGlobal), arguments,
|
||||
pos);
|
||||
} else {
|
||||
initialize = NULL;
|
||||
}
|
||||
|
||||
// Construct the call to Runtime_InitializeVarGlobal
|
||||
// and add it to the initialization statement block.
|
||||
// Note that the function does different things depending on
|
||||
// the number of arguments (2 or 3).
|
||||
initialize = factory()->NewCallRuntime(
|
||||
ast_value_factory_->initialize_var_global_string(),
|
||||
Runtime::FunctionForId(Runtime::kInitializeVarGlobal),
|
||||
arguments, pos);
|
||||
}
|
||||
|
||||
block->AddStatement(
|
||||
factory()->NewExpressionStatement(initialize, RelocInfo::kNoPosition),
|
||||
zone());
|
||||
if (initialize != NULL) {
|
||||
block->AddStatement(factory()->NewExpressionStatement(
|
||||
initialize, RelocInfo::kNoPosition),
|
||||
zone());
|
||||
}
|
||||
} else if (needs_init) {
|
||||
// Constant initializations always assign to the declared constant which
|
||||
// is always at the function scope level. This is only relevant for
|
||||
|
593
src/runtime.cc
593
src/runtime.cc
@ -641,8 +641,8 @@ RUNTIME_FUNCTION(Runtime_CreateGlobalPrivateSymbol) {
|
||||
ASSERT(symbol->IsUndefined());
|
||||
symbol = isolate->factory()->NewPrivateSymbol();
|
||||
Handle<Symbol>::cast(symbol)->set_name(*name);
|
||||
JSObject::SetProperty(Handle<JSObject>::cast(privates),
|
||||
name, symbol, NONE, STRICT).Assert();
|
||||
JSObject::SetProperty(Handle<JSObject>::cast(privates), name, symbol, NONE,
|
||||
STRICT).Assert();
|
||||
}
|
||||
return *symbol;
|
||||
}
|
||||
@ -2110,6 +2110,50 @@ static Object* ThrowRedeclarationError(Isolate* isolate, Handle<String> name) {
|
||||
}
|
||||
|
||||
|
||||
// May throw a RedeclarationError.
|
||||
static Object* DeclareGlobals(Isolate* isolate, Handle<GlobalObject> global,
|
||||
Handle<String> name, Handle<Object> value,
|
||||
PropertyAttributes attr, bool is_var,
|
||||
bool is_const, bool is_function) {
|
||||
// Do the lookup own properties only, see ES5 erratum.
|
||||
LookupIterator it(global, name, LookupIterator::CHECK_HIDDEN);
|
||||
PropertyAttributes old_attributes = JSReceiver::GetPropertyAttributes(&it);
|
||||
|
||||
if (old_attributes != ABSENT) {
|
||||
// The name was declared before; check for conflicting re-declarations.
|
||||
if (is_const) return ThrowRedeclarationError(isolate, name);
|
||||
|
||||
// Skip var re-declarations.
|
||||
if (is_var) return isolate->heap()->undefined_value();
|
||||
|
||||
ASSERT(is_function);
|
||||
if ((old_attributes & DONT_DELETE) != 0) {
|
||||
// Only allow reconfiguring globals to functions in user code (no
|
||||
// natives, which are marked as read-only).
|
||||
ASSERT((attr & READ_ONLY) == 0);
|
||||
|
||||
// Check whether we can reconfigure the existing property into a
|
||||
// function.
|
||||
PropertyDetails old_details = it.property_details();
|
||||
// TODO(verwaest): CALLBACKS invalidly includes ExecutableAccessInfo,
|
||||
// which are actually data properties, not accessor properties.
|
||||
if (old_details.IsReadOnly() || old_details.IsDontEnum() ||
|
||||
old_details.type() == CALLBACKS) {
|
||||
return ThrowRedeclarationError(isolate, name);
|
||||
}
|
||||
// If the existing property is not configurable, keep its attributes. Do
|
||||
attr = old_attributes;
|
||||
}
|
||||
}
|
||||
|
||||
// Define or redefine own property.
|
||||
RETURN_FAILURE_ON_EXCEPTION(isolate, JSObject::SetOwnPropertyIgnoreAttributes(
|
||||
global, name, value, attr));
|
||||
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_DeclareGlobals) {
|
||||
HandleScope scope(isolate);
|
||||
ASSERT(args.length() == 3);
|
||||
@ -2124,181 +2168,42 @@ RUNTIME_FUNCTION(Runtime_DeclareGlobals) {
|
||||
for (int i = 0; i < length; i += 2) {
|
||||
HandleScope scope(isolate);
|
||||
Handle<String> name(String::cast(pairs->get(i)));
|
||||
Handle<Object> value(pairs->get(i + 1), isolate);
|
||||
Handle<Object> initial_value(pairs->get(i + 1), isolate);
|
||||
|
||||
// We have to declare a global const property. To capture we only
|
||||
// assign to it when evaluating the assignment for "const x =
|
||||
// <expr>" the initial value is the hole.
|
||||
bool is_var = value->IsUndefined();
|
||||
bool is_const = value->IsTheHole();
|
||||
bool is_function = value->IsSharedFunctionInfo();
|
||||
bool is_var = initial_value->IsUndefined();
|
||||
bool is_const = initial_value->IsTheHole();
|
||||
bool is_function = initial_value->IsSharedFunctionInfo();
|
||||
ASSERT(is_var + is_const + is_function == 1);
|
||||
|
||||
if (is_var || is_const) {
|
||||
// Lookup the property in the global object, and don't set the
|
||||
// value of the variable if the property is already there.
|
||||
// Do the lookup own properties only, see ES5 erratum.
|
||||
LookupResult lookup(isolate);
|
||||
global->LookupOwn(name, &lookup, true);
|
||||
if (lookup.IsFound()) {
|
||||
// We found an existing property. Unless it was an interceptor
|
||||
// that claims the property is absent, skip this declaration.
|
||||
if (!lookup.IsInterceptor()) continue;
|
||||
if (JSReceiver::GetPropertyAttributes(global, name) != ABSENT) continue;
|
||||
// Fall-through and introduce the absent property by using
|
||||
// SetProperty.
|
||||
}
|
||||
} else if (is_function) {
|
||||
Handle<Object> value;
|
||||
if (is_function) {
|
||||
// Copy the function and update its context. Use it as value.
|
||||
Handle<SharedFunctionInfo> shared =
|
||||
Handle<SharedFunctionInfo>::cast(value);
|
||||
Handle<SharedFunctionInfo>::cast(initial_value);
|
||||
Handle<JSFunction> function =
|
||||
isolate->factory()->NewFunctionFromSharedFunctionInfo(
|
||||
shared, context, TENURED);
|
||||
isolate->factory()->NewFunctionFromSharedFunctionInfo(shared, context,
|
||||
TENURED);
|
||||
value = function;
|
||||
} else {
|
||||
value = isolate->factory()->undefined_value();
|
||||
}
|
||||
|
||||
LookupResult lookup(isolate);
|
||||
global->LookupOwn(name, &lookup, true);
|
||||
|
||||
// Compute the property attributes. According to ECMA-262,
|
||||
// the property must be non-configurable except in eval.
|
||||
int attr = NONE;
|
||||
bool is_eval = DeclareGlobalsEvalFlag::decode(flags);
|
||||
if (!is_eval) {
|
||||
attr |= DONT_DELETE;
|
||||
}
|
||||
bool is_native = DeclareGlobalsNativeFlag::decode(flags);
|
||||
if (is_const || (is_native && is_function)) {
|
||||
attr |= READ_ONLY;
|
||||
}
|
||||
bool is_eval = DeclareGlobalsEvalFlag::decode(flags);
|
||||
int attr = NONE;
|
||||
if (is_const) attr |= READ_ONLY;
|
||||
if (is_function && is_native) attr |= READ_ONLY;
|
||||
if (!is_const && !is_eval) attr |= DONT_DELETE;
|
||||
|
||||
StrictMode strict_mode = DeclareGlobalsStrictMode::decode(flags);
|
||||
|
||||
if (!lookup.IsFound() || is_function) {
|
||||
// If the own property exists, check that we can reconfigure it
|
||||
// as required for function declarations.
|
||||
if (lookup.IsFound() && lookup.IsDontDelete()) {
|
||||
if (lookup.IsReadOnly() || lookup.IsDontEnum() ||
|
||||
lookup.IsPropertyCallbacks()) {
|
||||
return ThrowRedeclarationError(isolate, name);
|
||||
}
|
||||
// If the existing property is not configurable, keep its attributes.
|
||||
attr = lookup.GetAttributes();
|
||||
}
|
||||
// Define or redefine own property.
|
||||
RETURN_FAILURE_ON_EXCEPTION(isolate,
|
||||
JSObject::SetOwnPropertyIgnoreAttributes(
|
||||
global, name, value, static_cast<PropertyAttributes>(attr)));
|
||||
} else {
|
||||
// Do a [[Put]] on the existing (own) property.
|
||||
RETURN_FAILURE_ON_EXCEPTION(
|
||||
isolate,
|
||||
JSObject::SetProperty(
|
||||
global, name, value, static_cast<PropertyAttributes>(attr),
|
||||
strict_mode));
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT(!isolate->has_pending_exception());
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_DeclareContextSlot) {
|
||||
HandleScope scope(isolate);
|
||||
ASSERT(args.length() == 4);
|
||||
|
||||
// Declarations are always made in a function or native context. In the
|
||||
// case of eval code, the context passed is the context of the caller,
|
||||
// which may be some nested context and not the declaration context.
|
||||
CONVERT_ARG_HANDLE_CHECKED(Context, context_arg, 0);
|
||||
Handle<Context> context(context_arg->declaration_context());
|
||||
CONVERT_ARG_HANDLE_CHECKED(String, name, 1);
|
||||
CONVERT_SMI_ARG_CHECKED(mode_arg, 2);
|
||||
PropertyAttributes mode = static_cast<PropertyAttributes>(mode_arg);
|
||||
RUNTIME_ASSERT(mode == READ_ONLY || mode == NONE);
|
||||
CONVERT_ARG_HANDLE_CHECKED(Object, initial_value, 3);
|
||||
|
||||
int index;
|
||||
PropertyAttributes attributes;
|
||||
ContextLookupFlags flags = DONT_FOLLOW_CHAINS;
|
||||
BindingFlags binding_flags;
|
||||
Handle<Object> holder =
|
||||
context->Lookup(name, flags, &index, &attributes, &binding_flags);
|
||||
|
||||
if (attributes != ABSENT) {
|
||||
// The name was declared before; check for conflicting re-declarations.
|
||||
// Note: this is actually inconsistent with what happens for globals (where
|
||||
// we silently ignore such declarations).
|
||||
if (((attributes & READ_ONLY) != 0) || (mode == READ_ONLY)) {
|
||||
// Functions are not read-only.
|
||||
ASSERT(mode != READ_ONLY || initial_value->IsTheHole());
|
||||
return ThrowRedeclarationError(isolate, name);
|
||||
}
|
||||
|
||||
// Initialize it if necessary.
|
||||
if (*initial_value != NULL) {
|
||||
if (index >= 0) {
|
||||
ASSERT(holder.is_identical_to(context));
|
||||
if (((attributes & READ_ONLY) == 0) ||
|
||||
context->get(index)->IsTheHole()) {
|
||||
context->set(index, *initial_value);
|
||||
}
|
||||
} else {
|
||||
// Slow case: The property is in the context extension object of a
|
||||
// function context or the global object of a native context.
|
||||
Handle<JSObject> object = Handle<JSObject>::cast(holder);
|
||||
RETURN_FAILURE_ON_EXCEPTION(
|
||||
isolate,
|
||||
JSReceiver::SetProperty(object, name, initial_value, mode, SLOPPY));
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// The property is not in the function context. It needs to be
|
||||
// "declared" in the function context's extension context or as a
|
||||
// property of the the global object.
|
||||
Handle<JSObject> object;
|
||||
if (context->has_extension()) {
|
||||
object = Handle<JSObject>(JSObject::cast(context->extension()));
|
||||
} else {
|
||||
// Context extension objects are allocated lazily.
|
||||
ASSERT(context->IsFunctionContext());
|
||||
object = isolate->factory()->NewJSObject(
|
||||
isolate->context_extension_function());
|
||||
context->set_extension(*object);
|
||||
}
|
||||
ASSERT(*object != NULL);
|
||||
|
||||
// Declare the property by setting it to the initial value if provided,
|
||||
// or undefined, and use the correct mode (e.g. READ_ONLY attribute for
|
||||
// constant declarations).
|
||||
ASSERT(!JSReceiver::HasOwnProperty(object, name));
|
||||
Handle<Object> value(isolate->heap()->undefined_value(), isolate);
|
||||
if (*initial_value != NULL) value = initial_value;
|
||||
// Declaring a const context slot is a conflicting declaration if
|
||||
// there is a callback with that name in a prototype. It is
|
||||
// allowed to introduce const variables in
|
||||
// JSContextExtensionObjects. They are treated specially in
|
||||
// SetProperty and no setters are invoked for those since they are
|
||||
// not real JSObjects.
|
||||
if (initial_value->IsTheHole() &&
|
||||
!object->IsJSContextExtensionObject()) {
|
||||
LookupResult lookup(isolate);
|
||||
object->Lookup(name, &lookup);
|
||||
if (lookup.IsPropertyCallbacks()) {
|
||||
return ThrowRedeclarationError(isolate, name);
|
||||
}
|
||||
}
|
||||
if (object->IsJSGlobalObject()) {
|
||||
// Define own property on the global object.
|
||||
RETURN_FAILURE_ON_EXCEPTION(isolate,
|
||||
JSObject::SetOwnPropertyIgnoreAttributes(object, name, value, mode));
|
||||
} else {
|
||||
RETURN_FAILURE_ON_EXCEPTION(isolate,
|
||||
JSReceiver::SetProperty(object, name, value, mode, SLOPPY));
|
||||
}
|
||||
Object* result = DeclareGlobals(isolate, global, name, value,
|
||||
static_cast<PropertyAttributes>(attr),
|
||||
is_var, is_const, is_function);
|
||||
if (isolate->has_pending_exception()) return result;
|
||||
}
|
||||
|
||||
return isolate->heap()->undefined_value();
|
||||
@ -2313,60 +2218,23 @@ RUNTIME_FUNCTION(Runtime_InitializeVarGlobal) {
|
||||
|
||||
// Determine if we need to assign to the variable if it already
|
||||
// exists (based on the number of arguments).
|
||||
RUNTIME_ASSERT(args.length() == 2 || args.length() == 3);
|
||||
bool assign = args.length() == 3;
|
||||
RUNTIME_ASSERT(args.length() == 3);
|
||||
|
||||
CONVERT_ARG_HANDLE_CHECKED(String, name, 0);
|
||||
CONVERT_STRICT_MODE_ARG_CHECKED(strict_mode, 1);
|
||||
CONVERT_ARG_HANDLE_CHECKED(Object, value, 2);
|
||||
|
||||
// According to ECMA-262, section 12.2, page 62, the property must
|
||||
// not be deletable.
|
||||
PropertyAttributes attributes = DONT_DELETE;
|
||||
|
||||
// Lookup the property as own on the global object. If it isn't
|
||||
// there, there is a property with this name in the prototype chain.
|
||||
// We follow Safari and Firefox behavior and only set the property
|
||||
// if there is an explicit initialization value that we have
|
||||
// to assign to the property.
|
||||
// Note that objects can have hidden prototypes, so we need to traverse
|
||||
// the whole chain of hidden prototypes to do an 'own' lookup.
|
||||
LookupResult lookup(isolate);
|
||||
isolate->context()->global_object()->LookupOwn(name, &lookup, true);
|
||||
if (lookup.IsInterceptor()) {
|
||||
Handle<JSObject> holder(lookup.holder());
|
||||
PropertyAttributes intercepted =
|
||||
JSReceiver::GetPropertyAttributes(holder, name);
|
||||
if (intercepted != ABSENT && (intercepted & READ_ONLY) == 0) {
|
||||
// Found an interceptor that's not read only.
|
||||
if (assign) {
|
||||
CONVERT_ARG_HANDLE_CHECKED(Object, value, 2);
|
||||
Handle<Object> result;
|
||||
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
|
||||
isolate, result,
|
||||
JSObject::SetPropertyForResult(
|
||||
holder, &lookup, name, value, attributes, strict_mode));
|
||||
return *result;
|
||||
} else {
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (assign) {
|
||||
CONVERT_ARG_HANDLE_CHECKED(Object, value, 2);
|
||||
Handle<GlobalObject> global(isolate->context()->global_object());
|
||||
Handle<Object> result;
|
||||
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
|
||||
isolate, result,
|
||||
JSReceiver::SetProperty(global, name, value, attributes, strict_mode));
|
||||
return *result;
|
||||
}
|
||||
return isolate->heap()->undefined_value();
|
||||
Handle<GlobalObject> global(isolate->context()->global_object());
|
||||
Handle<Object> result;
|
||||
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
|
||||
isolate, result,
|
||||
JSReceiver::SetProperty(global, name, value, NONE, strict_mode));
|
||||
return *result;
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_InitializeConstGlobal) {
|
||||
SealHandleScope shs(isolate);
|
||||
HandleScope handle_scope(isolate);
|
||||
// All constants are declared with an initial value. The name
|
||||
// of the constant is the first argument and the initial value
|
||||
// is the second.
|
||||
@ -2374,71 +2242,112 @@ RUNTIME_FUNCTION(Runtime_InitializeConstGlobal) {
|
||||
CONVERT_ARG_HANDLE_CHECKED(String, name, 0);
|
||||
CONVERT_ARG_HANDLE_CHECKED(Object, value, 1);
|
||||
|
||||
// Get the current global object from top.
|
||||
GlobalObject* global = isolate->context()->global_object();
|
||||
Handle<GlobalObject> global = isolate->global_object();
|
||||
|
||||
// According to ECMA-262, section 12.2, page 62, the property must
|
||||
// not be deletable. Since it's a const, it must be READ_ONLY too.
|
||||
PropertyAttributes attributes =
|
||||
// Lookup the property as own on the global object.
|
||||
LookupIterator it(global, name, LookupIterator::CHECK_HIDDEN);
|
||||
PropertyAttributes old_attributes = JSReceiver::GetPropertyAttributes(&it);
|
||||
|
||||
PropertyAttributes attr =
|
||||
static_cast<PropertyAttributes>(DONT_DELETE | READ_ONLY);
|
||||
|
||||
// Lookup the property as own on the global object. If it isn't
|
||||
// there, we add the property and take special precautions to always
|
||||
// add it even in case of callbacks in the prototype chain (this rules
|
||||
// out using SetProperty). We use SetOwnPropertyIgnoreAttributes instead
|
||||
LookupResult lookup(isolate);
|
||||
global->LookupOwn(name, &lookup);
|
||||
if (!lookup.IsFound()) {
|
||||
HandleScope handle_scope(isolate);
|
||||
Handle<GlobalObject> global(isolate->context()->global_object());
|
||||
JSObject::AddProperty(global, name, value, attributes);
|
||||
return *value;
|
||||
}
|
||||
|
||||
if (!lookup.IsReadOnly()) {
|
||||
// Restore global object from context (in case of GC) and continue
|
||||
// with setting the value.
|
||||
HandleScope handle_scope(isolate);
|
||||
Handle<GlobalObject> global(isolate->context()->global_object());
|
||||
|
||||
// BUG 1213575: Handle the case where we have to set a read-only
|
||||
// property through an interceptor and only do it if it's
|
||||
// uninitialized, e.g. the hole. Nirk...
|
||||
// Passing sloppy mode because the property is writable.
|
||||
RETURN_FAILURE_ON_EXCEPTION(
|
||||
isolate,
|
||||
JSReceiver::SetProperty(global, name, value, attributes, SLOPPY));
|
||||
return *value;
|
||||
}
|
||||
|
||||
// Set the value, but only if we're assigning the initial value to a
|
||||
// constant. For now, we determine this by checking if the
|
||||
// current value is the hole.
|
||||
// Strict mode handling not needed (const is disallowed in strict mode).
|
||||
if (lookup.IsField()) {
|
||||
FixedArray* properties = global->properties();
|
||||
int index = lookup.GetFieldIndex().outobject_array_index();
|
||||
if (properties->get(index)->IsTheHole() || !lookup.IsReadOnly()) {
|
||||
properties->set(index, *value);
|
||||
// Set the value if the property is either missing, or the property attributes
|
||||
// allow setting the value without invoking an accessor.
|
||||
if (it.IsFound()) {
|
||||
// Ignore if we can't reconfigure the value.
|
||||
if ((old_attributes & DONT_DELETE) != 0) {
|
||||
if ((old_attributes & READ_ONLY) != 0 ||
|
||||
it.property_kind() == LookupIterator::ACCESSOR) {
|
||||
return *value;
|
||||
}
|
||||
attr = static_cast<PropertyAttributes>(old_attributes | READ_ONLY);
|
||||
}
|
||||
} else if (lookup.IsNormal()) {
|
||||
if (global->GetNormalizedProperty(&lookup)->IsTheHole() ||
|
||||
!lookup.IsReadOnly()) {
|
||||
HandleScope scope(isolate);
|
||||
JSObject::SetNormalizedProperty(Handle<JSObject>(global), &lookup, value);
|
||||
}
|
||||
} else {
|
||||
// Ignore re-initialization of constants that have already been
|
||||
// assigned a constant value.
|
||||
ASSERT(lookup.IsReadOnly() && lookup.IsConstant());
|
||||
}
|
||||
|
||||
// Use the set value as the result of the operation.
|
||||
RETURN_FAILURE_ON_EXCEPTION(isolate, JSObject::SetOwnPropertyIgnoreAttributes(
|
||||
global, name, value, attr));
|
||||
|
||||
return *value;
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_InitializeConstContextSlot) {
|
||||
RUNTIME_FUNCTION(Runtime_DeclareLookupSlot) {
|
||||
HandleScope scope(isolate);
|
||||
ASSERT(args.length() == 4);
|
||||
|
||||
// Declarations are always made in a function, native, or global context. In
|
||||
// the case of eval code, the context passed is the context of the caller,
|
||||
// which may be some nested context and not the declaration context.
|
||||
CONVERT_ARG_HANDLE_CHECKED(Context, context_arg, 0);
|
||||
Handle<Context> context(context_arg->declaration_context());
|
||||
CONVERT_ARG_HANDLE_CHECKED(String, name, 1);
|
||||
CONVERT_SMI_ARG_CHECKED(attr_arg, 2);
|
||||
PropertyAttributes attr = static_cast<PropertyAttributes>(attr_arg);
|
||||
RUNTIME_ASSERT(attr == READ_ONLY || attr == NONE);
|
||||
CONVERT_ARG_HANDLE_CHECKED(Object, initial_value, 3);
|
||||
|
||||
// TODO(verwaest): Unify the encoding indicating "var" with DeclareGlobals.
|
||||
bool is_var = *initial_value == NULL;
|
||||
bool is_const = initial_value->IsTheHole();
|
||||
bool is_function = initial_value->IsJSFunction();
|
||||
ASSERT(is_var + is_const + is_function == 1);
|
||||
|
||||
int index;
|
||||
PropertyAttributes attributes;
|
||||
ContextLookupFlags flags = DONT_FOLLOW_CHAINS;
|
||||
BindingFlags binding_flags;
|
||||
Handle<Object> holder =
|
||||
context->Lookup(name, flags, &index, &attributes, &binding_flags);
|
||||
|
||||
Handle<JSObject> object;
|
||||
Handle<Object> value =
|
||||
is_function ? initial_value
|
||||
: Handle<Object>::cast(isolate->factory()->undefined_value());
|
||||
|
||||
// TODO(verwaest): This case should probably not be covered by this function,
|
||||
// but by DeclareGlobals instead.
|
||||
if ((attributes != ABSENT && holder->IsJSGlobalObject()) ||
|
||||
(context_arg->has_extension() &&
|
||||
context_arg->extension()->IsJSGlobalObject())) {
|
||||
return DeclareGlobals(isolate, Handle<JSGlobalObject>::cast(holder), name,
|
||||
value, attr, is_var, is_const, is_function);
|
||||
}
|
||||
|
||||
if (attributes != ABSENT) {
|
||||
// The name was declared before; check for conflicting re-declarations.
|
||||
if (is_const || (attributes & READ_ONLY) != 0) {
|
||||
return ThrowRedeclarationError(isolate, name);
|
||||
}
|
||||
|
||||
// Skip var re-declarations.
|
||||
if (is_var) return isolate->heap()->undefined_value();
|
||||
|
||||
ASSERT(is_function);
|
||||
if (index >= 0) {
|
||||
ASSERT(holder.is_identical_to(context));
|
||||
context->set(index, *initial_value);
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
|
||||
object = Handle<JSObject>::cast(holder);
|
||||
|
||||
} else if (context->has_extension()) {
|
||||
object = handle(JSObject::cast(context->extension()));
|
||||
ASSERT(object->IsJSContextExtensionObject() || object->IsJSGlobalObject());
|
||||
} else {
|
||||
ASSERT(context->IsFunctionContext());
|
||||
object =
|
||||
isolate->factory()->NewJSObject(isolate->context_extension_function());
|
||||
context->set_extension(*object);
|
||||
}
|
||||
|
||||
RETURN_FAILURE_ON_EXCEPTION(isolate, JSObject::SetOwnPropertyIgnoreAttributes(
|
||||
object, name, value, attr));
|
||||
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_InitializeLegacyConstLookupSlot) {
|
||||
HandleScope scope(isolate);
|
||||
ASSERT(args.length() == 3);
|
||||
|
||||
@ -2451,86 +2360,56 @@ RUNTIME_FUNCTION(Runtime_InitializeConstContextSlot) {
|
||||
|
||||
int index;
|
||||
PropertyAttributes attributes;
|
||||
ContextLookupFlags flags = FOLLOW_CHAINS;
|
||||
ContextLookupFlags flags = DONT_FOLLOW_CHAINS;
|
||||
BindingFlags binding_flags;
|
||||
Handle<Object> holder =
|
||||
context->Lookup(name, flags, &index, &attributes, &binding_flags);
|
||||
|
||||
if (index >= 0) {
|
||||
ASSERT(holder->IsContext());
|
||||
// Property was found in a context. Perform the assignment if we
|
||||
// found some non-constant or an uninitialized constant.
|
||||
// Property was found in a context. Perform the assignment if the constant
|
||||
// was uninitialized.
|
||||
Handle<Context> context = Handle<Context>::cast(holder);
|
||||
if ((attributes & READ_ONLY) == 0 || context->get(index)->IsTheHole()) {
|
||||
context->set(index, *value);
|
||||
}
|
||||
ASSERT((attributes & READ_ONLY) != 0);
|
||||
if (context->get(index)->IsTheHole()) context->set(index, *value);
|
||||
return *value;
|
||||
}
|
||||
|
||||
// The property could not be found, we introduce it as a property of the
|
||||
// global object.
|
||||
PropertyAttributes attr =
|
||||
static_cast<PropertyAttributes>(DONT_DELETE | READ_ONLY);
|
||||
|
||||
// Strict mode handling not needed (legacy const is disallowed in strict
|
||||
// mode).
|
||||
|
||||
// The declared const was configurable, and may have been deleted in the
|
||||
// meanwhile. If so, re-introduce the variable in the context extension.
|
||||
ASSERT(context_arg->has_extension());
|
||||
if (attributes == ABSENT) {
|
||||
Handle<JSObject> global = Handle<JSObject>(
|
||||
isolate->context()->global_object());
|
||||
// Strict mode not needed (const disallowed in strict mode).
|
||||
RETURN_FAILURE_ON_EXCEPTION(
|
||||
isolate,
|
||||
JSReceiver::SetProperty(global, name, value, NONE, SLOPPY));
|
||||
return *value;
|
||||
}
|
||||
|
||||
// The property was present in some function's context extension object,
|
||||
// as a property on the subject of a with, or as a property of the global
|
||||
// object.
|
||||
//
|
||||
// In most situations, eval-introduced consts should still be present in
|
||||
// the context extension object. However, because declaration and
|
||||
// initialization are separate, the property might have been deleted
|
||||
// before we reach the initialization point.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// function f() { eval("delete x; const x;"); }
|
||||
//
|
||||
// In that case, the initialization behaves like a normal assignment.
|
||||
Handle<JSObject> object = Handle<JSObject>::cast(holder);
|
||||
|
||||
if (*object == context->extension()) {
|
||||
// This is the property that was introduced by the const declaration.
|
||||
// Set it if it hasn't been set before. NOTE: We cannot use
|
||||
// GetProperty() to get the current value as it 'unholes' the value.
|
||||
LookupResult lookup(isolate);
|
||||
object->LookupOwnRealNamedProperty(name, &lookup);
|
||||
ASSERT(lookup.IsFound()); // the property was declared
|
||||
ASSERT(lookup.IsReadOnly()); // and it was declared as read-only
|
||||
|
||||
if (lookup.IsField()) {
|
||||
FixedArray* properties = object->properties();
|
||||
FieldIndex index = lookup.GetFieldIndex();
|
||||
ASSERT(!index.is_inobject());
|
||||
if (properties->get(index.outobject_array_index())->IsTheHole()) {
|
||||
properties->set(index.outobject_array_index(), *value);
|
||||
}
|
||||
} else if (lookup.IsNormal()) {
|
||||
if (object->GetNormalizedProperty(&lookup)->IsTheHole()) {
|
||||
JSObject::SetNormalizedProperty(object, &lookup, value);
|
||||
}
|
||||
} else {
|
||||
// We should not reach here. Any real, named property should be
|
||||
// either a field or a dictionary slot.
|
||||
UNREACHABLE();
|
||||
}
|
||||
holder = handle(context_arg->extension(), isolate);
|
||||
} else {
|
||||
// The property was found on some other object. Set it if it is not a
|
||||
// read-only property.
|
||||
if ((attributes & READ_ONLY) == 0) {
|
||||
// Strict mode not needed (const disallowed in strict mode).
|
||||
RETURN_FAILURE_ON_EXCEPTION(
|
||||
isolate,
|
||||
JSReceiver::SetProperty(object, name, value, attributes, SLOPPY));
|
||||
// For JSContextExtensionObjects, the initializer can be run multiple times
|
||||
// if in a for loop: for (var i = 0; i < 2; i++) { const x = i; }. Only the
|
||||
// first assignment should go through. For JSGlobalObjects, additionally any
|
||||
// code can run in between that modifies the declared property.
|
||||
ASSERT(holder->IsJSGlobalObject() || holder->IsJSContextExtensionObject());
|
||||
|
||||
LookupIterator it(holder, name, LookupIterator::CHECK_HIDDEN);
|
||||
PropertyAttributes old_attributes = JSReceiver::GetPropertyAttributes(&it);
|
||||
|
||||
// Ignore if we can't reconfigure the value.
|
||||
if ((old_attributes & DONT_DELETE) != 0) {
|
||||
if ((old_attributes & READ_ONLY) != 0 ||
|
||||
it.property_kind() == LookupIterator::ACCESSOR) {
|
||||
return *value;
|
||||
}
|
||||
attr = static_cast<PropertyAttributes>(old_attributes | READ_ONLY);
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_FAILURE_ON_EXCEPTION(
|
||||
isolate, JSObject::SetOwnPropertyIgnoreAttributes(
|
||||
Handle<JSObject>::cast(holder), name, value, attr));
|
||||
|
||||
return *value;
|
||||
}
|
||||
|
||||
@ -9333,7 +9212,7 @@ RUNTIME_FUNCTION_RETURN_PAIR(Runtime_LoadContextSlotNoReferenceError) {
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_StoreContextSlot) {
|
||||
RUNTIME_FUNCTION(Runtime_StoreLookupSlot) {
|
||||
HandleScope scope(isolate);
|
||||
ASSERT(args.length() == 4);
|
||||
|
||||
@ -9351,22 +9230,13 @@ RUNTIME_FUNCTION(Runtime_StoreContextSlot) {
|
||||
&index,
|
||||
&attributes,
|
||||
&binding_flags);
|
||||
// In case of JSProxy, an exception might have been thrown.
|
||||
if (isolate->has_pending_exception()) return isolate->heap()->exception();
|
||||
|
||||
// The property was found in a context slot.
|
||||
if (index >= 0) {
|
||||
// The property was found in a context slot.
|
||||
Handle<Context> context = Handle<Context>::cast(holder);
|
||||
if (binding_flags == MUTABLE_CHECK_INITIALIZED &&
|
||||
context->get(index)->IsTheHole()) {
|
||||
Handle<Object> error =
|
||||
isolate->factory()->NewReferenceError("not_defined",
|
||||
HandleVector(&name, 1));
|
||||
return isolate->Throw(*error);
|
||||
}
|
||||
// Ignore if read_only variable.
|
||||
if ((attributes & READ_ONLY) == 0) {
|
||||
// Context is a fixed array and set cannot fail.
|
||||
context->set(index, *value);
|
||||
Handle<Context>::cast(holder)->set(index, *value);
|
||||
} else if (strict_mode == STRICT) {
|
||||
// Setting read only property in strict mode.
|
||||
Handle<Object> error =
|
||||
@ -9381,39 +9251,22 @@ RUNTIME_FUNCTION(Runtime_StoreContextSlot) {
|
||||
// context extension object, a property of the subject of a with, or a
|
||||
// property of the global object.
|
||||
Handle<JSReceiver> object;
|
||||
|
||||
if (!holder.is_null()) {
|
||||
if (attributes != ABSENT) {
|
||||
// The property exists on the holder.
|
||||
object = Handle<JSReceiver>::cast(holder);
|
||||
} else {
|
||||
// The property was not found.
|
||||
ASSERT(attributes == ABSENT);
|
||||
|
||||
if (strict_mode == STRICT) {
|
||||
// Throw in strict mode (assignment to undefined variable).
|
||||
Handle<Object> error =
|
||||
isolate->factory()->NewReferenceError(
|
||||
"not_defined", HandleVector(&name, 1));
|
||||
return isolate->Throw(*error);
|
||||
}
|
||||
// In sloppy mode, the property is added to the global object.
|
||||
attributes = NONE;
|
||||
object = Handle<JSReceiver>(isolate->context()->global_object());
|
||||
}
|
||||
|
||||
// Set the property if it's not read only or doesn't yet exist.
|
||||
if ((attributes & READ_ONLY) == 0 ||
|
||||
(JSReceiver::GetOwnPropertyAttributes(object, name) == ABSENT)) {
|
||||
RETURN_FAILURE_ON_EXCEPTION(
|
||||
isolate,
|
||||
JSReceiver::SetProperty(object, name, value, NONE, strict_mode));
|
||||
} else if (strict_mode == STRICT && (attributes & READ_ONLY) != 0) {
|
||||
// Setting read only property in strict mode.
|
||||
Handle<Object> error =
|
||||
isolate->factory()->NewTypeError(
|
||||
"strict_cannot_assign", HandleVector(&name, 1));
|
||||
} else if (strict_mode == STRICT) {
|
||||
// If absent in strict mode: throw.
|
||||
Handle<Object> error = isolate->factory()->NewReferenceError(
|
||||
"not_defined", HandleVector(&name, 1));
|
||||
return isolate->Throw(*error);
|
||||
} else {
|
||||
// If absent in sloppy mode: add the property to the global object.
|
||||
object = Handle<JSReceiver>(context->global_object());
|
||||
}
|
||||
|
||||
RETURN_FAILURE_ON_EXCEPTION(
|
||||
isolate, JSReceiver::SetProperty(object, name, value, NONE, strict_mode));
|
||||
|
||||
return *value;
|
||||
}
|
||||
|
||||
|
598
src/runtime.h
598
src/runtime.h
@ -177,310 +177,310 @@ namespace internal {
|
||||
F(IsValidSmi, 1, 1)
|
||||
|
||||
|
||||
#define RUNTIME_FUNCTION_LIST_ALWAYS_2(F) \
|
||||
/* Reflection */ \
|
||||
F(FunctionSetInstanceClassName, 2, 1) \
|
||||
F(FunctionSetLength, 2, 1) \
|
||||
F(FunctionSetPrototype, 2, 1) \
|
||||
F(FunctionGetName, 1, 1) \
|
||||
F(FunctionSetName, 2, 1) \
|
||||
F(FunctionNameShouldPrintAsAnonymous, 1, 1) \
|
||||
F(FunctionMarkNameShouldPrintAsAnonymous, 1, 1) \
|
||||
F(FunctionIsGenerator, 1, 1) \
|
||||
F(FunctionBindArguments, 4, 1) \
|
||||
F(BoundFunctionGetBindings, 1, 1) \
|
||||
F(FunctionRemovePrototype, 1, 1) \
|
||||
F(FunctionGetSourceCode, 1, 1) \
|
||||
F(FunctionGetScript, 1, 1) \
|
||||
F(FunctionGetScriptSourcePosition, 1, 1) \
|
||||
F(FunctionGetPositionForOffset, 2, 1) \
|
||||
F(FunctionIsAPIFunction, 1, 1) \
|
||||
F(FunctionIsBuiltin, 1, 1) \
|
||||
F(GetScript, 1, 1) \
|
||||
F(CollectStackTrace, 2, 1) \
|
||||
F(GetV8Version, 0, 1) \
|
||||
\
|
||||
F(SetCode, 2, 1) \
|
||||
\
|
||||
F(CreateApiFunction, 2, 1) \
|
||||
F(IsTemplate, 1, 1) \
|
||||
F(GetTemplateField, 2, 1) \
|
||||
F(DisableAccessChecks, 1, 1) \
|
||||
F(EnableAccessChecks, 1, 1) \
|
||||
\
|
||||
/* Dates */ \
|
||||
F(DateCurrentTime, 0, 1) \
|
||||
F(DateParseString, 2, 1) \
|
||||
F(DateLocalTimezone, 1, 1) \
|
||||
F(DateToUTC, 1, 1) \
|
||||
F(DateMakeDay, 2, 1) \
|
||||
F(DateSetValue, 3, 1) \
|
||||
F(DateCacheVersion, 0, 1) \
|
||||
\
|
||||
/* Globals */ \
|
||||
F(CompileString, 2, 1) \
|
||||
\
|
||||
/* Eval */ \
|
||||
F(GlobalProxy, 1, 1) \
|
||||
F(IsAttachedGlobal, 1, 1) \
|
||||
\
|
||||
F(AddProperty, 4, 1) \
|
||||
F(AddPropertyForTemplate, 4, 1) \
|
||||
F(SetProperty, 4, 1) \
|
||||
F(DefineDataPropertyUnchecked, 4, 1) \
|
||||
F(DefineAccessorPropertyUnchecked, 5, 1) \
|
||||
F(GetDataProperty, 2, 1) \
|
||||
F(SetHiddenProperty, 3, 1) \
|
||||
\
|
||||
/* Arrays */ \
|
||||
F(RemoveArrayHoles, 2, 1) \
|
||||
F(GetArrayKeys, 2, 1) \
|
||||
F(MoveArrayContents, 2, 1) \
|
||||
F(EstimateNumberOfElements, 1, 1) \
|
||||
\
|
||||
/* Getters and Setters */ \
|
||||
F(LookupAccessor, 3, 1) \
|
||||
\
|
||||
/* ES5 */ \
|
||||
F(ObjectFreeze, 1, 1) \
|
||||
\
|
||||
/* Harmony modules */ \
|
||||
F(IsJSModule, 1, 1) \
|
||||
\
|
||||
/* Harmony symbols */ \
|
||||
F(CreateSymbol, 1, 1) \
|
||||
F(CreatePrivateSymbol, 1, 1) \
|
||||
F(CreateGlobalPrivateSymbol, 1, 1) \
|
||||
F(NewSymbolWrapper, 1, 1) \
|
||||
F(SymbolDescription, 1, 1) \
|
||||
F(SymbolRegistry, 0, 1) \
|
||||
F(SymbolIsPrivate, 1, 1) \
|
||||
\
|
||||
/* Harmony proxies */ \
|
||||
F(CreateJSProxy, 2, 1) \
|
||||
F(CreateJSFunctionProxy, 4, 1) \
|
||||
F(IsJSProxy, 1, 1) \
|
||||
F(IsJSFunctionProxy, 1, 1) \
|
||||
F(GetHandler, 1, 1) \
|
||||
F(GetCallTrap, 1, 1) \
|
||||
F(GetConstructTrap, 1, 1) \
|
||||
F(Fix, 1, 1) \
|
||||
\
|
||||
/* Harmony sets */ \
|
||||
F(SetInitialize, 1, 1) \
|
||||
F(SetAdd, 2, 1) \
|
||||
F(SetHas, 2, 1) \
|
||||
F(SetDelete, 2, 1) \
|
||||
F(SetClear, 1, 1) \
|
||||
F(SetGetSize, 1, 1) \
|
||||
\
|
||||
F(SetIteratorInitialize, 3, 1) \
|
||||
F(SetIteratorNext, 2, 1) \
|
||||
\
|
||||
/* Harmony maps */ \
|
||||
F(MapInitialize, 1, 1) \
|
||||
F(MapGet, 2, 1) \
|
||||
F(MapHas, 2, 1) \
|
||||
F(MapDelete, 2, 1) \
|
||||
F(MapClear, 1, 1) \
|
||||
F(MapSet, 3, 1) \
|
||||
F(MapGetSize, 1, 1) \
|
||||
\
|
||||
F(MapIteratorInitialize, 3, 1) \
|
||||
F(MapIteratorNext, 2, 1) \
|
||||
\
|
||||
/* Harmony weak maps and sets */ \
|
||||
F(WeakCollectionInitialize, 1, 1) \
|
||||
F(WeakCollectionGet, 2, 1) \
|
||||
F(WeakCollectionHas, 2, 1) \
|
||||
F(WeakCollectionDelete, 2, 1) \
|
||||
F(WeakCollectionSet, 3, 1) \
|
||||
\
|
||||
/* Harmony events */ \
|
||||
F(EnqueueMicrotask, 1, 1) \
|
||||
F(RunMicrotasks, 0, 1) \
|
||||
\
|
||||
/* Harmony observe */ \
|
||||
F(IsObserved, 1, 1) \
|
||||
F(SetIsObserved, 1, 1) \
|
||||
F(GetObservationState, 0, 1) \
|
||||
F(ObservationWeakMapCreate, 0, 1) \
|
||||
F(ObserverObjectAndRecordHaveSameOrigin, 3, 1) \
|
||||
F(ObjectWasCreatedInCurrentOrigin, 1, 1) \
|
||||
F(GetObjectContextObjectObserve, 1, 1) \
|
||||
F(GetObjectContextObjectGetNotifier, 1, 1) \
|
||||
F(GetObjectContextNotifierPerformChange, 1, 1) \
|
||||
\
|
||||
/* Harmony typed arrays */ \
|
||||
F(ArrayBufferInitialize, 2, 1)\
|
||||
F(ArrayBufferSliceImpl, 3, 1) \
|
||||
F(ArrayBufferIsView, 1, 1) \
|
||||
F(ArrayBufferNeuter, 1, 1) \
|
||||
\
|
||||
F(TypedArrayInitializeFromArrayLike, 4, 1) \
|
||||
F(TypedArrayGetBuffer, 1, 1) \
|
||||
F(TypedArraySetFastCases, 3, 1) \
|
||||
\
|
||||
F(DataViewGetBuffer, 1, 1) \
|
||||
F(DataViewGetInt8, 3, 1) \
|
||||
F(DataViewGetUint8, 3, 1) \
|
||||
F(DataViewGetInt16, 3, 1) \
|
||||
F(DataViewGetUint16, 3, 1) \
|
||||
F(DataViewGetInt32, 3, 1) \
|
||||
F(DataViewGetUint32, 3, 1) \
|
||||
F(DataViewGetFloat32, 3, 1) \
|
||||
F(DataViewGetFloat64, 3, 1) \
|
||||
\
|
||||
F(DataViewSetInt8, 4, 1) \
|
||||
F(DataViewSetUint8, 4, 1) \
|
||||
F(DataViewSetInt16, 4, 1) \
|
||||
F(DataViewSetUint16, 4, 1) \
|
||||
F(DataViewSetInt32, 4, 1) \
|
||||
F(DataViewSetUint32, 4, 1) \
|
||||
F(DataViewSetFloat32, 4, 1) \
|
||||
F(DataViewSetFloat64, 4, 1) \
|
||||
\
|
||||
/* Statements */ \
|
||||
F(NewObjectFromBound, 1, 1) \
|
||||
\
|
||||
/* Declarations and initialization */ \
|
||||
F(InitializeVarGlobal, -1 /* 2 or 3 */, 1) \
|
||||
F(OptimizeObjectForAddingMultipleProperties, 2, 1) \
|
||||
\
|
||||
/* Debugging */ \
|
||||
F(DebugPrint, 1, 1) \
|
||||
F(GlobalPrint, 1, 1) \
|
||||
F(DebugTrace, 0, 1) \
|
||||
F(TraceEnter, 0, 1) \
|
||||
F(TraceExit, 1, 1) \
|
||||
F(Abort, 1, 1) \
|
||||
F(AbortJS, 1, 1) \
|
||||
/* ES5 */ \
|
||||
F(OwnKeys, 1, 1) \
|
||||
\
|
||||
/* Message objects */ \
|
||||
F(MessageGetStartPosition, 1, 1) \
|
||||
F(MessageGetScript, 1, 1) \
|
||||
\
|
||||
#define RUNTIME_FUNCTION_LIST_ALWAYS_2(F) \
|
||||
/* Reflection */ \
|
||||
F(FunctionSetInstanceClassName, 2, 1) \
|
||||
F(FunctionSetLength, 2, 1) \
|
||||
F(FunctionSetPrototype, 2, 1) \
|
||||
F(FunctionGetName, 1, 1) \
|
||||
F(FunctionSetName, 2, 1) \
|
||||
F(FunctionNameShouldPrintAsAnonymous, 1, 1) \
|
||||
F(FunctionMarkNameShouldPrintAsAnonymous, 1, 1) \
|
||||
F(FunctionIsGenerator, 1, 1) \
|
||||
F(FunctionBindArguments, 4, 1) \
|
||||
F(BoundFunctionGetBindings, 1, 1) \
|
||||
F(FunctionRemovePrototype, 1, 1) \
|
||||
F(FunctionGetSourceCode, 1, 1) \
|
||||
F(FunctionGetScript, 1, 1) \
|
||||
F(FunctionGetScriptSourcePosition, 1, 1) \
|
||||
F(FunctionGetPositionForOffset, 2, 1) \
|
||||
F(FunctionIsAPIFunction, 1, 1) \
|
||||
F(FunctionIsBuiltin, 1, 1) \
|
||||
F(GetScript, 1, 1) \
|
||||
F(CollectStackTrace, 2, 1) \
|
||||
F(GetV8Version, 0, 1) \
|
||||
\
|
||||
F(SetCode, 2, 1) \
|
||||
\
|
||||
F(CreateApiFunction, 2, 1) \
|
||||
F(IsTemplate, 1, 1) \
|
||||
F(GetTemplateField, 2, 1) \
|
||||
F(DisableAccessChecks, 1, 1) \
|
||||
F(EnableAccessChecks, 1, 1) \
|
||||
\
|
||||
/* Dates */ \
|
||||
F(DateCurrentTime, 0, 1) \
|
||||
F(DateParseString, 2, 1) \
|
||||
F(DateLocalTimezone, 1, 1) \
|
||||
F(DateToUTC, 1, 1) \
|
||||
F(DateMakeDay, 2, 1) \
|
||||
F(DateSetValue, 3, 1) \
|
||||
F(DateCacheVersion, 0, 1) \
|
||||
\
|
||||
/* Globals */ \
|
||||
F(CompileString, 2, 1) \
|
||||
\
|
||||
/* Eval */ \
|
||||
F(GlobalProxy, 1, 1) \
|
||||
F(IsAttachedGlobal, 1, 1) \
|
||||
\
|
||||
F(AddProperty, 4, 1) \
|
||||
F(AddPropertyForTemplate, 4, 1) \
|
||||
F(SetProperty, 4, 1) \
|
||||
F(DefineDataPropertyUnchecked, 4, 1) \
|
||||
F(DefineAccessorPropertyUnchecked, 5, 1) \
|
||||
F(GetDataProperty, 2, 1) \
|
||||
F(SetHiddenProperty, 3, 1) \
|
||||
\
|
||||
/* Arrays */ \
|
||||
F(RemoveArrayHoles, 2, 1) \
|
||||
F(GetArrayKeys, 2, 1) \
|
||||
F(MoveArrayContents, 2, 1) \
|
||||
F(EstimateNumberOfElements, 1, 1) \
|
||||
\
|
||||
/* Getters and Setters */ \
|
||||
F(LookupAccessor, 3, 1) \
|
||||
\
|
||||
/* ES5 */ \
|
||||
F(ObjectFreeze, 1, 1) \
|
||||
\
|
||||
/* Harmony modules */ \
|
||||
F(IsJSModule, 1, 1) \
|
||||
\
|
||||
/* Harmony symbols */ \
|
||||
F(CreateSymbol, 1, 1) \
|
||||
F(CreatePrivateSymbol, 1, 1) \
|
||||
F(CreateGlobalPrivateSymbol, 1, 1) \
|
||||
F(NewSymbolWrapper, 1, 1) \
|
||||
F(SymbolDescription, 1, 1) \
|
||||
F(SymbolRegistry, 0, 1) \
|
||||
F(SymbolIsPrivate, 1, 1) \
|
||||
\
|
||||
/* Harmony proxies */ \
|
||||
F(CreateJSProxy, 2, 1) \
|
||||
F(CreateJSFunctionProxy, 4, 1) \
|
||||
F(IsJSProxy, 1, 1) \
|
||||
F(IsJSFunctionProxy, 1, 1) \
|
||||
F(GetHandler, 1, 1) \
|
||||
F(GetCallTrap, 1, 1) \
|
||||
F(GetConstructTrap, 1, 1) \
|
||||
F(Fix, 1, 1) \
|
||||
\
|
||||
/* Harmony sets */ \
|
||||
F(SetInitialize, 1, 1) \
|
||||
F(SetAdd, 2, 1) \
|
||||
F(SetHas, 2, 1) \
|
||||
F(SetDelete, 2, 1) \
|
||||
F(SetClear, 1, 1) \
|
||||
F(SetGetSize, 1, 1) \
|
||||
\
|
||||
F(SetIteratorInitialize, 3, 1) \
|
||||
F(SetIteratorNext, 2, 1) \
|
||||
\
|
||||
/* Harmony maps */ \
|
||||
F(MapInitialize, 1, 1) \
|
||||
F(MapGet, 2, 1) \
|
||||
F(MapHas, 2, 1) \
|
||||
F(MapDelete, 2, 1) \
|
||||
F(MapClear, 1, 1) \
|
||||
F(MapSet, 3, 1) \
|
||||
F(MapGetSize, 1, 1) \
|
||||
\
|
||||
F(MapIteratorInitialize, 3, 1) \
|
||||
F(MapIteratorNext, 2, 1) \
|
||||
\
|
||||
/* Harmony weak maps and sets */ \
|
||||
F(WeakCollectionInitialize, 1, 1) \
|
||||
F(WeakCollectionGet, 2, 1) \
|
||||
F(WeakCollectionHas, 2, 1) \
|
||||
F(WeakCollectionDelete, 2, 1) \
|
||||
F(WeakCollectionSet, 3, 1) \
|
||||
\
|
||||
/* Harmony events */ \
|
||||
F(EnqueueMicrotask, 1, 1) \
|
||||
F(RunMicrotasks, 0, 1) \
|
||||
\
|
||||
/* Harmony observe */ \
|
||||
F(IsObserved, 1, 1) \
|
||||
F(SetIsObserved, 1, 1) \
|
||||
F(GetObservationState, 0, 1) \
|
||||
F(ObservationWeakMapCreate, 0, 1) \
|
||||
F(ObserverObjectAndRecordHaveSameOrigin, 3, 1) \
|
||||
F(ObjectWasCreatedInCurrentOrigin, 1, 1) \
|
||||
F(GetObjectContextObjectObserve, 1, 1) \
|
||||
F(GetObjectContextObjectGetNotifier, 1, 1) \
|
||||
F(GetObjectContextNotifierPerformChange, 1, 1) \
|
||||
\
|
||||
/* Harmony typed arrays */ \
|
||||
F(ArrayBufferInitialize, 2, 1) \
|
||||
F(ArrayBufferSliceImpl, 3, 1) \
|
||||
F(ArrayBufferIsView, 1, 1) \
|
||||
F(ArrayBufferNeuter, 1, 1) \
|
||||
\
|
||||
F(TypedArrayInitializeFromArrayLike, 4, 1) \
|
||||
F(TypedArrayGetBuffer, 1, 1) \
|
||||
F(TypedArraySetFastCases, 3, 1) \
|
||||
\
|
||||
F(DataViewGetBuffer, 1, 1) \
|
||||
F(DataViewGetInt8, 3, 1) \
|
||||
F(DataViewGetUint8, 3, 1) \
|
||||
F(DataViewGetInt16, 3, 1) \
|
||||
F(DataViewGetUint16, 3, 1) \
|
||||
F(DataViewGetInt32, 3, 1) \
|
||||
F(DataViewGetUint32, 3, 1) \
|
||||
F(DataViewGetFloat32, 3, 1) \
|
||||
F(DataViewGetFloat64, 3, 1) \
|
||||
\
|
||||
F(DataViewSetInt8, 4, 1) \
|
||||
F(DataViewSetUint8, 4, 1) \
|
||||
F(DataViewSetInt16, 4, 1) \
|
||||
F(DataViewSetUint16, 4, 1) \
|
||||
F(DataViewSetInt32, 4, 1) \
|
||||
F(DataViewSetUint32, 4, 1) \
|
||||
F(DataViewSetFloat32, 4, 1) \
|
||||
F(DataViewSetFloat64, 4, 1) \
|
||||
\
|
||||
/* Statements */ \
|
||||
F(NewObjectFromBound, 1, 1) \
|
||||
\
|
||||
/* Declarations and initialization */ \
|
||||
F(InitializeVarGlobal, 3, 1) \
|
||||
F(OptimizeObjectForAddingMultipleProperties, 2, 1) \
|
||||
\
|
||||
/* Debugging */ \
|
||||
F(DebugPrint, 1, 1) \
|
||||
F(GlobalPrint, 1, 1) \
|
||||
F(DebugTrace, 0, 1) \
|
||||
F(TraceEnter, 0, 1) \
|
||||
F(TraceExit, 1, 1) \
|
||||
F(Abort, 1, 1) \
|
||||
F(AbortJS, 1, 1) \
|
||||
/* ES5 */ \
|
||||
F(OwnKeys, 1, 1) \
|
||||
\
|
||||
/* Message objects */ \
|
||||
F(MessageGetStartPosition, 1, 1) \
|
||||
F(MessageGetScript, 1, 1) \
|
||||
\
|
||||
/* Pseudo functions - handled as macros by parser */ \
|
||||
F(IS_VAR, 1, 1) \
|
||||
\
|
||||
/* expose boolean functions from objects-inl.h */ \
|
||||
F(HasFastSmiElements, 1, 1) \
|
||||
F(HasFastSmiOrObjectElements, 1, 1) \
|
||||
F(HasFastObjectElements, 1, 1) \
|
||||
F(HasFastDoubleElements, 1, 1) \
|
||||
F(HasFastHoleyElements, 1, 1) \
|
||||
F(HasDictionaryElements, 1, 1) \
|
||||
F(HasSloppyArgumentsElements, 1, 1) \
|
||||
F(HasExternalUint8ClampedElements, 1, 1) \
|
||||
F(HasExternalArrayElements, 1, 1) \
|
||||
F(HasExternalInt8Elements, 1, 1) \
|
||||
F(HasExternalUint8Elements, 1, 1) \
|
||||
F(HasExternalInt16Elements, 1, 1) \
|
||||
F(HasExternalUint16Elements, 1, 1) \
|
||||
F(HasExternalInt32Elements, 1, 1) \
|
||||
F(HasExternalUint32Elements, 1, 1) \
|
||||
F(HasExternalFloat32Elements, 1, 1) \
|
||||
F(HasExternalFloat64Elements, 1, 1) \
|
||||
F(HasFixedUint8ClampedElements, 1, 1) \
|
||||
F(HasFixedInt8Elements, 1, 1) \
|
||||
F(HasFixedUint8Elements, 1, 1) \
|
||||
F(HasFixedInt16Elements, 1, 1) \
|
||||
F(HasFixedUint16Elements, 1, 1) \
|
||||
F(HasFixedInt32Elements, 1, 1) \
|
||||
F(HasFixedUint32Elements, 1, 1) \
|
||||
F(HasFixedFloat32Elements, 1, 1) \
|
||||
F(HasFixedFloat64Elements, 1, 1) \
|
||||
F(HasFastProperties, 1, 1) \
|
||||
F(TransitionElementsKind, 2, 1) \
|
||||
F(HaveSameMap, 2, 1) \
|
||||
F(IS_VAR, 1, 1) \
|
||||
\
|
||||
/* expose boolean functions from objects-inl.h */ \
|
||||
F(HasFastSmiElements, 1, 1) \
|
||||
F(HasFastSmiOrObjectElements, 1, 1) \
|
||||
F(HasFastObjectElements, 1, 1) \
|
||||
F(HasFastDoubleElements, 1, 1) \
|
||||
F(HasFastHoleyElements, 1, 1) \
|
||||
F(HasDictionaryElements, 1, 1) \
|
||||
F(HasSloppyArgumentsElements, 1, 1) \
|
||||
F(HasExternalUint8ClampedElements, 1, 1) \
|
||||
F(HasExternalArrayElements, 1, 1) \
|
||||
F(HasExternalInt8Elements, 1, 1) \
|
||||
F(HasExternalUint8Elements, 1, 1) \
|
||||
F(HasExternalInt16Elements, 1, 1) \
|
||||
F(HasExternalUint16Elements, 1, 1) \
|
||||
F(HasExternalInt32Elements, 1, 1) \
|
||||
F(HasExternalUint32Elements, 1, 1) \
|
||||
F(HasExternalFloat32Elements, 1, 1) \
|
||||
F(HasExternalFloat64Elements, 1, 1) \
|
||||
F(HasFixedUint8ClampedElements, 1, 1) \
|
||||
F(HasFixedInt8Elements, 1, 1) \
|
||||
F(HasFixedUint8Elements, 1, 1) \
|
||||
F(HasFixedInt16Elements, 1, 1) \
|
||||
F(HasFixedUint16Elements, 1, 1) \
|
||||
F(HasFixedInt32Elements, 1, 1) \
|
||||
F(HasFixedUint32Elements, 1, 1) \
|
||||
F(HasFixedFloat32Elements, 1, 1) \
|
||||
F(HasFixedFloat64Elements, 1, 1) \
|
||||
F(HasFastProperties, 1, 1) \
|
||||
F(TransitionElementsKind, 2, 1) \
|
||||
F(HaveSameMap, 2, 1) \
|
||||
F(IsJSGlobalProxy, 1, 1)
|
||||
|
||||
|
||||
#define RUNTIME_FUNCTION_LIST_ALWAYS_3(F) \
|
||||
/* String and Regexp */ \
|
||||
F(NumberToStringRT, 1, 1) \
|
||||
F(RegExpConstructResult, 3, 1) \
|
||||
F(RegExpExecRT, 4, 1) \
|
||||
F(StringAdd, 2, 1) \
|
||||
F(SubString, 3, 1) \
|
||||
F(InternalizeString, 1, 1) \
|
||||
F(StringCompare, 2, 1) \
|
||||
F(StringCharCodeAtRT, 2, 1) \
|
||||
F(GetFromCache, 2, 1) \
|
||||
\
|
||||
/* Compilation */ \
|
||||
F(CompileUnoptimized, 1, 1) \
|
||||
F(CompileOptimized, 2, 1) \
|
||||
F(TryInstallOptimizedCode, 1, 1) \
|
||||
F(NotifyDeoptimized, 1, 1) \
|
||||
F(NotifyStubFailure, 0, 1) \
|
||||
\
|
||||
/* Utilities */ \
|
||||
F(AllocateInNewSpace, 1, 1) \
|
||||
F(AllocateInTargetSpace, 2, 1) \
|
||||
F(AllocateHeapNumber, 0, 1) \
|
||||
F(NumberToSmi, 1, 1) \
|
||||
F(NumberToStringSkipCache, 1, 1) \
|
||||
\
|
||||
F(NewSloppyArguments, 3, 1) \
|
||||
F(NewStrictArguments, 3, 1) \
|
||||
\
|
||||
/* Harmony generators */ \
|
||||
F(CreateJSGeneratorObject, 0, 1) \
|
||||
F(SuspendJSGeneratorObject, 1, 1) \
|
||||
F(ResumeJSGeneratorObject, 3, 1) \
|
||||
F(ThrowGeneratorStateError, 1, 1) \
|
||||
\
|
||||
/* Arrays */ \
|
||||
F(ArrayConstructor, -1, 1) \
|
||||
F(InternalArrayConstructor, -1, 1) \
|
||||
\
|
||||
/* Literals */ \
|
||||
F(MaterializeRegExpLiteral, 4, 1)\
|
||||
F(CreateObjectLiteral, 4, 1) \
|
||||
F(CreateArrayLiteral, 4, 1) \
|
||||
F(CreateArrayLiteralStubBailout, 3, 1) \
|
||||
\
|
||||
/* Statements */ \
|
||||
F(NewClosure, 3, 1) \
|
||||
F(NewClosureFromStubFailure, 1, 1) \
|
||||
F(NewObject, 1, 1) \
|
||||
F(NewObjectWithAllocationSite, 2, 1) \
|
||||
F(FinalizeInstanceSize, 1, 1) \
|
||||
F(Throw, 1, 1) \
|
||||
F(ReThrow, 1, 1) \
|
||||
F(ThrowReferenceError, 1, 1) \
|
||||
F(ThrowNotDateError, 0, 1) \
|
||||
F(StackGuard, 0, 1) \
|
||||
F(Interrupt, 0, 1) \
|
||||
F(PromoteScheduledException, 0, 1) \
|
||||
\
|
||||
/* Contexts */ \
|
||||
F(NewGlobalContext, 2, 1) \
|
||||
F(NewFunctionContext, 1, 1) \
|
||||
F(PushWithContext, 2, 1) \
|
||||
F(PushCatchContext, 3, 1) \
|
||||
F(PushBlockContext, 2, 1) \
|
||||
F(PushModuleContext, 2, 1) \
|
||||
F(DeleteContextSlot, 2, 1) \
|
||||
F(LoadContextSlot, 2, 2) \
|
||||
#define RUNTIME_FUNCTION_LIST_ALWAYS_3(F) \
|
||||
/* String and Regexp */ \
|
||||
F(NumberToStringRT, 1, 1) \
|
||||
F(RegExpConstructResult, 3, 1) \
|
||||
F(RegExpExecRT, 4, 1) \
|
||||
F(StringAdd, 2, 1) \
|
||||
F(SubString, 3, 1) \
|
||||
F(InternalizeString, 1, 1) \
|
||||
F(StringCompare, 2, 1) \
|
||||
F(StringCharCodeAtRT, 2, 1) \
|
||||
F(GetFromCache, 2, 1) \
|
||||
\
|
||||
/* Compilation */ \
|
||||
F(CompileUnoptimized, 1, 1) \
|
||||
F(CompileOptimized, 2, 1) \
|
||||
F(TryInstallOptimizedCode, 1, 1) \
|
||||
F(NotifyDeoptimized, 1, 1) \
|
||||
F(NotifyStubFailure, 0, 1) \
|
||||
\
|
||||
/* Utilities */ \
|
||||
F(AllocateInNewSpace, 1, 1) \
|
||||
F(AllocateInTargetSpace, 2, 1) \
|
||||
F(AllocateHeapNumber, 0, 1) \
|
||||
F(NumberToSmi, 1, 1) \
|
||||
F(NumberToStringSkipCache, 1, 1) \
|
||||
\
|
||||
F(NewSloppyArguments, 3, 1) \
|
||||
F(NewStrictArguments, 3, 1) \
|
||||
\
|
||||
/* Harmony generators */ \
|
||||
F(CreateJSGeneratorObject, 0, 1) \
|
||||
F(SuspendJSGeneratorObject, 1, 1) \
|
||||
F(ResumeJSGeneratorObject, 3, 1) \
|
||||
F(ThrowGeneratorStateError, 1, 1) \
|
||||
\
|
||||
/* Arrays */ \
|
||||
F(ArrayConstructor, -1, 1) \
|
||||
F(InternalArrayConstructor, -1, 1) \
|
||||
\
|
||||
/* Literals */ \
|
||||
F(MaterializeRegExpLiteral, 4, 1) \
|
||||
F(CreateObjectLiteral, 4, 1) \
|
||||
F(CreateArrayLiteral, 4, 1) \
|
||||
F(CreateArrayLiteralStubBailout, 3, 1) \
|
||||
\
|
||||
/* Statements */ \
|
||||
F(NewClosure, 3, 1) \
|
||||
F(NewClosureFromStubFailure, 1, 1) \
|
||||
F(NewObject, 1, 1) \
|
||||
F(NewObjectWithAllocationSite, 2, 1) \
|
||||
F(FinalizeInstanceSize, 1, 1) \
|
||||
F(Throw, 1, 1) \
|
||||
F(ReThrow, 1, 1) \
|
||||
F(ThrowReferenceError, 1, 1) \
|
||||
F(ThrowNotDateError, 0, 1) \
|
||||
F(StackGuard, 0, 1) \
|
||||
F(Interrupt, 0, 1) \
|
||||
F(PromoteScheduledException, 0, 1) \
|
||||
\
|
||||
/* Contexts */ \
|
||||
F(NewGlobalContext, 2, 1) \
|
||||
F(NewFunctionContext, 1, 1) \
|
||||
F(PushWithContext, 2, 1) \
|
||||
F(PushCatchContext, 3, 1) \
|
||||
F(PushBlockContext, 2, 1) \
|
||||
F(PushModuleContext, 2, 1) \
|
||||
F(DeleteContextSlot, 2, 1) \
|
||||
F(LoadContextSlot, 2, 2) \
|
||||
F(LoadContextSlotNoReferenceError, 2, 2) \
|
||||
F(StoreContextSlot, 4, 1) \
|
||||
\
|
||||
/* Declarations and initialization */ \
|
||||
F(DeclareGlobals, 3, 1) \
|
||||
F(DeclareModules, 1, 1) \
|
||||
F(DeclareContextSlot, 4, 1) \
|
||||
F(InitializeConstGlobal, 2, 1) \
|
||||
F(InitializeConstContextSlot, 3, 1) \
|
||||
\
|
||||
/* Eval */ \
|
||||
F(ResolvePossiblyDirectEval, 5, 2) \
|
||||
\
|
||||
/* Maths */ \
|
||||
F(MathPowSlow, 2, 1) \
|
||||
F(StoreLookupSlot, 4, 1) \
|
||||
\
|
||||
/* Declarations and initialization */ \
|
||||
F(DeclareGlobals, 3, 1) \
|
||||
F(DeclareModules, 1, 1) \
|
||||
F(DeclareLookupSlot, 4, 1) \
|
||||
F(InitializeConstGlobal, 2, 1) \
|
||||
F(InitializeLegacyConstLookupSlot, 3, 1) \
|
||||
\
|
||||
/* Eval */ \
|
||||
F(ResolvePossiblyDirectEval, 5, 2) \
|
||||
\
|
||||
/* Maths */ \
|
||||
F(MathPowSlow, 2, 1) \
|
||||
F(MathPowRT, 2, 1)
|
||||
|
||||
|
||||
|
@ -824,7 +824,7 @@ void FullCodeGenerator::VisitVariableDeclaration(
|
||||
} else {
|
||||
__ Push(Smi::FromInt(0)); // Indicates no initial value.
|
||||
}
|
||||
__ CallRuntime(Runtime::kDeclareContextSlot, 4);
|
||||
__ CallRuntime(Runtime::kDeclareLookupSlot, 4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -878,7 +878,7 @@ void FullCodeGenerator::VisitFunctionDeclaration(
|
||||
__ Push(variable->name());
|
||||
__ Push(Smi::FromInt(NONE));
|
||||
VisitForStackValue(declaration->fun());
|
||||
__ CallRuntime(Runtime::kDeclareContextSlot, 4);
|
||||
__ CallRuntime(Runtime::kDeclareLookupSlot, 4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -2383,16 +2383,6 @@ void FullCodeGenerator::EmitStoreToStackLocalOrContextSlot(
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::EmitCallStoreContextSlot(
|
||||
Handle<String> name, StrictMode strict_mode) {
|
||||
__ Push(rax); // Value.
|
||||
__ Push(rsi); // Context.
|
||||
__ Push(name);
|
||||
__ Push(Smi::FromInt(strict_mode));
|
||||
__ CallRuntime(Runtime::kStoreContextSlot, 4);
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::EmitVariableAssignment(Variable* var,
|
||||
Token::Value op) {
|
||||
if (var->IsUnallocated()) {
|
||||
@ -2408,7 +2398,7 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var,
|
||||
__ Push(rax);
|
||||
__ Push(rsi);
|
||||
__ Push(var->name());
|
||||
__ CallRuntime(Runtime::kInitializeConstContextSlot, 3);
|
||||
__ CallRuntime(Runtime::kInitializeLegacyConstLookupSlot, 3);
|
||||
} else {
|
||||
ASSERT(var->IsStackLocal() || var->IsContextSlot());
|
||||
Label skip;
|
||||
@ -2422,27 +2412,31 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var,
|
||||
|
||||
} else if (var->mode() == LET && op != Token::INIT_LET) {
|
||||
// Non-initializing assignment to let variable needs a write barrier.
|
||||
if (var->IsLookupSlot()) {
|
||||
EmitCallStoreContextSlot(var->name(), strict_mode());
|
||||
} else {
|
||||
ASSERT(var->IsStackAllocated() || var->IsContextSlot());
|
||||
Label assign;
|
||||
MemOperand location = VarOperand(var, rcx);
|
||||
__ movp(rdx, location);
|
||||
__ CompareRoot(rdx, Heap::kTheHoleValueRootIndex);
|
||||
__ j(not_equal, &assign, Label::kNear);
|
||||
__ Push(var->name());
|
||||
__ CallRuntime(Runtime::kThrowReferenceError, 1);
|
||||
__ bind(&assign);
|
||||
EmitStoreToStackLocalOrContextSlot(var, location);
|
||||
}
|
||||
ASSERT(!var->IsLookupSlot());
|
||||
ASSERT(var->IsStackAllocated() || var->IsContextSlot());
|
||||
Label assign;
|
||||
MemOperand location = VarOperand(var, rcx);
|
||||
__ movp(rdx, location);
|
||||
__ CompareRoot(rdx, Heap::kTheHoleValueRootIndex);
|
||||
__ j(not_equal, &assign, Label::kNear);
|
||||
__ Push(var->name());
|
||||
__ CallRuntime(Runtime::kThrowReferenceError, 1);
|
||||
__ bind(&assign);
|
||||
EmitStoreToStackLocalOrContextSlot(var, location);
|
||||
|
||||
} else if (!var->is_const_mode() || op == Token::INIT_CONST) {
|
||||
// Assignment to var or initializing assignment to let/const
|
||||
// in harmony mode.
|
||||
if (var->IsLookupSlot()) {
|
||||
EmitCallStoreContextSlot(var->name(), strict_mode());
|
||||
ASSERT(op == Token::ASSIGN || op == Token::INIT_VAR ||
|
||||
op == Token::ASSIGN_ADD);
|
||||
// Assignment to var.
|
||||
__ Push(rax); // Value.
|
||||
__ Push(rsi); // Context.
|
||||
__ Push(var->name());
|
||||
__ Push(Smi::FromInt(strict_mode()));
|
||||
__ CallRuntime(Runtime::kStoreLookupSlot, 4);
|
||||
} else {
|
||||
// Assignment to var or initializing assignment to let/const in harmony
|
||||
// mode.
|
||||
ASSERT(var->IsStackAllocated() || var->IsContextSlot());
|
||||
MemOperand location = VarOperand(var, rcx);
|
||||
if (generate_debug_code_ && op == Token::INIT_LET) {
|
||||
|
@ -236,17 +236,14 @@ TEST(Unknown) {
|
||||
{ DeclarationContext context;
|
||||
context.Check("var x; x",
|
||||
1, // access
|
||||
1, // declaration
|
||||
2, // declaration + initialization
|
||||
EXPECT_RESULT, Undefined(CcTest::isolate()));
|
||||
0, 0, EXPECT_RESULT, Undefined(CcTest::isolate()));
|
||||
}
|
||||
|
||||
{ DeclarationContext context;
|
||||
context.Check("var x = 0; x",
|
||||
1, // access
|
||||
2, // declaration + initialization
|
||||
2, // declaration + initialization
|
||||
EXPECT_RESULT, Number::New(CcTest::isolate(), 0));
|
||||
1, // initialization
|
||||
0, EXPECT_RESULT, Number::New(CcTest::isolate(), 0));
|
||||
}
|
||||
|
||||
{ DeclarationContext context;
|
||||
@ -260,78 +257,19 @@ TEST(Unknown) {
|
||||
{ DeclarationContext context;
|
||||
context.Check("const x; x",
|
||||
1, // access
|
||||
2, // declaration + initialization
|
||||
1, // declaration
|
||||
EXPECT_RESULT, Undefined(CcTest::isolate()));
|
||||
0, 0, EXPECT_RESULT, Undefined(CcTest::isolate()));
|
||||
}
|
||||
|
||||
{ DeclarationContext context;
|
||||
// SB 0 - BUG 1213579
|
||||
context.Check("const x = 0; x",
|
||||
1, // access
|
||||
2, // declaration + initialization
|
||||
1, // declaration
|
||||
EXPECT_RESULT, Undefined(CcTest::isolate()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
class PresentPropertyContext: public DeclarationContext {
|
||||
protected:
|
||||
virtual v8::Handle<Integer> Query(Local<String> key) {
|
||||
return Integer::New(isolate(), v8::None);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
TEST(Present) {
|
||||
HandleScope scope(CcTest::isolate());
|
||||
|
||||
{ PresentPropertyContext context;
|
||||
context.Check("var x; x",
|
||||
1, // access
|
||||
0,
|
||||
2, // declaration + initialization
|
||||
EXPECT_EXCEPTION); // x is not defined!
|
||||
}
|
||||
|
||||
{ PresentPropertyContext context;
|
||||
context.Check("var x = 0; x",
|
||||
1, // access
|
||||
1, // initialization
|
||||
2, // declaration + initialization
|
||||
EXPECT_RESULT, Number::New(CcTest::isolate(), 0));
|
||||
}
|
||||
|
||||
{ PresentPropertyContext context;
|
||||
context.Check("function x() { }; x",
|
||||
1, // access
|
||||
0,
|
||||
0,
|
||||
EXPECT_RESULT);
|
||||
}
|
||||
|
||||
{ PresentPropertyContext context;
|
||||
context.Check("const x; x",
|
||||
1, // access
|
||||
1, // initialization
|
||||
1, // (re-)declaration
|
||||
EXPECT_RESULT, Undefined(CcTest::isolate()));
|
||||
}
|
||||
|
||||
{ PresentPropertyContext context;
|
||||
context.Check("const x = 0; x",
|
||||
1, // access
|
||||
1, // initialization
|
||||
1, // (re-)declaration
|
||||
EXPECT_RESULT, Number::New(CcTest::isolate(), 0));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
class AbsentPropertyContext: public DeclarationContext {
|
||||
protected:
|
||||
virtual v8::Handle<Integer> Query(Local<String> key) {
|
||||
@ -348,17 +286,14 @@ TEST(Absent) {
|
||||
{ AbsentPropertyContext context;
|
||||
context.Check("var x; x",
|
||||
1, // access
|
||||
1, // declaration
|
||||
2, // declaration + initialization
|
||||
EXPECT_RESULT, Undefined(isolate));
|
||||
0, 0, EXPECT_RESULT, Undefined(isolate));
|
||||
}
|
||||
|
||||
{ AbsentPropertyContext context;
|
||||
context.Check("var x = 0; x",
|
||||
1, // access
|
||||
2, // declaration + initialization
|
||||
2, // declaration + initialization
|
||||
EXPECT_RESULT, Number::New(isolate, 0));
|
||||
1, // initialization
|
||||
0, EXPECT_RESULT, Number::New(isolate, 0));
|
||||
}
|
||||
|
||||
{ AbsentPropertyContext context;
|
||||
@ -372,25 +307,19 @@ TEST(Absent) {
|
||||
{ AbsentPropertyContext context;
|
||||
context.Check("const x; x",
|
||||
1, // access
|
||||
2, // declaration + initialization
|
||||
1, // declaration
|
||||
EXPECT_RESULT, Undefined(isolate));
|
||||
0, 0, EXPECT_RESULT, Undefined(isolate));
|
||||
}
|
||||
|
||||
{ AbsentPropertyContext context;
|
||||
context.Check("const x = 0; x",
|
||||
1, // access
|
||||
2, // declaration + initialization
|
||||
1, // declaration
|
||||
EXPECT_RESULT, Undefined(isolate)); // SB 0 - BUG 1213579
|
||||
0, 0, EXPECT_RESULT, Number::New(isolate, 0));
|
||||
}
|
||||
|
||||
{ AbsentPropertyContext context;
|
||||
context.Check("if (false) { var x = 0 }; x",
|
||||
1, // access
|
||||
1, // declaration
|
||||
1, // declaration + initialization
|
||||
EXPECT_RESULT, Undefined(isolate));
|
||||
0, 0, EXPECT_RESULT, Undefined(isolate));
|
||||
}
|
||||
}
|
||||
|
||||
@ -439,17 +368,14 @@ TEST(Appearing) {
|
||||
{ AppearingPropertyContext context;
|
||||
context.Check("var x; x",
|
||||
1, // access
|
||||
1, // declaration
|
||||
2, // declaration + initialization
|
||||
EXPECT_RESULT, Undefined(CcTest::isolate()));
|
||||
0, 0, EXPECT_RESULT, Undefined(CcTest::isolate()));
|
||||
}
|
||||
|
||||
{ AppearingPropertyContext context;
|
||||
context.Check("var x = 0; x",
|
||||
1, // access
|
||||
2, // declaration + initialization
|
||||
2, // declaration + initialization
|
||||
EXPECT_RESULT, Number::New(CcTest::isolate(), 0));
|
||||
1, // initialization
|
||||
0, EXPECT_RESULT, Number::New(CcTest::isolate(), 0));
|
||||
}
|
||||
|
||||
{ AppearingPropertyContext context;
|
||||
@ -463,78 +389,13 @@ TEST(Appearing) {
|
||||
{ AppearingPropertyContext context;
|
||||
context.Check("const x; x",
|
||||
1, // access
|
||||
2, // declaration + initialization
|
||||
1, // declaration
|
||||
EXPECT_RESULT, Undefined(CcTest::isolate()));
|
||||
0, 0, EXPECT_RESULT, Undefined(CcTest::isolate()));
|
||||
}
|
||||
|
||||
{ AppearingPropertyContext context;
|
||||
context.Check("const x = 0; x",
|
||||
1, // access
|
||||
2, // declaration + initialization
|
||||
1, // declaration
|
||||
EXPECT_RESULT, Undefined(CcTest::isolate()));
|
||||
// Result is undefined because declaration succeeded but
|
||||
// initialization to 0 failed (due to context behavior).
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
class ReappearingPropertyContext: public DeclarationContext {
|
||||
public:
|
||||
enum State {
|
||||
DECLARE,
|
||||
DONT_DECLARE,
|
||||
INITIALIZE,
|
||||
UNKNOWN
|
||||
};
|
||||
|
||||
ReappearingPropertyContext() : state_(DECLARE) { }
|
||||
|
||||
protected:
|
||||
virtual v8::Handle<Integer> Query(Local<String> key) {
|
||||
switch (state_) {
|
||||
case DECLARE:
|
||||
// Force the first declaration by returning that
|
||||
// the property is absent.
|
||||
state_ = DONT_DECLARE;
|
||||
return Handle<Integer>();
|
||||
case DONT_DECLARE:
|
||||
// Ignore the second declaration by returning
|
||||
// that the property is already there.
|
||||
state_ = INITIALIZE;
|
||||
return Integer::New(isolate(), v8::None);
|
||||
case INITIALIZE:
|
||||
// Force an initialization by returning that
|
||||
// the property is absent. This will make sure
|
||||
// that the setter is called and it will not
|
||||
// lead to redeclaration conflicts (yet).
|
||||
state_ = UNKNOWN;
|
||||
return Handle<Integer>();
|
||||
default:
|
||||
CHECK(state_ == UNKNOWN);
|
||||
break;
|
||||
}
|
||||
// Do the lookup in the object.
|
||||
return Handle<Integer>();
|
||||
}
|
||||
|
||||
private:
|
||||
State state_;
|
||||
};
|
||||
|
||||
|
||||
TEST(Reappearing) {
|
||||
v8::V8::Initialize();
|
||||
HandleScope scope(CcTest::isolate());
|
||||
|
||||
{ ReappearingPropertyContext context;
|
||||
context.Check("const x; var x = 0",
|
||||
0,
|
||||
3, // const declaration+initialization, var initialization
|
||||
3, // 2 x declaration + var initialization
|
||||
EXPECT_RESULT, Undefined(CcTest::isolate()));
|
||||
0, 0, EXPECT_RESULT, Number::New(CcTest::isolate(), 0));
|
||||
}
|
||||
}
|
||||
|
||||
@ -669,19 +530,13 @@ TEST(ExistsInHiddenPrototype) {
|
||||
HandleScope scope(CcTest::isolate());
|
||||
|
||||
{ ExistsInHiddenPrototypeContext context;
|
||||
context.Check("var x; x",
|
||||
1, // access
|
||||
0,
|
||||
2, // declaration + initialization
|
||||
EXPECT_EXCEPTION); // x is not defined!
|
||||
context.Check("var x; x", 0, 0, 0, EXPECT_RESULT,
|
||||
Undefined(CcTest::isolate()));
|
||||
}
|
||||
|
||||
{ ExistsInHiddenPrototypeContext context;
|
||||
context.Check("var x = 0; x",
|
||||
1, // access
|
||||
1, // initialization
|
||||
2, // declaration + initialization
|
||||
EXPECT_RESULT, Number::New(CcTest::isolate(), 0));
|
||||
context.Check("var x = 0; x", 0, 0, 0, EXPECT_RESULT,
|
||||
Number::New(CcTest::isolate(), 0));
|
||||
}
|
||||
|
||||
{ ExistsInHiddenPrototypeContext context;
|
||||
@ -694,20 +549,14 @@ TEST(ExistsInHiddenPrototype) {
|
||||
|
||||
// TODO(mstarzinger): The semantics of global const is vague.
|
||||
{ ExistsInHiddenPrototypeContext context;
|
||||
context.Check("const x; x",
|
||||
0,
|
||||
0,
|
||||
1, // (re-)declaration
|
||||
EXPECT_RESULT, Undefined(CcTest::isolate()));
|
||||
context.Check("const x; x", 0, 0, 0, EXPECT_RESULT,
|
||||
Undefined(CcTest::isolate()));
|
||||
}
|
||||
|
||||
// TODO(mstarzinger): The semantics of global const is vague.
|
||||
{ ExistsInHiddenPrototypeContext context;
|
||||
context.Check("const x = 0; x",
|
||||
0,
|
||||
0,
|
||||
1, // (re-)declaration
|
||||
EXPECT_RESULT, Number::New(CcTest::isolate(), 0));
|
||||
context.Check("const x = 0; x", 0, 0, 0, EXPECT_RESULT,
|
||||
Number::New(CcTest::isolate(), 0));
|
||||
}
|
||||
}
|
||||
|
||||
@ -768,10 +617,8 @@ TEST(CrossScriptReferences) {
|
||||
EXPECT_RESULT, Number::New(isolate, 1));
|
||||
context.Check("var x = 2; x",
|
||||
EXPECT_RESULT, Number::New(isolate, 2));
|
||||
context.Check("const x = 3; x",
|
||||
EXPECT_RESULT, Number::New(isolate, 3));
|
||||
context.Check("const x = 4; x",
|
||||
EXPECT_RESULT, Number::New(isolate, 4));
|
||||
context.Check("const x = 3; x", EXPECT_EXCEPTION);
|
||||
context.Check("const x = 4; x", EXPECT_EXCEPTION);
|
||||
context.Check("x = 5; x",
|
||||
EXPECT_RESULT, Number::New(isolate, 5));
|
||||
context.Check("var x = 6; x",
|
||||
@ -787,8 +634,7 @@ TEST(CrossScriptReferences) {
|
||||
EXPECT_RESULT, Number::New(isolate, 1));
|
||||
context.Check("var x = 2; x", // assignment ignored
|
||||
EXPECT_RESULT, Number::New(isolate, 1));
|
||||
context.Check("const x = 3; x",
|
||||
EXPECT_RESULT, Number::New(isolate, 1));
|
||||
context.Check("const x = 3; x", EXPECT_EXCEPTION);
|
||||
context.Check("x = 4; x", // assignment ignored
|
||||
EXPECT_RESULT, Number::New(isolate, 1));
|
||||
context.Check("var x = 5; x", // assignment ignored
|
||||
|
@ -36,29 +36,29 @@ function testIntroduceGlobal() {
|
||||
var source =
|
||||
// Deleting 'x' removes the local const property.
|
||||
"delete x;" +
|
||||
// Initialization turns into assignment to global 'x'.
|
||||
// Initialization redefines global 'x'.
|
||||
"const x = 3; assertEquals(3, x);" +
|
||||
// No constness of the global 'x'.
|
||||
"x = 4; assertEquals(4, x);";
|
||||
// Test constness of the global 'x'.
|
||||
"x = 4; assertEquals(3, x);";
|
||||
eval(source);
|
||||
}
|
||||
|
||||
testIntroduceGlobal();
|
||||
assertEquals(4, x);
|
||||
assertEquals("undefined", typeof x);
|
||||
|
||||
function testAssignExistingGlobal() {
|
||||
var source =
|
||||
// Delete 'x' to remove the local const property.
|
||||
"delete x;" +
|
||||
// Initialization turns into assignment to global 'x'.
|
||||
// Initialization redefines global 'x'.
|
||||
"const x = 5; assertEquals(5, x);" +
|
||||
// No constness of the global 'x'.
|
||||
"x = 6; assertEquals(6, x);";
|
||||
// Test constness of the global 'x'.
|
||||
"x = 6; assertEquals(5, x);";
|
||||
eval(source);
|
||||
}
|
||||
|
||||
testAssignExistingGlobal();
|
||||
assertEquals(6, x);
|
||||
assertEquals("undefined", typeof x);
|
||||
|
||||
function testAssignmentArgument(x) {
|
||||
function local() {
|
||||
@ -66,7 +66,7 @@ function testAssignmentArgument(x) {
|
||||
eval(source);
|
||||
}
|
||||
local();
|
||||
assertEquals(7, x);
|
||||
assertEquals("undefined", typeof x);
|
||||
}
|
||||
|
||||
for (var i = 0; i < 5; i++) {
|
||||
@ -74,17 +74,18 @@ for (var i = 0; i < 5; i++) {
|
||||
}
|
||||
%OptimizeFunctionOnNextCall(testAssignmentArgument);
|
||||
testAssignmentArgument();
|
||||
assertEquals(6, x);
|
||||
assertEquals("undefined", typeof x);
|
||||
|
||||
__defineSetter__('x', function() { throw 42; });
|
||||
function testAssignGlobalThrows() {
|
||||
// Initialization turns into assignment to global 'x' which
|
||||
// throws an exception.
|
||||
var source = "delete x; const x = 8";
|
||||
var finished = false;
|
||||
function testRedefineGlobal() {
|
||||
// Initialization redefines global 'x'.
|
||||
var source = "delete x; const x = 8; finished = true;";
|
||||
eval(source);
|
||||
}
|
||||
|
||||
assertThrows("testAssignGlobalThrows()");
|
||||
testRedefineGlobal();
|
||||
assertTrue(finished);
|
||||
|
||||
function testInitFastCaseExtension() {
|
||||
var source = "const x = 9; assertEquals(9, x); x = 10; assertEquals(9, x)";
|
||||
@ -111,7 +112,7 @@ function testAssignSurroundingContextSlot() {
|
||||
eval(source);
|
||||
}
|
||||
local();
|
||||
assertEquals(13, x);
|
||||
assertEquals(12, x);
|
||||
}
|
||||
|
||||
testAssignSurroundingContextSlot();
|
||||
|
@ -49,37 +49,6 @@ function TestLocal(s,e) {
|
||||
}
|
||||
|
||||
|
||||
// NOTE: TestGlobal usually only tests the given string in the context
|
||||
// of a global object in dictionary mode. This is because we use
|
||||
// delete to get rid of any added properties.
|
||||
function TestGlobal(s,e) {
|
||||
// Collect the global properties before the call.
|
||||
var properties = [];
|
||||
for (var key in this) properties.push(key);
|
||||
// Compute the result.
|
||||
var result;
|
||||
try {
|
||||
var code = s + (e ? "; $$$result=" + e : "");
|
||||
if (this.execScript) {
|
||||
execScript(code);
|
||||
} else {
|
||||
this.eval(code);
|
||||
}
|
||||
// Avoid issues if $$$result is not defined by
|
||||
// reading it through this.
|
||||
result = this.$$$result;
|
||||
} catch (x) {
|
||||
result = CheckException(x);
|
||||
}
|
||||
// Get rid of any introduced global properties before
|
||||
// returning the result.
|
||||
for (var key in this) {
|
||||
if (properties.indexOf(key) == -1) delete this[key];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
function TestContext(s,e) {
|
||||
try {
|
||||
// Use a with-statement to force the system to do dynamic
|
||||
@ -98,8 +67,6 @@ function TestAll(expected,s,opt_e) {
|
||||
var msg = s;
|
||||
if (opt_e) { e = opt_e; msg += "; " + opt_e; }
|
||||
assertEquals(expected, TestLocal(s,e), "local:'" + msg + "'");
|
||||
// Redeclarations of global consts do not throw, they are silently ignored.
|
||||
assertEquals(42, TestGlobal(s, 42), "global:'" + msg + "'");
|
||||
assertEquals(expected, TestContext(s,e), "context:'" + msg + "'");
|
||||
}
|
||||
|
||||
@ -112,7 +79,7 @@ function TestConflict(def0, def1) {
|
||||
// Eval first definition.
|
||||
TestAll("TypeError", 'eval("' + def0 +'"); ' + def1);
|
||||
// Eval second definition.
|
||||
TestAll("TypeError", def0 + '; eval("' + def1 + '")');
|
||||
TestAll("TypeError", def0 + '; eval("' + def1 +'")');
|
||||
// Eval both definitions separately.
|
||||
TestAll("TypeError", 'eval("' + def0 +'"); eval("' + def1 + '")');
|
||||
}
|
||||
@ -234,47 +201,26 @@ var undefined = 1; // Should be silently ignored.
|
||||
assertEquals(original_undef, undefined, "undefined got overwritten");
|
||||
undefined = original_undef;
|
||||
|
||||
var a; const a; const a = 1;
|
||||
assertEquals(1, a, "a has wrong value");
|
||||
a = 2;
|
||||
assertEquals(2, a, "a should be writable");
|
||||
|
||||
var b = 1; const b = 2;
|
||||
assertEquals(2, b, "b has wrong value");
|
||||
|
||||
var c = 1; const c = 2; const c = 3;
|
||||
assertEquals(3, c, "c has wrong value");
|
||||
|
||||
const d = 1; const d = 2;
|
||||
assertEquals(1, d, "d has wrong value");
|
||||
|
||||
const e = 1; var e = 2;
|
||||
const e = 1; eval('var e = 2');
|
||||
assertEquals(1, e, "e has wrong value");
|
||||
|
||||
const f = 1; const f;
|
||||
assertEquals(1, f, "f has wrong value");
|
||||
|
||||
var g; const g = 1;
|
||||
assertEquals(1, g, "g has wrong value");
|
||||
g = 2;
|
||||
assertEquals(2, g, "g should be writable");
|
||||
|
||||
const h; var h = 1;
|
||||
assertEquals(undefined,h, "h has wrong value");
|
||||
const h; eval('var h = 1');
|
||||
assertEquals(undefined, h, "h has wrong value");
|
||||
|
||||
eval("Object.defineProperty(this, 'i', { writable: true });"
|
||||
+ "const i = 7;"
|
||||
+ "assertEquals(7, i, \"i has wrong value\");");
|
||||
|
||||
var global = this;
|
||||
assertThrows(function() {
|
||||
Object.defineProperty(global, 'j', { writable: true })
|
||||
}, TypeError);
|
||||
const j = 2; // This is what makes the function above throw, because the
|
||||
// const declaration gets hoisted and makes the property non-configurable.
|
||||
Object.defineProperty(global, 'j', { value: 100, writable: true });
|
||||
assertEquals(100, j);
|
||||
// The const declaration stays configurable, so the declaration above goes
|
||||
// through even though the const declaration is hoisted above.
|
||||
const j = 2;
|
||||
assertEquals(2, j, "j has wrong value");
|
||||
|
||||
var k = 1; const k;
|
||||
// You could argue about the expected result here. For now, the winning
|
||||
// argument is that "const k;" is equivalent to "const k = undefined;".
|
||||
assertEquals(undefined, k, "k has wrong value");
|
||||
var k = 1;
|
||||
try { eval('const k'); } catch(e) { }
|
||||
assertEquals(1, k, "k has wrong value");
|
||||
try { eval('const k = 10'); } catch(e) { }
|
||||
assertEquals(1, k, "k has wrong value");
|
||||
|
@ -41,17 +41,20 @@ try { eval("var b"); } catch (e) { caught++; assertTrue(e instanceof TypeError);
|
||||
assertEquals(0, b);
|
||||
try { eval("var b = 1"); } catch (e) { caught++; assertTrue(e instanceof TypeError); }
|
||||
assertEquals(0, b);
|
||||
assertEquals(0, caught);
|
||||
|
||||
eval("var c");
|
||||
try { eval("const c"); } catch (e) { caught++; assertTrue(e instanceof TypeError); }
|
||||
assertTrue(typeof c == 'undefined');
|
||||
assertEquals(1, caught);
|
||||
try { eval("const c = 1"); } catch (e) { caught++; assertTrue(e instanceof TypeError); }
|
||||
assertEquals(1, c);
|
||||
assertEquals(undefined, c);
|
||||
assertEquals(2, caught);
|
||||
|
||||
eval("var d = 0");
|
||||
try { eval("const d"); } catch (e) { caught++; assertTrue(e instanceof TypeError); }
|
||||
assertEquals(undefined, d);
|
||||
assertEquals(0, d);
|
||||
assertEquals(3, caught);
|
||||
try { eval("const d = 1"); } catch (e) { caught++; assertTrue(e instanceof TypeError); }
|
||||
assertEquals(1, d);
|
||||
|
||||
assertEquals(0, caught);
|
||||
assertEquals(0, d);
|
||||
assertEquals(4, caught);
|
||||
|
@ -25,8 +25,6 @@
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Flags: --es52_globals
|
||||
|
||||
var setter_value = 0;
|
||||
|
||||
this.__defineSetter__("a", function(v) { setter_value = v; });
|
||||
@ -35,8 +33,9 @@ assertEquals(1, setter_value);
|
||||
assertFalse("value" in Object.getOwnPropertyDescriptor(this, "a"));
|
||||
|
||||
eval("with({}) { eval('var a = 2') }");
|
||||
assertEquals(2, setter_value);
|
||||
assertTrue("get" in Object.getOwnPropertyDescriptor(this, "a"));
|
||||
assertFalse("value" in Object.getOwnPropertyDescriptor(this, "a"));
|
||||
assertEquals(2, setter_value);
|
||||
|
||||
// Function declarations are treated specially to match Safari. We do
|
||||
// not call setters for them.
|
||||
@ -47,10 +46,8 @@ assertTrue("value" in Object.getOwnPropertyDescriptor(this, "a"));
|
||||
this.__defineSetter__("b", function(v) { setter_value = v; });
|
||||
try {
|
||||
eval("const b = 3");
|
||||
} catch(e) {
|
||||
assertUnreachable();
|
||||
}
|
||||
assertEquals(3, setter_value);
|
||||
} catch(e) { }
|
||||
assertEquals(2, setter_value);
|
||||
|
||||
try {
|
||||
eval("with({}) { eval('const b = 23') }");
|
||||
|
@ -37,4 +37,4 @@ try {
|
||||
assertTrue(e instanceof TypeError);
|
||||
caught = true;
|
||||
}
|
||||
assertFalse(caught);
|
||||
assertTrue(caught);
|
||||
|
@ -2,6 +2,6 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
__defineSetter__('x', function() { });
|
||||
Object.defineProperty(this, 'x', {set: function() { }});
|
||||
Object.freeze(this);
|
||||
eval('const x = 1');
|
||||
eval('"use strict"; x = 20;');
|
@ -642,10 +642,6 @@
|
||||
# We do not correctly handle assignments within "with"
|
||||
'ecma_3/Statements/12.10-01': [FAIL],
|
||||
|
||||
# We do not throw an exception when a const is redeclared.
|
||||
# (We only fail section 1 of the test.)
|
||||
'js1_5/Regress/regress-103602': [FAIL],
|
||||
|
||||
##################### MOZILLA EXTENSION TESTS #####################
|
||||
|
||||
'ecma/extensions/15.1.2.1-1': [FAIL_OK],
|
||||
|
@ -49,7 +49,7 @@ EXPAND_MACROS = [
|
||||
# to parse them!
|
||||
EXPECTED_FUNCTION_COUNT = 417
|
||||
EXPECTED_FUZZABLE_COUNT = 332
|
||||
EXPECTED_CCTEST_COUNT = 6
|
||||
EXPECTED_CCTEST_COUNT = 9
|
||||
EXPECTED_UNKNOWN_COUNT = 4
|
||||
EXPECTED_BUILTINS_COUNT = 810
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user