[es6] Spread in array literals

This allows you to put iterables into your array literals
and the will get spread into the array.

  let x = [0, ...range(1, 3)];  // [0, 1, 2]

This is done by treating the array literal up to the first
spread element as usual, including using a boiler plate
array, and then appending the remaining expressions and rest
expressions.

BUG=v8:3018
LOG=N

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

Cr-Commit-Position: refs/heads/master@{#28534}
This commit is contained in:
arv 2015-05-21 01:09:06 -07:00 committed by Commit bot
parent e56585077b
commit 9502e91adb
23 changed files with 547 additions and 49 deletions

View File

@ -1893,8 +1893,11 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) {
// Emit code to evaluate all the non-constant subexpressions and to store
// them into the newly cloned array.
for (int i = 0; i < length; i++) {
Expression* subexpr = subexprs->at(i);
int array_index = 0;
for (; array_index < length; array_index++) {
Expression* subexpr = subexprs->at(array_index);
if (subexpr->IsSpread()) break;
// If the subexpression is a literal or a simple materialized literal it
// is already set in the cloned array.
if (CompileTimeValue::IsCompileTimeValue(subexpr)) continue;
@ -1907,7 +1910,7 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) {
VisitForAccumulatorValue(subexpr);
if (has_fast_elements) {
int offset = FixedArray::kHeaderSize + (i * kPointerSize);
int offset = FixedArray::kHeaderSize + (array_index * kPointerSize);
__ ldr(r6, MemOperand(sp, kPointerSize)); // Copy of array literal.
__ ldr(r1, FieldMemOperand(r6, JSObject::kElementsOffset));
__ str(result_register(), FieldMemOperand(r1, offset));
@ -1916,12 +1919,37 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) {
kLRHasBeenSaved, kDontSaveFPRegs,
EMIT_REMEMBERED_SET, INLINE_SMI_CHECK);
} else {
__ mov(r3, Operand(Smi::FromInt(i)));
__ mov(r3, Operand(Smi::FromInt(array_index)));
StoreArrayLiteralElementStub stub(isolate());
__ CallStub(&stub);
}
PrepareForBailoutForId(expr->GetIdForElement(i), NO_REGISTERS);
PrepareForBailoutForId(expr->GetIdForElement(array_index), NO_REGISTERS);
}
// In case the array literal contains spread expressions it has two parts. The
// first part is the "static" array which has a literal index is handled
// above. The second part is the part after the first spread expression
// (inclusive) and these elements gets appended to the array. Note that the
// number elements an iterable produces is unknown ahead of time.
if (array_index < length && result_saved) {
__ pop(); // literal index
__ Pop(r0);
result_saved = false;
}
for (; array_index < length; array_index++) {
Expression* subexpr = subexprs->at(array_index);
__ Push(r0);
if (subexpr->IsSpread()) {
VisitForStackValue(subexpr->AsSpread()->expression());
__ InvokeBuiltin(Builtins::CONCAT_ITERABLE_TO_ARRAY, CALL_FUNCTION);
} else {
VisitForStackValue(subexpr);
__ CallRuntime(Runtime::kAppendElement, 2);
}
PrepareForBailoutForId(expr->GetIdForElement(array_index), NO_REGISTERS);
}
if (result_saved) {

View File

@ -1864,8 +1864,11 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) {
// Emit code to evaluate all the non-constant subexpressions and to store
// them into the newly cloned array.
for (int i = 0; i < length; i++) {
Expression* subexpr = subexprs->at(i);
int array_index = 0;
for (; array_index < length; array_index++) {
Expression* subexpr = subexprs->at(array_index);
if (subexpr->IsSpread()) break;
// If the subexpression is a literal or a simple materialized literal it
// is already set in the cloned array.
if (CompileTimeValue::IsCompileTimeValue(subexpr)) continue;
@ -1878,7 +1881,7 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) {
VisitForAccumulatorValue(subexpr);
if (has_fast_elements) {
int offset = FixedArray::kHeaderSize + (i * kPointerSize);
int offset = FixedArray::kHeaderSize + (array_index * kPointerSize);
__ Peek(x6, kPointerSize); // Copy of array literal.
__ Ldr(x1, FieldMemOperand(x6, JSObject::kElementsOffset));
__ Str(result_register(), FieldMemOperand(x1, offset));
@ -1887,12 +1890,37 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) {
kLRHasBeenSaved, kDontSaveFPRegs,
EMIT_REMEMBERED_SET, INLINE_SMI_CHECK);
} else {
__ Mov(x3, Smi::FromInt(i));
__ Mov(x3, Smi::FromInt(array_index));
StoreArrayLiteralElementStub stub(isolate());
__ CallStub(&stub);
}
PrepareForBailoutForId(expr->GetIdForElement(i), NO_REGISTERS);
PrepareForBailoutForId(expr->GetIdForElement(array_index), NO_REGISTERS);
}
// In case the array literal contains spread expressions it has two parts. The
// first part is the "static" array which has a literal index is handled
// above. The second part is the part after the first spread expression
// (inclusive) and these elements gets appended to the array. Note that the
// number elements an iterable produces is unknown ahead of time.
if (array_index < length && result_saved) {
__ Drop(1); // literal index
__ Pop(x0);
result_saved = false;
}
for (; array_index < length; array_index++) {
Expression* subexpr = subexprs->at(array_index);
__ Push(x0);
if (subexpr->IsSpread()) {
VisitForStackValue(subexpr->AsSpread()->expression());
__ InvokeBuiltin(Builtins::CONCAT_ITERABLE_TO_ARRAY, CALL_FUNCTION);
} else {
VisitForStackValue(subexpr);
__ CallRuntime(Runtime::kAppendElement, 2);
}
PrepareForBailoutForId(expr->GetIdForElement(array_index), NO_REGISTERS);
}
if (result_saved) {

View File

@ -324,7 +324,11 @@ void AstNumberingVisitor::VisitCompareOperation(CompareOperation* node) {
}
void AstNumberingVisitor::VisitSpread(Spread* node) { UNREACHABLE(); }
void AstNumberingVisitor::VisitSpread(Spread* node) {
IncrementNodeCount();
DisableOptimization(kSpread);
Visit(node->expression());
}
void AstNumberingVisitor::VisitForInStatement(ForInStatement* node) {

View File

@ -386,8 +386,10 @@ void ArrayLiteral::BuildConstantElements(Isolate* isolate) {
bool is_simple = true;
int depth_acc = 1;
bool is_holey = false;
for (int i = 0, n = values()->length(); i < n; i++) {
Expression* element = values()->at(i);
int array_index = 0;
for (int n = values()->length(); array_index < n; array_index++) {
Expression* element = values()->at(array_index);
if (element->IsSpread()) break;
MaterializedLiteral* m_literal = element->AsMaterializedLiteral();
if (m_literal != NULL) {
m_literal->BuildConstants(isolate);
@ -400,18 +402,24 @@ void ArrayLiteral::BuildConstantElements(Isolate* isolate) {
is_holey = true;
} else if (boilerplate_value->IsUninitialized()) {
is_simple = false;
JSObject::SetOwnElement(
array, i, handle(Smi::FromInt(0), isolate), SLOPPY).Assert();
JSObject::SetOwnElement(array, array_index,
handle(Smi::FromInt(0), isolate),
SLOPPY).Assert();
} else {
JSObject::SetOwnElement(array, i, boilerplate_value, SLOPPY).Assert();
JSObject::SetOwnElement(array, array_index, boilerplate_value, SLOPPY)
.Assert();
}
}
if (array_index != values()->length()) {
JSArray::SetElementsLength(
array, handle(Smi::FromInt(array_index), isolate)).Assert();
}
Handle<FixedArrayBase> element_values(array->elements());
// Simple and shallow arrays can be lazily copied, we transform the
// elements array to a copy-on-write array.
if (is_simple && depth_acc == 1 && values()->length() > 0 &&
if (is_simple && depth_acc == 1 && array_index > 0 &&
array->HasFastSmiOrObjectElements()) {
element_values->set_map(isolate->heap()->fixed_cow_array_map());
}

View File

@ -210,6 +210,7 @@ namespace internal {
V(kScriptContext, "Allocation of script context") \
V(kSmiAdditionOverflow, "Smi addition overflow") \
V(kSmiSubtractionOverflow, "Smi subtraction overflow") \
V(kSpread, "Spread in array literal") \
V(kStackAccessBelowStackPointer, "Stack access below stack pointer") \
V(kStackFrameTypesMustMatch, "Stack frame types must match") \
V(kSuperReference, "Super reference") \

View File

@ -1746,6 +1746,7 @@ EMPTY_NATIVE_FUNCTIONS_FOR_FEATURE(harmony_reflect)
EMPTY_NATIVE_FUNCTIONS_FOR_FEATURE(harmony_spreadcalls)
EMPTY_NATIVE_FUNCTIONS_FOR_FEATURE(harmony_destructuring)
EMPTY_NATIVE_FUNCTIONS_FOR_FEATURE(harmony_object)
EMPTY_NATIVE_FUNCTIONS_FOR_FEATURE(harmony_spread_arrays)
void Genesis::InstallNativeFunctions_harmony_proxies() {
@ -1776,6 +1777,7 @@ EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_rest_parameters)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_spreadcalls)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_destructuring)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_object)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_spread_arrays)
void Genesis::InitializeGlobal_harmony_regexps() {
Handle<JSObject> builtins(native_context()->builtins());
@ -2401,6 +2403,7 @@ bool Genesis::InstallExperimentalNatives() {
static const char* harmony_destructuring_natives[] = {nullptr};
static const char* harmony_object_natives[] = {"native harmony-object.js",
NULL};
static const char* harmony_spread_arrays_natives[] = {nullptr};
for (int i = ExperimentalNatives::GetDebuggerCount();
i < ExperimentalNatives::GetBuiltinsCount(); i++) {

View File

@ -211,6 +211,7 @@ enum BuiltinExtraArguments {
V(APPLY_PREPARE, 1) \
V(REFLECT_APPLY_PREPARE, 1) \
V(REFLECT_CONSTRUCT_PREPARE, 2) \
V(CONCAT_ITERABLE_TO_ARRAY, 1) \
V(STACK_OVERFLOW, 1)
class BuiltinFunctionTable;

View File

@ -1927,23 +1927,64 @@ void AstGraphBuilder::VisitArrayLiteral(ArrayLiteral* expr) {
// Create nodes to evaluate all the non-constant subexpressions and to store
// them into the newly cloned array.
for (int i = 0; i < expr->values()->length(); i++) {
Expression* subexpr = expr->values()->at(i);
int array_index = 0;
for (; array_index < expr->values()->length(); array_index++) {
Expression* subexpr = expr->values()->at(array_index);
if (subexpr->IsSpread()) break;
if (CompileTimeValue::IsCompileTimeValue(subexpr)) continue;
VisitForValue(subexpr);
{
FrameStateBeforeAndAfter states(this, subexpr->id());
Node* value = environment()->Pop();
Node* index = jsgraph()->Constant(i);
Node* index = jsgraph()->Constant(array_index);
Node* store =
BuildKeyedStore(literal, index, value, TypeFeedbackId::None());
states.AddToNode(store, expr->GetIdForElement(i),
states.AddToNode(store, expr->GetIdForElement(array_index),
OutputFrameStateCombine::Ignore());
}
}
environment()->Pop(); // Array literal index.
// In case the array literal contains spread expressions it has two parts. The
// first part is the "static" array which has a literal index is handled
// above. The second part is the part after the first spread expression
// (inclusive) and these elements gets appended to the array. Note that the
// number elements an iterable produces is unknown ahead of time.
bool has_spread = array_index < expr->values()->length();
if (has_spread) {
environment()->Pop(); // Array literal index.
}
for (; array_index < expr->values()->length(); array_index++) {
Expression* subexpr = expr->values()->at(array_index);
Node* array = environment()->Pop();
Node* result;
if (subexpr->IsSpread()) {
VisitForValue(subexpr->AsSpread()->expression());
Node* iterable = environment()->Pop();
Node* builtins = BuildLoadBuiltinsObject();
Node* function = BuildLoadObjectField(
builtins, JSBuiltinsObject::OffsetOfFunctionWithId(
Builtins::CONCAT_ITERABLE_TO_ARRAY));
result = NewNode(javascript()->CallFunction(3, NO_CALL_FUNCTION_FLAGS,
language_mode()),
function, array, iterable);
} else {
VisitForValue(subexpr);
Node* value = environment()->Pop();
const Operator* op =
javascript()->CallRuntime(Runtime::kAppendElement, 2);
result = NewNode(op, array, value);
}
PrepareFrameState(result, expr->GetIdForElement(array_index));
environment()->Push(result);
}
if (!has_spread) {
environment()->Pop(); // Array literal index.
}
ast_context()->ProduceValue(environment()->Pop());
}

View File

@ -193,6 +193,7 @@ DEFINE_IMPLICATION(es_staging, harmony)
V(harmony_unicode_regexps, "harmony unicode regexps") \
V(harmony_reflect, "harmony Reflect API") \
V(harmony_destructuring, "harmony destructuring") \
V(harmony_spread_arrays, "harmony spread in array literals")
// Features that are complete (but still behind --harmony/es-staging flag).
#define HARMONY_STAGED(V) \

View File

@ -267,7 +267,9 @@ void BreakableStatementChecker::VisitCompareOperation(CompareOperation* expr) {
}
void BreakableStatementChecker::VisitSpread(Spread* expr) { UNREACHABLE(); }
void BreakableStatementChecker::VisitSpread(Spread* expr) {
Visit(expr->expression());
}
void BreakableStatementChecker::VisitThisFunction(ThisFunction* expr) {

View File

@ -5861,6 +5861,10 @@ void HOptimizedGraphBuilder::VisitArrayLiteral(ArrayLiteral* expr) {
for (int i = 0; i < length; i++) {
Expression* subexpr = subexprs->at(i);
if (subexpr->IsSpread()) {
return Bailout(kSpread);
}
// If the subexpression is a literal or a simple materialized literal it
// is already set in the cloned array.
if (CompileTimeValue::IsCompileTimeValue(subexpr)) continue;

View File

@ -1815,8 +1815,11 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) {
// Emit code to evaluate all the non-constant subexpressions and to store
// them into the newly cloned array.
for (int i = 0; i < length; i++) {
Expression* subexpr = subexprs->at(i);
int array_index = 0;
for (; array_index < length; array_index++) {
Expression* subexpr = subexprs->at(array_index);
if (subexpr->IsSpread()) break;
// If the subexpression is a literal or a simple materialized literal it
// is already set in the cloned array.
if (CompileTimeValue::IsCompileTimeValue(subexpr)) continue;
@ -1831,7 +1834,7 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) {
if (has_constant_fast_elements) {
// Fast-case array literal with ElementsKind of FAST_*_ELEMENTS, they
// cannot transition and don't need to call the runtime stub.
int offset = FixedArray::kHeaderSize + (i * kPointerSize);
int offset = FixedArray::kHeaderSize + (array_index * kPointerSize);
__ mov(ebx, Operand(esp, kPointerSize)); // Copy of array literal.
__ mov(ebx, FieldOperand(ebx, JSObject::kElementsOffset));
// Store the subexpression value in the array's elements.
@ -1843,16 +1846,41 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) {
INLINE_SMI_CHECK);
} else {
// Store the subexpression value in the array's elements.
__ mov(ecx, Immediate(Smi::FromInt(i)));
__ mov(ecx, Immediate(Smi::FromInt(array_index)));
StoreArrayLiteralElementStub stub(isolate());
__ CallStub(&stub);
}
PrepareForBailoutForId(expr->GetIdForElement(i), NO_REGISTERS);
PrepareForBailoutForId(expr->GetIdForElement(array_index), NO_REGISTERS);
}
// In case the array literal contains spread expressions it has two parts. The
// first part is the "static" array which has a literal index is handled
// above. The second part is the part after the first spread expression
// (inclusive) and these elements gets appended to the array. Note that the
// number elements an iterable produces is unknown ahead of time.
if (array_index < length && result_saved) {
__ Drop(1); // literal index
__ Pop(eax);
result_saved = false;
}
for (; array_index < length; array_index++) {
Expression* subexpr = subexprs->at(array_index);
__ Push(eax);
if (subexpr->IsSpread()) {
VisitForStackValue(subexpr->AsSpread()->expression());
__ InvokeBuiltin(Builtins::CONCAT_ITERABLE_TO_ARRAY, CALL_FUNCTION);
} else {
VisitForStackValue(subexpr);
__ CallRuntime(Runtime::kAppendElement, 2);
}
PrepareForBailoutForId(expr->GetIdForElement(array_index), NO_REGISTERS);
}
if (result_saved) {
__ add(esp, Immediate(kPointerSize)); // literal index
__ Drop(1); // literal index
context()->PlugTOS();
} else {
context()->Plug(eax);

View File

@ -1877,8 +1877,11 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) {
// Emit code to evaluate all the non-constant subexpressions and to store
// them into the newly cloned array.
for (int i = 0; i < length; i++) {
Expression* subexpr = subexprs->at(i);
int array_index = 0;
for (; array_index < length; array_index++) {
Expression* subexpr = subexprs->at(array_index);
if (subexpr->IsSpread()) break;
// If the subexpression is a literal or a simple materialized literal it
// is already set in the cloned array.
if (CompileTimeValue::IsCompileTimeValue(subexpr)) continue;
@ -1892,7 +1895,7 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) {
VisitForAccumulatorValue(subexpr);
if (has_fast_elements) {
int offset = FixedArray::kHeaderSize + (i * kPointerSize);
int offset = FixedArray::kHeaderSize + (array_index * kPointerSize);
__ lw(t2, MemOperand(sp, kPointerSize)); // Copy of array literal.
__ lw(a1, FieldMemOperand(t2, JSObject::kElementsOffset));
__ sw(result_register(), FieldMemOperand(a1, offset));
@ -1901,14 +1904,40 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) {
kRAHasBeenSaved, kDontSaveFPRegs,
EMIT_REMEMBERED_SET, INLINE_SMI_CHECK);
} else {
__ li(a3, Operand(Smi::FromInt(i)));
__ li(a3, Operand(Smi::FromInt(array_index)));
__ mov(a0, result_register());
StoreArrayLiteralElementStub stub(isolate());
__ CallStub(&stub);
}
PrepareForBailoutForId(expr->GetIdForElement(i), NO_REGISTERS);
PrepareForBailoutForId(expr->GetIdForElement(array_index), NO_REGISTERS);
}
// In case the array literal contains spread expressions it has two parts. The
// first part is the "static" array which has a literal index is handled
// above. The second part is the part after the first spread expression
// (inclusive) and these elements gets appended to the array. Note that the
// number elements an iterable produces is unknown ahead of time.
if (array_index < length && result_saved) {
__ Pop(); // literal index
__ Pop(v0);
result_saved = false;
}
for (; array_index < length; array_index++) {
Expression* subexpr = subexprs->at(array_index);
__ Push(v0);
if (subexpr->IsSpread()) {
VisitForStackValue(subexpr->AsSpread()->expression());
__ InvokeBuiltin(Builtins::CONCAT_ITERABLE_TO_ARRAY, CALL_FUNCTION);
} else {
VisitForStackValue(subexpr);
__ CallRuntime(Runtime::kAppendElement, 2);
}
PrepareForBailoutForId(expr->GetIdForElement(array_index), NO_REGISTERS);
}
if (result_saved) {
__ Pop(); // literal index
context()->PlugTOS();

View File

@ -1876,8 +1876,11 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) {
// Emit code to evaluate all the non-constant subexpressions and to store
// them into the newly cloned array.
for (int i = 0; i < length; i++) {
Expression* subexpr = subexprs->at(i);
int array_index = 0;
for (; array_index < length; array_index++) {
Expression* subexpr = subexprs->at(array_index);
if (subexpr->IsSpread()) break;
// If the subexpression is a literal or a simple materialized literal it
// is already set in the cloned array.
if (CompileTimeValue::IsCompileTimeValue(subexpr)) continue;
@ -1891,7 +1894,7 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) {
VisitForAccumulatorValue(subexpr);
if (has_fast_elements) {
int offset = FixedArray::kHeaderSize + (i * kPointerSize);
int offset = FixedArray::kHeaderSize + (array_index * kPointerSize);
__ ld(a6, MemOperand(sp, kPointerSize)); // Copy of array literal.
__ ld(a1, FieldMemOperand(a6, JSObject::kElementsOffset));
__ sd(result_register(), FieldMemOperand(a1, offset));
@ -1900,14 +1903,40 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) {
kRAHasBeenSaved, kDontSaveFPRegs,
EMIT_REMEMBERED_SET, INLINE_SMI_CHECK);
} else {
__ li(a3, Operand(Smi::FromInt(i)));
__ li(a3, Operand(Smi::FromInt(array_index)));
__ mov(a0, result_register());
StoreArrayLiteralElementStub stub(isolate());
__ CallStub(&stub);
}
PrepareForBailoutForId(expr->GetIdForElement(i), NO_REGISTERS);
PrepareForBailoutForId(expr->GetIdForElement(array_index), NO_REGISTERS);
}
// In case the array literal contains spread expressions it has two parts. The
// first part is the "static" array which has a literal index is handled
// above. The second part is the part after the first spread expression
// (inclusive) and these elements gets appended to the array. Note that the
// number elements an iterable produces is unknown ahead of time.
if (array_index < length && result_saved) {
__ Pop(); // literal index
__ Pop(v0);
result_saved = false;
}
for (; array_index < length; array_index++) {
Expression* subexpr = subexprs->at(array_index);
__ Push(v0);
if (subexpr->IsSpread()) {
VisitForStackValue(subexpr->AsSpread()->expression());
__ InvokeBuiltin(Builtins::CONCAT_ITERABLE_TO_ARRAY, CALL_FUNCTION);
} else {
VisitForStackValue(subexpr);
__ CallRuntime(Runtime::kAppendElement, 2);
}
PrepareForBailoutForId(expr->GetIdForElement(array_index), NO_REGISTERS);
}
if (result_saved) {
__ Pop(); // literal index
context()->PlugTOS();

View File

@ -878,6 +878,7 @@ Parser::Parser(ParseInfo* info)
set_allow_harmony_rest_params(FLAG_harmony_rest_parameters);
set_allow_harmony_spreadcalls(FLAG_harmony_spreadcalls);
set_allow_harmony_destructuring(FLAG_harmony_destructuring);
set_allow_harmony_spread_arrays(FLAG_harmony_spread_arrays);
set_allow_strong_mode(FLAG_strong_mode);
for (int feature = 0; feature < v8::Isolate::kUseCounterFeatureCount;
++feature) {
@ -4288,6 +4289,8 @@ PreParser::PreParseResult Parser::ParseLazyFunctionBodyWithPreParser(
allow_harmony_spreadcalls());
reusable_preparser_->set_allow_harmony_destructuring(
allow_harmony_destructuring());
reusable_preparser_->set_allow_harmony_spread_arrays(
allow_harmony_spread_arrays());
reusable_preparser_->set_allow_strong_mode(allow_strong_mode());
}
PreParser::PreParseResult result = reusable_preparser_->PreParseLazyFunction(

View File

@ -123,6 +123,9 @@ class ParserBase : public Traits {
bool allow_harmony_destructuring() const {
return allow_harmony_destructuring_;
}
bool allow_harmony_spread_arrays() const {
return allow_harmony_spread_arrays_;
}
bool allow_strong_mode() const { return allow_strong_mode_; }
@ -161,7 +164,9 @@ class ParserBase : public Traits {
void set_allow_harmony_destructuring(bool allow) {
allow_harmony_destructuring_ = allow;
}
void set_allow_harmony_spread_arrays(bool allow) {
allow_harmony_spread_arrays_ = allow;
}
protected:
enum AllowRestrictedIdentifiers {
@ -1011,6 +1016,7 @@ class ParserBase : public Traits {
bool allow_harmony_rest_params_;
bool allow_harmony_spreadcalls_;
bool allow_harmony_destructuring_;
bool allow_harmony_spread_arrays_;
bool allow_strong_mode_;
};
@ -2508,12 +2514,13 @@ typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseArrayLiteral(
}
elem = this->GetLiteralTheHole(peek_position(), factory());
} else if (peek() == Token::ELLIPSIS) {
ExpressionUnexpectedToken(classifier);
if (!allow_harmony_spread_arrays()) {
ExpressionUnexpectedToken(classifier);
}
int start_pos = peek_position();
Consume(Token::ELLIPSIS);
ExpressionT argument =
this->ParseAssignmentExpression(true, classifier, CHECK_OK);
elem = factory()->NewSpread(argument, start_pos);
seen_spread = true;
} else {

View File

@ -54,6 +54,7 @@ var CALL_NON_FUNCTION;
var CALL_NON_FUNCTION_AS_CONSTRUCTOR;
var CALL_FUNCTION_PROXY;
var CALL_FUNCTION_PROXY_AS_CONSTRUCTOR;
var CONCAT_ITERABLE_TO_ARRAY;
var APPLY_PREPARE;
var REFLECT_APPLY_PREPARE;
var REFLECT_CONSTRUCT_PREPARE;
@ -726,6 +727,11 @@ REFLECT_CONSTRUCT_PREPARE = function REFLECT_CONSTRUCT_PREPARE(
}
CONCAT_ITERABLE_TO_ARRAY = function CONCAT_ITERABLE_TO_ARRAY(iterable) {
return %$concatIterableToArray(this, iterable);
};
STACK_OVERFLOW = function STACK_OVERFLOW(length) {
throw %MakeRangeError(kStackOverflow);
}

View File

@ -721,6 +721,23 @@ RUNTIME_FUNCTION(Runtime_AddElement) {
}
RUNTIME_FUNCTION(Runtime_AppendElement) {
HandleScope scope(isolate);
RUNTIME_ASSERT(args.length() == 2);
CONVERT_ARG_HANDLE_CHECKED(JSArray, array, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, value, 1);
int index = Smi::cast(array->length())->value();
Handle<Object> result;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, result, JSObject::SetElement(array, index, value, NONE, SLOPPY,
false, DEFINE_PROPERTY));
return *array;
}
RUNTIME_FUNCTION(Runtime_DeleteProperty) {
HandleScope scope(isolate);
DCHECK(args.length() == 3);

View File

@ -414,6 +414,7 @@ namespace internal {
F(AddNamedProperty, 4, 1) \
F(SetProperty, 4, 1) \
F(AddElement, 4, 1) \
F(AppendElement, 2, 1) \
F(DeleteProperty, 3, 1) \
F(HasOwnProperty, 2, 1) \
F(HasProperty, 2, 1) \

View File

@ -754,7 +754,7 @@ void AstTyper::VisitCompareOperation(CompareOperation* expr) {
}
void AstTyper::VisitSpread(Spread* expr) { UNREACHABLE(); }
void AstTyper::VisitSpread(Spread* expr) { RECURSE(Visit(expr->expression())); }
void AstTyper::VisitThisFunction(ThisFunction* expr) {

View File

@ -1847,8 +1847,11 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) {
// Emit code to evaluate all the non-constant subexpressions and to store
// them into the newly cloned array.
for (int i = 0; i < length; i++) {
Expression* subexpr = subexprs->at(i);
int array_index = 0;
for (; array_index < length; array_index++) {
Expression* subexpr = subexprs->at(array_index);
if (subexpr->IsSpread()) break;
// If the subexpression is a literal or a simple materialized literal it
// is already set in the cloned array.
if (CompileTimeValue::IsCompileTimeValue(subexpr)) continue;
@ -1863,7 +1866,7 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) {
if (has_constant_fast_elements) {
// Fast-case array literal with ElementsKind of FAST_*_ELEMENTS, they
// cannot transition and don't need to call the runtime stub.
int offset = FixedArray::kHeaderSize + (i * kPointerSize);
int offset = FixedArray::kHeaderSize + (array_index * kPointerSize);
__ movp(rbx, Operand(rsp, kPointerSize)); // Copy of array literal.
__ movp(rbx, FieldOperand(rbx, JSObject::kElementsOffset));
// Store the subexpression value in the array's elements.
@ -1875,16 +1878,41 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) {
INLINE_SMI_CHECK);
} else {
// Store the subexpression value in the array's elements.
__ Move(rcx, Smi::FromInt(i));
__ Move(rcx, Smi::FromInt(array_index));
StoreArrayLiteralElementStub stub(isolate());
__ CallStub(&stub);
}
PrepareForBailoutForId(expr->GetIdForElement(i), NO_REGISTERS);
PrepareForBailoutForId(expr->GetIdForElement(array_index), NO_REGISTERS);
}
// In case the array literal contains spread expressions it has two parts. The
// first part is the "static" array which has a literal index is handled
// above. The second part is the part after the first spread expression
// (inclusive) and these elements gets appended to the array. Note that the
// number elements an iterable produces is unknown ahead of time.
if (array_index < length && result_saved) {
__ Drop(1); // literal index
__ Pop(rax);
result_saved = false;
}
for (; array_index < length; array_index++) {
Expression* subexpr = subexprs->at(array_index);
__ Push(rax);
if (subexpr->IsSpread()) {
VisitForStackValue(subexpr->AsSpread()->expression());
__ InvokeBuiltin(Builtins::CONCAT_ITERABLE_TO_ARRAY, CALL_FUNCTION);
} else {
VisitForStackValue(subexpr);
__ CallRuntime(Runtime::kAppendElement, 2);
}
PrepareForBailoutForId(expr->GetIdForElement(array_index), NO_REGISTERS);
}
if (result_saved) {
__ addp(rsp, Immediate(kPointerSize)); // literal index
__ Drop(1); // literal index
context()->PlugTOS();
} else {
context()->Plug(rax);

View File

@ -1366,6 +1366,7 @@ enum ParserFlag {
kAllowHarmonyComputedPropertyNames,
kAllowHarmonySpreadCalls,
kAllowHarmonyDestructuring,
kAllowHarmonySpreadArrays,
kAllowStrongMode
};
@ -1397,6 +1398,8 @@ void SetParserFlags(i::ParserBase<Traits>* parser,
flags.Contains(kAllowHarmonyComputedPropertyNames));
parser->set_allow_harmony_destructuring(
flags.Contains(kAllowHarmonyDestructuring));
parser->set_allow_harmony_spread_arrays(
flags.Contains(kAllowHarmonySpreadArrays));
parser->set_allow_strong_mode(flags.Contains(kAllowStrongMode));
}
@ -6520,3 +6523,50 @@ TEST(DestructuringNegativeTests) {
arraysize(always_flags));
}
}
TEST(SpreadArray) {
i::FLAG_harmony_spread_arrays = true;
const char* context_data[][2] = {
{"'use strict';", ""}, {"", ""}, {NULL, NULL}};
// clang-format off
const char* data[] = {
"[...a]",
"[a, ...b]",
"[...a,]",
"[...a, ,]",
"[, ...a]",
"[...a, ...b]",
"[...a, , ...b]",
"[...[...a]]",
"[, ...a]",
"[, , ...a]",
NULL};
// clang-format on
static const ParserFlag always_flags[] = {kAllowHarmonySpreadArrays};
RunParserSyncTest(context_data, data, kSuccess, NULL, 0, always_flags,
arraysize(always_flags));
}
TEST(SpreadArrayError) {
i::FLAG_harmony_spread_arrays = true;
const char* context_data[][2] = {
{"'use strict';", ""}, {"", ""}, {NULL, NULL}};
// clang-format off
const char* data[] = {
"[...]",
"[a, ...]",
"[..., ]",
"[..., ...]",
"[ (...a)]",
NULL};
// clang-format on
static const ParserFlag always_flags[] = {kAllowHarmonySpreadArrays};
RunParserSyncTest(context_data, data, kError, NULL, 0, always_flags,
arraysize(always_flags));
}

View File

@ -0,0 +1,179 @@
// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --harmony-spread-arrays --allow-natives-syntax
(function TestBasics() {
var a = [1, 2];
var b = [...a];
assertArrayEquals([1, 2], b)
assertArrayEquals(['a', 'b', 'c', 'd', 'e', 'f'],
['a', ...'bc', 'd', ...'ef'])
})();
var log = [];
function* gen(n) {
log.push(n, 1);
yield 1;
log.push(n, 2);
yield 2;
log.push(n, 3);
yield 3;
log.push(n, 'done');
}
function id(v) {
log.push(v);
return v;
}
(function TestGenerator() {
assertArrayEquals([1, 2, 3], [...gen('a')]);
assertArrayEquals(['x', 1, 2, 3, 'y', 1, 2, 3, 'z'],
['x', ...gen('a'), 'y', ...gen('b'), 'z']);
})();
(function TestOrderOfExecution() {
log = [];
assertArrayEquals(['x', 1, 2, 3, 'y', 1, 2, 3, 'z'],
[id('x'), ...gen('a'), id('y'), ...gen('b'), id('z')]);
assertArrayEquals([
'x', 'a', 1, 'a', 2, 'a', 3, 'a', 'done',
'y', 'b', 1, 'b', 2, 'b', 3, 'b', 'done',
'z'
], log);
})();
(function TestNotIterable() {
var a;
assertThrows(function() {
a = [...42];
}, TypeError);
assertSame(undefined, a);
})();
(function TestInvalidIterator() {
var iter = {
[Symbol.iterator]: 42
};
var a;
assertThrows(function() {
a = [...iter];
}, TypeError);
assertSame(undefined, a);
})();
(function TestIteratorNotAnObject() {
var iter = {
[Symbol.iterator]() {
return 42;
}
};
var a;
assertThrows(function() {
a = [...iter];
}, TypeError);
assertSame(undefined, a);
})();
(function TestIteratorNoNext() {
var iter = {
[Symbol.iterator]() {
return {};
}
};
var a;
assertThrows(function() {
a = [...iter];
}, TypeError);
assertSame(undefined, a);
})();
(function TestIteratorResultDoneThrows() {
function MyError() {}
var iter = {
[Symbol.iterator]() {
return {
next() {
return {
get done() {
throw new MyError();
}
}
}
};
}
};
var a;
assertThrows(function() {
a = [...iter];
}, MyError);
assertSame(undefined, a);
})();
(function TestIteratorResultValueThrows() {
function MyError() {}
var iter = {
[Symbol.iterator]() {
return {
next() {
return {
done: false,
get value() {
throw new MyError();
}
}
}
};
}
};
var a;
assertThrows(function() {
a = [...iter];
}, MyError);
assertSame(undefined, a);
})();
(function TestOptimize() {
function f() {
return [...'abc'];
}
assertArrayEquals(['a', 'b', 'c'], f());
%OptimizeFunctionOnNextCall(f);
assertArrayEquals(['a', 'b', 'c'], f());
})();
(function TestDeoptimize() {
var iter = {
[Symbol.iterator]() {
var i = 0;
return {
next() {
$DeoptimizeFunction(f);
return {value: ++i, done: i === 3};
}
};
}
};
function f() {
return [0, ...iter];
}
assertArrayEquals([0, 1, 2], f());
});