[typedarray] Fix IterableToList when Number has an iterator

IterableToListCanBeElided checked that the input was always a HeapObject
but this is not true when an iterator symbol is defined on the Number
prototype, meaning Smi and HeapNumber can also be passed in.

Added a regression test for the crash and some correctness tests for
smi and double input to TA.from.

Also factored out the tests in typedarray-from.js that modify global
state e.g. protector cells, so that one iteration of the top level
loop does not interfere with the next.

Bug: chromium:814643
Change-Id: I364d11f011faf8370446f905a35a945d47e4477f
Reviewed-on: https://chromium-review.googlesource.com/930962
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Commit-Queue: Peter Marshall <petermarshall@chromium.org>
Cr-Commit-Position: refs/heads/master@{#51461}
This commit is contained in:
Peter Marshall 2018-02-22 10:48:50 +01:00 committed by Commit Bot
parent ef93026137
commit aaa78c330e
3 changed files with 32 additions and 10 deletions

View File

@ -1247,9 +1247,13 @@ RUNTIME_FUNCTION(Runtime_CreateDataProperty) {
RUNTIME_FUNCTION(Runtime_IterableToListCanBeElided) {
HandleScope scope(isolate);
DCHECK_EQ(1, args.length());
CONVERT_ARG_HANDLE_CHECKED(HeapObject, obj, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, obj, 0);
if (!obj->IsJSObject()) return isolate->heap()->ToBoolean(false);
// If an iterator symbol is added to the Number prototype, we could see a Smi.
if (obj->IsSmi()) return isolate->heap()->ToBoolean(false);
if (!HeapObject::cast(*obj)->IsJSObject()) {
return isolate->heap()->ToBoolean(false);
}
// While iteration alone may not have observable side-effects, calling
// toNumber on an object will. Make sure the arg is not an array of objects.

View File

@ -151,14 +151,6 @@ for (var constructor of typedArrayConstructors) {
assertThrows(function() { constructor.from([], 'noncallable'); },
TypeError);
source = [1, 2, 3];
source[Symbol.iterator] = undefined;
assertArrayLikeEquals(constructor.from(source), source, constructor);
source = [{ valueOf: function(){ return 42; }}];
source[Symbol.iterator] = undefined;
assertArrayLikeEquals(constructor.from(source), [42], constructor);
source = [1, 2, 3];
var proxy = new Proxy(source, {});
assertArrayLikeEquals(constructor.from(proxy), source, constructor);
@ -171,6 +163,26 @@ for (var constructor of typedArrayConstructors) {
}
});
assertArrayLikeEquals(constructor.from(proxy), [2, 3, 4], constructor);
}
// Tests that modify global state in a way that affects fast paths e.g. by
// invalidating protectors or changing prototypes.
for (var constructor of typedArrayConstructors) {
source = [1, 2, 3];
source[Symbol.iterator] = undefined;
assertArrayLikeEquals(constructor.from(source), source, constructor);
source = [{ valueOf: function(){ return 42; }}];
source[Symbol.iterator] = undefined;
assertArrayLikeEquals(constructor.from(source), [42], constructor);
Number.prototype[Symbol.iterator] = function* () {
yield 1;
yield 2;
yield 3;
}
assertArrayLikeEquals(constructor.from(1), [1, 2, 3], constructor);
assertArrayLikeEquals(constructor.from(1.1), [1, 2, 3], constructor);
var nullIterator = {};
nullIterator[Symbol.iterator] = null;

View File

@ -0,0 +1,6 @@
// Copyright 2018 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.
Number.prototype.__proto__ = String.prototype;
Uint8Array.from(1);