Explorar el Código

LibJS: Let Array.prototype.map() resize new array before loop

Currently we would create an empty array of size 0 and appening results
of the callback function while skipping empty values.

This is incorrect, we should be initializing a full array of the correct
size beforehand and then inserting the results while still skipping
empty values.

Wrong: new Array(5).map(() => {}) // []
Right: new Array(5).map(() => {}) // [<empty> * 5]
Linus Groh hace 5 años
padre
commit
ad8abce8a5

+ 11 - 1
Libraries/LibJS/Runtime/ArrayPrototype.cpp

@@ -143,29 +143,39 @@ Value ArrayPrototype::for_each(Interpreter& interpreter)
 
 Value ArrayPrototype::map(Interpreter& interpreter)
 {
+    // FIXME: Make generic, i.e. work with length and numeric properties only
+    // This should work: Array.prototype.map.call("abc", ch => ...)
     auto* array = array_from(interpreter);
     if (!array)
         return {};
+
     auto* callback = callback_from_args(interpreter, "map");
     if (!callback)
         return {};
+
     auto this_value = interpreter.argument(1);
     auto initial_array_size = array->elements().size();
     auto* new_array = Array::create(interpreter.global_object());
+    new_array->elements().resize(initial_array_size);
+
     for (size_t i = 0; i < initial_array_size; ++i) {
         if (i >= array->elements().size())
             break;
+
         auto value = array->elements()[i];
         if (value.is_empty())
             continue;
+
         MarkedValueList arguments(interpreter.heap());
         arguments.append(value);
         arguments.append(Value((i32)i));
         arguments.append(array);
+
         auto result = interpreter.call(callback, this_value, move(arguments));
         if (interpreter.exception())
             return {};
-        new_array->elements().append(result);
+
+        new_array->elements()[i] = result;
     }
     return Value(new_array);
 }

+ 4 - 0
Libraries/LibJS/Tests/Array.prototype.map.js

@@ -26,6 +26,10 @@ try {
     assert([1, 2, 3].map(callback).length === 3);
     assert(callbackCalled === 3);
 
+    callbackCalled = 0;
+    assert([1, , , "foo", , undefined, , ,].map(callback).length === 8);
+    assert(callbackCalled === 3);
+
     var results = [undefined, null, true, "foo", 42, {}].map((value, index) => "" + index + " -> " + value);
     assert(results.length === 6);
     assert(results[0] === "0 -> undefined");