diff --git a/Userland/Libraries/LibJS/Bytecode/CommonImplementations.cpp b/Userland/Libraries/LibJS/Bytecode/CommonImplementations.cpp index a3a573cb66..0dfaa106cc 100644 --- a/Userland/Libraries/LibJS/Bytecode/CommonImplementations.cpp +++ b/Userland/Libraries/LibJS/Bytecode/CommonImplementations.cpp @@ -567,4 +567,61 @@ ThrowCompletionOr> iterator_to_array(VM& vm, Value iterator) } } +ThrowCompletionOr append(VM& vm, Value lhs, Value rhs, bool is_spread) +{ + // Note: This OpCode is used to construct array literals and argument arrays for calls, + // containing at least one spread element, + // Iterating over such a spread element to unpack it has to be visible by + // the user courtesy of + // (1) https://tc39.es/ecma262/#sec-runtime-semantics-arrayaccumulation + // SpreadElement : ... AssignmentExpression + // 1. Let spreadRef be ? Evaluation of AssignmentExpression. + // 2. Let spreadObj be ? GetValue(spreadRef). + // 3. Let iteratorRecord be ? GetIterator(spreadObj). + // 4. Repeat, + // a. Let next be ? IteratorStep(iteratorRecord). + // b. If next is false, return nextIndex. + // c. Let nextValue be ? IteratorValue(next). + // d. Perform ! CreateDataPropertyOrThrow(array, ! ToString(𝔽(nextIndex)), nextValue). + // e. Set nextIndex to nextIndex + 1. + // (2) https://tc39.es/ecma262/#sec-runtime-semantics-argumentlistevaluation + // ArgumentList : ... AssignmentExpression + // 1. Let list be a new empty List. + // 2. Let spreadRef be ? Evaluation of AssignmentExpression. + // 3. Let spreadObj be ? GetValue(spreadRef). + // 4. Let iteratorRecord be ? GetIterator(spreadObj). + // 5. Repeat, + // a. Let next be ? IteratorStep(iteratorRecord). + // b. If next is false, return list. + // c. Let nextArg be ? IteratorValue(next). + // d. Append nextArg to list. + // ArgumentList : ArgumentList , ... AssignmentExpression + // 1. Let precedingArgs be ? ArgumentListEvaluation of ArgumentList. + // 2. Let spreadRef be ? Evaluation of AssignmentExpression. + // 3. Let iteratorRecord be ? GetIterator(? GetValue(spreadRef)). + // 4. Repeat, + // a. Let next be ? IteratorStep(iteratorRecord). + // b. If next is false, return precedingArgs. + // c. Let nextArg be ? IteratorValue(next). + // d. Append nextArg to precedingArgs. + + // Note: We know from codegen, that lhs is a plain array with only indexed properties + auto& lhs_array = lhs.as_array(); + auto lhs_size = lhs_array.indexed_properties().array_like_size(); + + if (is_spread) { + // ...rhs + size_t i = lhs_size; + TRY(get_iterator_values(vm, rhs, [&i, &lhs_array](Value iterator_value) -> Optional { + lhs_array.indexed_properties().put(i, iterator_value, default_attributes); + ++i; + return {}; + })); + } else { + lhs_array.indexed_properties().put(lhs_size, rhs, default_attributes); + } + + return {}; +} + } diff --git a/Userland/Libraries/LibJS/Bytecode/CommonImplementations.h b/Userland/Libraries/LibJS/Bytecode/CommonImplementations.h index 623997b337..6b99c9843f 100644 --- a/Userland/Libraries/LibJS/Bytecode/CommonImplementations.h +++ b/Userland/Libraries/LibJS/Bytecode/CommonImplementations.h @@ -38,5 +38,6 @@ ThrowCompletionOr> super_call_with_argument_array(VM&, Valu Object* iterator_to_object(VM&, IteratorRecord); IteratorRecord object_to_iterator(VM&, Object&); ThrowCompletionOr> iterator_to_array(VM&, Value iterator); +ThrowCompletionOr append(VM& vm, Value lhs, Value rhs, bool is_spread); } diff --git a/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp b/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp index dd3cdfba26..d5a7fdf384 100644 --- a/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp +++ b/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp @@ -577,63 +577,7 @@ ThrowCompletionOr NewArray::execute_impl(Bytecode::Interpreter& interprete ThrowCompletionOr Append::execute_impl(Bytecode::Interpreter& interpreter) const { - // Note: This OpCode is used to construct array literals and argument arrays for calls, - // containing at least one spread element, - // Iterating over such a spread element to unpack it has to be visible by - // the user courtesy of - // (1) https://tc39.es/ecma262/#sec-runtime-semantics-arrayaccumulation - // SpreadElement : ... AssignmentExpression - // 1. Let spreadRef be ? Evaluation of AssignmentExpression. - // 2. Let spreadObj be ? GetValue(spreadRef). - // 3. Let iteratorRecord be ? GetIterator(spreadObj). - // 4. Repeat, - // a. Let next be ? IteratorStep(iteratorRecord). - // b. If next is false, return nextIndex. - // c. Let nextValue be ? IteratorValue(next). - // d. Perform ! CreateDataPropertyOrThrow(array, ! ToString(𝔽(nextIndex)), nextValue). - // e. Set nextIndex to nextIndex + 1. - // (2) https://tc39.es/ecma262/#sec-runtime-semantics-argumentlistevaluation - // ArgumentList : ... AssignmentExpression - // 1. Let list be a new empty List. - // 2. Let spreadRef be ? Evaluation of AssignmentExpression. - // 3. Let spreadObj be ? GetValue(spreadRef). - // 4. Let iteratorRecord be ? GetIterator(spreadObj). - // 5. Repeat, - // a. Let next be ? IteratorStep(iteratorRecord). - // b. If next is false, return list. - // c. Let nextArg be ? IteratorValue(next). - // d. Append nextArg to list. - // ArgumentList : ArgumentList , ... AssignmentExpression - // 1. Let precedingArgs be ? ArgumentListEvaluation of ArgumentList. - // 2. Let spreadRef be ? Evaluation of AssignmentExpression. - // 3. Let iteratorRecord be ? GetIterator(? GetValue(spreadRef)). - // 4. Repeat, - // a. Let next be ? IteratorStep(iteratorRecord). - // b. If next is false, return precedingArgs. - // c. Let nextArg be ? IteratorValue(next). - // d. Append nextArg to precedingArgs. - - auto& vm = interpreter.vm(); - - // Note: We know from codegen, that lhs is a plain array with only indexed properties - auto& lhs = interpreter.reg(m_lhs).as_array(); - auto lhs_size = lhs.indexed_properties().array_like_size(); - - auto rhs = interpreter.accumulator(); - - if (m_is_spread) { - // ...rhs - size_t i = lhs_size; - TRY(get_iterator_values(vm, rhs, [&i, &lhs](Value iterator_value) -> Optional { - lhs.indexed_properties().put(i, iterator_value, default_attributes); - ++i; - return {}; - })); - } else { - lhs.indexed_properties().put(lhs_size, rhs, default_attributes); - } - - return {}; + return append(interpreter.vm(), interpreter.reg(m_lhs), interpreter.accumulator(), m_is_spread); } ThrowCompletionOr ImportCall::execute_impl(Bytecode::Interpreter& interpreter) const