Merge V8 5.2.361.47 DO NOT MERGE
https://chromium.googlesource.com/v8/v8/+/5.2.361.47
FPIIM-449
Change-Id: Ibec421b85a9b88cb3a432ada642e469fe7e78346
(cherry picked from commit bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8)
diff --git a/src/interpreter/bytecode-generator.cc b/src/interpreter/bytecode-generator.cc
index b0fa245..650234a 100644
--- a/src/interpreter/bytecode-generator.cc
+++ b/src/interpreter/bytecode-generator.cc
@@ -5,6 +5,7 @@
#include "src/interpreter/bytecode-generator.h"
#include "src/ast/scopes.h"
+#include "src/code-stubs.h"
#include "src/compiler.h"
#include "src/interpreter/bytecode-register-allocator.h"
#include "src/interpreter/control-flow-builders.h"
@@ -554,38 +555,41 @@
Register result_register_;
};
-BytecodeGenerator::BytecodeGenerator(Isolate* isolate, Zone* zone)
- : isolate_(isolate),
- zone_(zone),
- builder_(nullptr),
- info_(nullptr),
- scope_(nullptr),
- globals_(0, zone),
+BytecodeGenerator::BytecodeGenerator(CompilationInfo* info)
+ : isolate_(info->isolate()),
+ zone_(info->zone()),
+ builder_(new (zone()) BytecodeArrayBuilder(
+ info->isolate(), info->zone(), info->num_parameters_including_this(),
+ info->scope()->MaxNestedContextChainLength(),
+ info->scope()->num_stack_slots(), info->literal())),
+ info_(info),
+ scope_(info->scope()),
+ globals_(0, info->zone()),
execution_control_(nullptr),
execution_context_(nullptr),
execution_result_(nullptr),
register_allocator_(nullptr),
+ generator_resume_points_(info->literal()->yield_count(), info->zone()),
+ generator_state_(),
try_catch_nesting_level_(0),
try_finally_nesting_level_(0) {
- InitializeAstVisitor(isolate);
+ InitializeAstVisitor(isolate());
}
-Handle<BytecodeArray> BytecodeGenerator::MakeBytecode(CompilationInfo* info) {
- set_info(info);
- set_scope(info->scope());
-
- // Initialize bytecode array builder.
- set_builder(new (zone()) BytecodeArrayBuilder(
- isolate(), zone(), info->num_parameters_including_this(),
- scope()->MaxNestedContextChainLength(), scope()->num_stack_slots(),
- info->literal()));
-
+Handle<BytecodeArray> BytecodeGenerator::MakeBytecode() {
// Initialize the incoming context.
ContextScope incoming_context(this, scope(), false);
// Initialize control scope.
ControlScopeForTopLevel control(this);
+ RegisterAllocationScope register_scope(this);
+
+ if (IsGeneratorFunction(info()->literal()->kind())) {
+ generator_state_ = register_allocator()->NewRegister();
+ VisitGeneratorPrologue();
+ }
+
// Build function context only if there are context allocated variables.
if (scope()->NeedsContext()) {
// Push a new inner context scope for the function.
@@ -597,9 +601,15 @@
MakeBytecodeBody();
}
+ // In generator functions, we may not have visited every yield in the AST
+ // since we skip some obviously dead code. Hence the generated bytecode may
+ // contain jumps to unbound labels (resume points that will never be used).
+ // We bind these now.
+ for (auto& label : generator_resume_points_) {
+ if (!label.is_bound()) builder()->Bind(&label);
+ }
+
builder()->EnsureReturn();
- set_scope(nullptr);
- set_info(nullptr);
return builder()->ToBytecodeArray();
}
@@ -628,12 +638,90 @@
VisitDeclarations(scope()->declarations());
// Perform a stack-check before the body.
- builder()->StackCheck();
+ builder()->StackCheck(info()->literal()->start_position());
// Visit statements in the function body.
VisitStatements(info()->literal()->body());
}
+void BytecodeGenerator::BuildIndexedJump(Register index, size_t start_index,
+ size_t size,
+ ZoneVector<BytecodeLabel>& targets) {
+ // TODO(neis): Optimize this by using a proper jump table.
+ for (size_t i = start_index; i < start_index + size; i++) {
+ DCHECK(0 <= i && i < targets.size());
+ builder()
+ ->LoadLiteral(Smi::FromInt(static_cast<int>(i)))
+ .CompareOperation(Token::Value::EQ_STRICT, index)
+ .JumpIfTrue(&(targets[i]));
+ }
+
+ RegisterAllocationScope register_scope(this);
+ Register reason = register_allocator()->NewRegister();
+ BailoutReason bailout_reason = BailoutReason::kInvalidJumpTableIndex;
+ builder()
+ ->LoadLiteral(Smi::FromInt(static_cast<int>(bailout_reason)))
+ .StoreAccumulatorInRegister(reason)
+ .CallRuntime(Runtime::kAbort, reason, 1);
+}
+
+void BytecodeGenerator::VisitIterationHeader(IterationStatement* stmt,
+ LoopBuilder* loop_builder) {
+ // Recall that stmt->yield_count() is always zero inside ordinary
+ // (i.e. non-generator) functions.
+
+ // Collect all labels for generator resume points within the loop (if any) so
+ // that they can be bound to the loop header below. Also create fresh labels
+ // for these resume points, to be used inside the loop.
+ ZoneVector<BytecodeLabel> resume_points_in_loop(zone());
+ size_t first_yield = stmt->first_yield_id();
+ for (size_t id = first_yield; id < first_yield + stmt->yield_count(); id++) {
+ DCHECK(0 <= id && id < generator_resume_points_.size());
+ auto& label = generator_resume_points_[id];
+ resume_points_in_loop.push_back(label);
+ generator_resume_points_[id] = BytecodeLabel();
+ }
+
+ loop_builder->LoopHeader(&resume_points_in_loop);
+
+ if (stmt->yield_count() > 0) {
+ // If we are not resuming, fall through to loop body.
+ // If we are resuming, perform state dispatch.
+ BytecodeLabel not_resuming;
+ builder()
+ ->LoadLiteral(Smi::FromInt(JSGeneratorObject::kGeneratorExecuting))
+ .CompareOperation(Token::Value::EQ, generator_state_)
+ .JumpIfTrue(¬_resuming);
+ BuildIndexedJump(generator_state_, first_yield,
+ stmt->yield_count(), generator_resume_points_);
+ builder()->Bind(¬_resuming);
+ }
+}
+
+void BytecodeGenerator::VisitGeneratorPrologue() {
+ // The generator resume trampoline abuses the new.target register both to
+ // indicate that this is a resume call and to pass in the generator object.
+ // In ordinary calls, new.target is always undefined because generator
+ // functions are non-constructable.
+ Register generator_object = Register::new_target();
+ BytecodeLabel regular_call;
+ builder()
+ ->LoadAccumulatorWithRegister(generator_object)
+ .JumpIfUndefined(®ular_call);
+
+ // This is a resume call. Restore registers and perform state dispatch.
+ // (The current context has already been restored by the trampoline.)
+ builder()
+ ->ResumeGenerator(generator_object)
+ .StoreAccumulatorInRegister(generator_state_);
+ BuildIndexedJump(generator_state_, 0, generator_resume_points_.size(),
+ generator_resume_points_);
+
+ builder()->Bind(®ular_call);
+ // This is a regular call. Fall through to the ordinary function prologue,
+ // after which we will run into the generator object creation and the initial
+ // yield (both inserted by the parser).
+}
void BytecodeGenerator::VisitBlock(Block* stmt) {
// Visit declarations and statements.
@@ -663,17 +751,14 @@
VariableMode mode = decl->mode();
// Const and let variables are initialized with the hole so that we can
// check that they are only assigned once.
- bool hole_init = mode == CONST || mode == CONST_LEGACY || mode == LET;
+ bool hole_init = mode == CONST || mode == LET;
switch (variable->location()) {
case VariableLocation::GLOBAL:
- case VariableLocation::UNALLOCATED: {
- Handle<Oddball> value = variable->binding_needs_init()
- ? isolate()->factory()->the_hole_value()
- : isolate()->factory()->undefined_value();
+ case VariableLocation::UNALLOCATED:
+ DCHECK(!variable->binding_needs_init());
globals()->push_back(variable->name());
- globals()->push_back(value);
+ globals()->push_back(isolate()->factory()->undefined_value());
break;
- }
case VariableLocation::LOCAL:
if (hole_init) {
Register destination(variable->index());
@@ -793,9 +878,7 @@
Handle<FixedArray> data = isolate()->factory()->NewFixedArray(
static_cast<int>(globals()->size()), TENURED);
for (Handle<Object> obj : *globals()) data->set(array_index++, *obj);
- int encoded_flags = DeclareGlobalsEvalFlag::encode(info()->is_eval()) |
- DeclareGlobalsNativeFlag::encode(info()->is_native()) |
- DeclareGlobalsLanguageMode::encode(language_mode());
+ int encoded_flags = info()->GetDeclareGlobalsFlags();
Register pairs = register_allocator()->NewRegister();
builder()->LoadLiteral(data);
@@ -955,23 +1038,21 @@
void BytecodeGenerator::VisitIterationBody(IterationStatement* stmt,
LoopBuilder* loop_builder) {
ControlScopeForIteration execution_control(this, stmt, loop_builder);
- builder()->StackCheck();
+ builder()->StackCheck(stmt->position());
Visit(stmt->body());
+ loop_builder->SetContinueTarget();
}
void BytecodeGenerator::VisitDoWhileStatement(DoWhileStatement* stmt) {
LoopBuilder loop_builder(builder());
- loop_builder.LoopHeader();
+ VisitIterationHeader(stmt, &loop_builder);
if (stmt->cond()->ToBooleanIsFalse()) {
VisitIterationBody(stmt, &loop_builder);
- loop_builder.Condition();
} else if (stmt->cond()->ToBooleanIsTrue()) {
- loop_builder.Condition();
VisitIterationBody(stmt, &loop_builder);
loop_builder.JumpToHeader();
} else {
VisitIterationBody(stmt, &loop_builder);
- loop_builder.Condition();
builder()->SetExpressionAsStatementPosition(stmt->cond());
VisitForAccumulatorValue(stmt->cond());
loop_builder.JumpToHeaderIfTrue();
@@ -986,8 +1067,7 @@
}
LoopBuilder loop_builder(builder());
- loop_builder.LoopHeader();
- loop_builder.Condition();
+ VisitIterationHeader(stmt, &loop_builder);
if (!stmt->cond()->ToBooleanIsTrue()) {
builder()->SetExpressionAsStatementPosition(stmt->cond());
VisitForAccumulatorValue(stmt->cond());
@@ -1010,8 +1090,7 @@
}
LoopBuilder loop_builder(builder());
- loop_builder.LoopHeader();
- loop_builder.Condition();
+ VisitIterationHeader(stmt, &loop_builder);
if (stmt->cond() && !stmt->cond()->ToBooleanIsTrue()) {
builder()->SetExpressionAsStatementPosition(stmt->cond());
VisitForAccumulatorValue(stmt->cond());
@@ -1019,7 +1098,6 @@
}
VisitIterationBody(stmt, &loop_builder);
if (stmt->next() != nullptr) {
- loop_builder.Next();
builder()->SetStatementPosition(stmt->next());
Visit(stmt->next());
}
@@ -1135,9 +1213,8 @@
builder()->StoreAccumulatorInRegister(index);
// The loop
- loop_builder.LoopHeader();
+ VisitIterationHeader(stmt, &loop_builder);
builder()->SetExpressionAsStatementPosition(stmt->each());
- loop_builder.Condition();
builder()->ForInDone(index, cache_length);
loop_builder.BreakIfTrue();
DCHECK(Register::AreContiguous(cache_type, cache_array));
@@ -1146,7 +1223,6 @@
loop_builder.ContinueIfUndefined();
VisitForInAssignment(stmt->each(), stmt->EachFeedbackSlot());
VisitIterationBody(stmt, &loop_builder);
- loop_builder.Next();
builder()->ForInStep(index);
builder()->StoreAccumulatorInRegister(index);
loop_builder.JumpToHeader();
@@ -1160,10 +1236,10 @@
LoopBuilder loop_builder(builder());
ControlScopeForIteration control_scope(this, stmt, &loop_builder);
+ builder()->SetExpressionAsStatementPosition(stmt->assign_iterator());
VisitForEffect(stmt->assign_iterator());
- loop_builder.LoopHeader();
- loop_builder.Next();
+ VisitIterationHeader(stmt, &loop_builder);
builder()->SetExpressionAsStatementPosition(stmt->next_result());
VisitForEffect(stmt->next_result());
VisitForAccumulatorValue(stmt->result_done());
@@ -1324,7 +1400,7 @@
.StoreAccumulatorInRegister(prototype);
VisitClassLiteralProperties(expr, literal, prototype);
- builder()->CallRuntime(Runtime::kFinalizeClassDefinition, literal, 2);
+ builder()->CallRuntime(Runtime::kToFastProperties, literal, 1);
// Assign to class variable.
if (expr->class_variable_proxy() != nullptr) {
Variable* var = expr->class_variable_proxy()->var();
@@ -1514,10 +1590,21 @@
void BytecodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
- // Deep-copy the literal boilerplate.
+ // Copy the literal boilerplate.
+ int fast_clone_properties_count = 0;
+ if (FastCloneShallowObjectStub::IsSupported(expr)) {
+ STATIC_ASSERT(
+ FastCloneShallowObjectStub::kMaximumClonedProperties <=
+ 1 << CreateObjectLiteralFlags::FastClonePropertiesCountBits::kShift);
+ fast_clone_properties_count =
+ FastCloneShallowObjectStub::PropertiesCount(expr->properties_count());
+ }
+ uint8_t flags =
+ CreateObjectLiteralFlags::FlagsBits::encode(expr->ComputeFlags()) |
+ CreateObjectLiteralFlags::FastClonePropertiesCountBits::encode(
+ fast_clone_properties_count);
builder()->CreateObjectLiteral(expr->constant_properties(),
- expr->literal_index(),
- expr->ComputeFlags(true));
+ expr->literal_index(), flags);
// Allocate in the outer scope since this register is used to return the
// expression's results to the caller.
@@ -1753,10 +1840,7 @@
void BytecodeGenerator::BuildHoleCheckForVariableLoad(VariableMode mode,
Handle<String> name) {
- if (mode == CONST_LEGACY) {
- BytecodeLabel end_label;
- builder()->JumpIfNotHole(&end_label).LoadUndefined().Bind(&end_label);
- } else if (mode == LET || mode == CONST) {
+ if (mode == LET || mode == CONST) {
BuildThrowIfHole(name);
}
}
@@ -1940,7 +2024,7 @@
RegisterAllocationScope assignment_register_scope(this);
BytecodeLabel end_label;
bool hole_check_required =
- (mode == CONST_LEGACY) || (mode == LET && op != Token::INIT) ||
+ (mode == LET && op != Token::INIT) ||
(mode == CONST && op != Token::INIT) ||
(mode == CONST && op == Token::INIT && variable->is_this());
switch (variable->location()) {
@@ -1953,6 +2037,16 @@
destination = Register(variable->index());
}
+ if (mode == CONST_LEGACY && op != Token::INIT) {
+ if (is_strict(language_mode())) {
+ builder()->CallRuntime(Runtime::kThrowConstAssignError, Register(),
+ 0);
+ }
+ // Non-initializing assignments to legacy constants are ignored
+ // in sloppy mode. Break here to avoid storing into variable.
+ break;
+ }
+
if (hole_check_required) {
// Load destination to check for hole.
Register value_temp = register_allocator()->NewRegister();
@@ -1960,28 +2054,9 @@
->StoreAccumulatorInRegister(value_temp)
.LoadAccumulatorWithRegister(destination);
- if (mode == CONST_LEGACY && op == Token::INIT) {
- // Perform an intialization check for legacy constants.
- builder()
- ->JumpIfNotHole(&end_label)
- .MoveRegister(value_temp, destination)
- .Bind(&end_label)
- .LoadAccumulatorWithRegister(value_temp);
- // Break here because the value should not be stored unconditionally.
- break;
- } else if (mode == CONST_LEGACY && op != Token::INIT) {
- DCHECK(!is_strict(language_mode()));
- // Ensure accumulator is in the correct state.
- builder()->LoadAccumulatorWithRegister(value_temp);
- // Break here, non-initializing assignments to legacy constants are
- // ignored.
- break;
- } else {
- BuildHoleCheckForVariableAssignment(variable, op);
- builder()->LoadAccumulatorWithRegister(value_temp);
- }
+ BuildHoleCheckForVariableAssignment(variable, op);
+ builder()->LoadAccumulatorWithRegister(value_temp);
}
-
builder()->StoreAccumulatorInRegister(destination);
break;
}
@@ -2018,6 +2093,16 @@
builder()->LoadAccumulatorWithRegister(value_temp);
}
+ if (mode == CONST_LEGACY && op != Token::INIT) {
+ if (is_strict(language_mode())) {
+ builder()->CallRuntime(Runtime::kThrowConstAssignError, Register(),
+ 0);
+ }
+ // Non-initializing assignments to legacy constants are ignored
+ // in sloppy mode. Break here to avoid storing into variable.
+ break;
+ }
+
if (hole_check_required) {
// Load destination to check for hole.
Register value_temp = register_allocator()->NewRegister();
@@ -2025,55 +2110,16 @@
->StoreAccumulatorInRegister(value_temp)
.LoadContextSlot(context_reg, variable->index());
- if (mode == CONST_LEGACY && op == Token::INIT) {
- // Perform an intialization check for legacy constants.
- builder()
- ->JumpIfNotHole(&end_label)
- .LoadAccumulatorWithRegister(value_temp)
- .StoreContextSlot(context_reg, variable->index())
- .Bind(&end_label);
- builder()->LoadAccumulatorWithRegister(value_temp);
- // Break here because the value should not be stored unconditionally.
- // The above code performs the store conditionally.
- break;
- } else if (mode == CONST_LEGACY && op != Token::INIT) {
- DCHECK(!is_strict(language_mode()));
- // Ensure accumulator is in the correct state.
- builder()->LoadAccumulatorWithRegister(value_temp);
- // Break here, non-initializing assignments to legacy constants are
- // ignored.
- break;
- } else {
- BuildHoleCheckForVariableAssignment(variable, op);
- builder()->LoadAccumulatorWithRegister(value_temp);
- }
+ BuildHoleCheckForVariableAssignment(variable, op);
+ builder()->LoadAccumulatorWithRegister(value_temp);
}
builder()->StoreContextSlot(context_reg, variable->index());
break;
}
case VariableLocation::LOOKUP: {
- if (mode == CONST_LEGACY && op == Token::INIT) {
- register_allocator()->PrepareForConsecutiveAllocations(3);
- Register value = register_allocator()->NextConsecutiveRegister();
- Register context = register_allocator()->NextConsecutiveRegister();
- Register name = register_allocator()->NextConsecutiveRegister();
-
- // InitializeLegacyConstLookupSlot runtime call returns the 'value'
- // passed to it. So, accumulator will have its original contents when
- // runtime call returns.
- builder()
- ->StoreAccumulatorInRegister(value)
- .MoveRegister(execution_context()->reg(), context)
- .LoadLiteral(variable->name())
- .StoreAccumulatorInRegister(name)
- .CallRuntime(Runtime::kInitializeLegacyConstLookupSlot, value, 3);
- } else if (mode == CONST_LEGACY && op != Token::INIT) {
- // Non-intializing assignments to legacy constants are ignored.
- DCHECK(!is_strict(language_mode()));
- } else {
- builder()->StoreLookupSlot(variable->name(), language_mode());
- }
+ DCHECK_NE(CONST_LEGACY, variable->mode());
+ builder()->StoreLookupSlot(variable->name(), language_mode());
break;
}
}
@@ -2224,16 +2270,86 @@
execution_result()->SetResultInAccumulator();
}
+void BytecodeGenerator::VisitYield(Yield* expr) {
+ builder()->SetExpressionPosition(expr);
+ Register value = VisitForRegisterValue(expr->expression());
-void BytecodeGenerator::VisitYield(Yield* expr) { UNIMPLEMENTED(); }
+ Register generator = VisitForRegisterValue(expr->generator_object());
+ // Save context, registers, and state. Then return.
+ builder()
+ ->LoadLiteral(Smi::FromInt(expr->yield_id()))
+ .SuspendGenerator(generator)
+ .LoadAccumulatorWithRegister(value)
+ .Return(); // Hard return (ignore any finally blocks).
+
+ builder()->Bind(&(generator_resume_points_[expr->yield_id()]));
+ // Upon resume, we continue here.
+
+ {
+ RegisterAllocationScope register_scope(this);
+
+ // Update state to indicate that we have finished resuming. Loop headers
+ // rely on this.
+ builder()
+ ->LoadLiteral(Smi::FromInt(JSGeneratorObject::kGeneratorExecuting))
+ .StoreAccumulatorInRegister(generator_state_);
+
+ Register input = register_allocator()->NewRegister();
+ builder()
+ ->CallRuntime(Runtime::kGeneratorGetInput, generator, 1)
+ .StoreAccumulatorInRegister(input);
+
+ Register resume_mode = register_allocator()->NewRegister();
+ builder()
+ ->CallRuntime(Runtime::kGeneratorGetResumeMode, generator, 1)
+ .StoreAccumulatorInRegister(resume_mode);
+
+ // Now dispatch on resume mode.
+
+ BytecodeLabel resume_with_next;
+ BytecodeLabel resume_with_return;
+ BytecodeLabel resume_with_throw;
+
+ builder()
+ ->LoadLiteral(Smi::FromInt(JSGeneratorObject::kNext))
+ .CompareOperation(Token::EQ_STRICT, resume_mode)
+ .JumpIfTrue(&resume_with_next)
+ .LoadLiteral(Smi::FromInt(JSGeneratorObject::kThrow))
+ .CompareOperation(Token::EQ_STRICT, resume_mode)
+ .JumpIfTrue(&resume_with_throw)
+ .Jump(&resume_with_return);
+
+ builder()->Bind(&resume_with_return);
+ {
+ register_allocator()->PrepareForConsecutiveAllocations(2);
+ Register value = register_allocator()->NextConsecutiveRegister();
+ Register done = register_allocator()->NextConsecutiveRegister();
+ builder()
+ ->MoveRegister(input, value)
+ .LoadTrue()
+ .StoreAccumulatorInRegister(done)
+ .CallRuntime(Runtime::kCreateIterResultObject, value, 2);
+ execution_control()->ReturnAccumulator();
+ }
+
+ builder()->Bind(&resume_with_throw);
+ builder()
+ ->LoadAccumulatorWithRegister(input)
+ .Throw();
+
+ builder()->Bind(&resume_with_next);
+ builder()->LoadAccumulatorWithRegister(input);
+ }
+ execution_result()->SetResultInAccumulator();
+}
void BytecodeGenerator::VisitThrow(Throw* expr) {
VisitForAccumulatorValue(expr->exception());
builder()->SetExpressionPosition(expr);
builder()->Throw();
- // Throw statments are modeled as expression instead of statments. These are
- // converted from assignment statements in Rewriter::ReWrite pass. An
+ // Throw statements are modeled as expressions instead of statements. These
+ // are converted from assignment statements in Rewriter::ReWrite pass. An
// assignment statement expects a value in the accumulator. This is a hack to
// avoid DCHECK fails assert accumulator has been set.
execution_result()->SetResultInAccumulator();
@@ -2452,12 +2568,14 @@
// callee value.
if (call_type == Call::POSSIBLY_EVAL_CALL && args->length() > 0) {
RegisterAllocationScope inner_register_scope(this);
- register_allocator()->PrepareForConsecutiveAllocations(5);
+ register_allocator()->PrepareForConsecutiveAllocations(6);
Register callee_for_eval = register_allocator()->NextConsecutiveRegister();
Register source = register_allocator()->NextConsecutiveRegister();
Register function = register_allocator()->NextConsecutiveRegister();
Register language = register_allocator()->NextConsecutiveRegister();
- Register position = register_allocator()->NextConsecutiveRegister();
+ Register eval_scope_position =
+ register_allocator()->NextConsecutiveRegister();
+ Register eval_position = register_allocator()->NextConsecutiveRegister();
// Set up arguments for ResolvePossiblyDirectEval by copying callee, source
// strings and function closure, and loading language and
@@ -2470,11 +2588,13 @@
.StoreAccumulatorInRegister(language)
.LoadLiteral(
Smi::FromInt(execution_context()->scope()->start_position()))
- .StoreAccumulatorInRegister(position);
+ .StoreAccumulatorInRegister(eval_scope_position)
+ .LoadLiteral(Smi::FromInt(expr->position()))
+ .StoreAccumulatorInRegister(eval_position);
// Call ResolvePossiblyDirectEval and modify the callee.
builder()
- ->CallRuntime(Runtime::kResolvePossiblyDirectEval, callee_for_eval, 5)
+ ->CallRuntime(Runtime::kResolvePossiblyDirectEval, callee_for_eval, 6)
.StoreAccumulatorInRegister(callee);
}
@@ -2739,13 +2859,12 @@
}
}
- // Convert old value into a number.
- builder()->CastAccumulatorToNumber();
-
// Save result for postfix expressions.
if (is_postfix) {
old_value = register_allocator()->outer()->NewRegister();
- builder()->StoreAccumulatorInRegister(old_value);
+
+ // Convert old value into a number before saving it.
+ builder()->CastAccumulatorToNumber().StoreAccumulatorInRegister(old_value);
}
// Perform +1/-1 operation.