[Builtins] Torque version of Array.prototype.forEach()
BUG=v8:7672 Change-Id: I0c157ce88b31312dfbea7a149c1d9fbdfb398278 Reviewed-on: https://chromium-review.googlesource.com/1013524 Commit-Queue: Michael Stanton <mvstanton@chromium.org> Reviewed-by: Daniel Clifford <danno@chromium.org> Cr-Commit-Position: refs/heads/master@{#53091}
This commit is contained in:
parent
4a1ceff1aa
commit
5945e1ccd0
@ -297,4 +297,207 @@ module array {
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
macro ArrayForEachTorqueContinuation(
|
||||
context: Context, o: Object, len: Number, callbackfn: Callable,
|
||||
thisArg: Object, initial_k: Smi): Object {
|
||||
// 5. Let k be 0.
|
||||
// 6. Repeat, while k < len
|
||||
for (let k: Smi = initial_k; k < len; k = k + 1) {
|
||||
// 6a. Let Pk be ! ToString(k).
|
||||
let pK: String = ToString_Inline(context, k);
|
||||
|
||||
// 6b. Let kPresent be ? HasProperty(O, Pk).
|
||||
let kPresent: Oddball = HasPropertyObject(o, pK, context, kHasProperty);
|
||||
|
||||
// 6c. If kPresent is true, then
|
||||
if (kPresent == True) {
|
||||
// 6c. i. Let kValue be ? Get(O, Pk).
|
||||
let kValue: Object = GetProperty(context, o, pK);
|
||||
|
||||
// 6c. ii. Perform ? Call(callbackfn, T, <kValue, k, O>).
|
||||
Call(context, callbackfn, thisArg, kValue, k, o);
|
||||
}
|
||||
|
||||
// 6d. Increase k by 1. (done by the loop).
|
||||
}
|
||||
return Undefined;
|
||||
}
|
||||
|
||||
javascript builtin ArrayForEachLoopEagerDeoptContinuation(
|
||||
context: Context, receiver: Object, callback: Object, thisArg: Object,
|
||||
initialK: Object, length: Object): Object {
|
||||
return ArrayForEachLoopContinuation(
|
||||
context, receiver, callback, thisArg, Undefined, receiver, initialK,
|
||||
length, Undefined);
|
||||
}
|
||||
|
||||
javascript builtin ArrayForEachLoopLazyDeoptContinuation(
|
||||
context: Context, receiver: Object, callback: Object, thisArg: Object,
|
||||
initialK: Object, length: Object, result: Object): Object {
|
||||
return ArrayForEachLoopContinuation(
|
||||
context, receiver, callback, thisArg, Undefined, receiver, initialK,
|
||||
length, Undefined);
|
||||
}
|
||||
|
||||
builtin ArrayForEachLoopContinuation(
|
||||
context: Context, receiver: Object, callback: Object, thisArg: Object,
|
||||
array: Object, object: Object, initialK: Object, length: Object,
|
||||
to: Object): Object {
|
||||
try {
|
||||
let callbackfn: Callable = cast<Callable>(callback) otherwise TypeError;
|
||||
let k: Smi = cast<Smi>(initialK) otherwise TypeError;
|
||||
let number_length: Number = cast<Number>(length) otherwise TypeError;
|
||||
|
||||
return ArrayForEachTorqueContinuation(
|
||||
context, object, number_length, callbackfn, thisArg, k);
|
||||
}
|
||||
label TypeError {
|
||||
ThrowTypeError(context, kCalledNonCallable, callback);
|
||||
// TODO(mvstanton): this should be unreachable.
|
||||
}
|
||||
}
|
||||
|
||||
macro VisitAllDoubleElements(
|
||||
context: Context, a: JSArray, len: Smi, callbackfn: Callable,
|
||||
thisArg: Object): void labels
|
||||
Bailout(Smi) {
|
||||
let k: Smi = 0;
|
||||
let map: Map = a.map;
|
||||
|
||||
try {
|
||||
// Build a fast loop over the smi array.
|
||||
for (; k < len; k = k + 1) {
|
||||
// Ensure that the map didn't change.
|
||||
if (map != a.map) goto slow;
|
||||
// Ensure that we haven't walked beyond a possibly updated length.
|
||||
if (k >= a.length) goto slow;
|
||||
let elements: FixedDoubleArray = cast<FixedDoubleArray>(a.elements)
|
||||
otherwise slow;
|
||||
try {
|
||||
let value: float64 = LoadDoubleWithHoleCheck(elements, k)
|
||||
otherwise found_hole;
|
||||
let boxed_value: HeapNumber = AllocateHeapNumberWithValue(value);
|
||||
Call(context, callbackfn, thisArg, boxed_value, k, a);
|
||||
}
|
||||
label found_hole {
|
||||
// If we found the hole, we need to bail out if the initial
|
||||
// array prototype has had elements inserted. This is preferable
|
||||
// to walking the prototype chain looking for elements.
|
||||
|
||||
if (IsNoElementsProtectorCellInvalid()) goto Bailout(k);
|
||||
}
|
||||
}
|
||||
}
|
||||
label slow {
|
||||
goto Bailout(k);
|
||||
}
|
||||
}
|
||||
|
||||
macro VisitAllFastElements(
|
||||
context: Context, a: JSArray, len: Smi, callbackfn: Callable,
|
||||
thisArg: Object): void labels
|
||||
Bailout(Smi) {
|
||||
let k: Smi = 0;
|
||||
let map: Map = a.map;
|
||||
|
||||
try {
|
||||
// Build a fast loop over the smi array.
|
||||
for (; k < len; k = k + 1) {
|
||||
// Ensure that the map didn't change.
|
||||
if (map != a.map) goto slow;
|
||||
// Ensure that we haven't walked beyond a possibly updated length.
|
||||
if (k >= a.length) goto slow;
|
||||
let elements: FixedArray = cast<FixedArray>(a.elements)
|
||||
otherwise slow;
|
||||
let value: Object = elements[k];
|
||||
if (value != Hole) {
|
||||
Call(context, callbackfn, thisArg, value, k, a);
|
||||
} else {
|
||||
// If we found the hole, we need to bail out if the initial
|
||||
// array prototype has had elements inserted. This is preferable
|
||||
// to walking the prototype chain looking for elements.
|
||||
if (IsNoElementsProtectorCellInvalid()) goto slow;
|
||||
}
|
||||
}
|
||||
}
|
||||
label slow {
|
||||
goto Bailout(k);
|
||||
}
|
||||
}
|
||||
|
||||
macro FastArrayForEach(
|
||||
context: Context, o: Object, len: Number, callbackfn: Callable,
|
||||
thisArg: Object): Object labels
|
||||
Bailout(Smi) {
|
||||
let k: Smi = 0;
|
||||
try {
|
||||
let smi_len: Smi = cast<Smi>(len) otherwise slow;
|
||||
let a: JSArray = cast<JSArray>(o) otherwise slow;
|
||||
let map: Map = a.map;
|
||||
|
||||
if (!IsPrototypeInitialArrayPrototype(context, map)) goto slow;
|
||||
let elementsKind: ElementsKind = map.elements_kind;
|
||||
if (!IsFastElementsKind(elementsKind)) goto slow;
|
||||
|
||||
if (IsElementsKindGreaterThan(elementsKind, HOLEY_ELEMENTS)) {
|
||||
VisitAllDoubleElements(context, a, smi_len, callbackfn, thisArg)
|
||||
otherwise Bailout;
|
||||
} else {
|
||||
VisitAllFastElements(context, a, smi_len, callbackfn, thisArg)
|
||||
otherwise Bailout;
|
||||
}
|
||||
}
|
||||
label slow {
|
||||
goto Bailout(k);
|
||||
}
|
||||
return Undefined;
|
||||
}
|
||||
|
||||
// https://tc39.github.io/ecma262/#sec-array.prototype.foreach
|
||||
javascript builtin ArrayForEach(
|
||||
context: Context, receiver: Object, ...arguments): Object {
|
||||
try {
|
||||
if (IsNullOrUndefined(receiver)) {
|
||||
goto NullOrUndefinedError;
|
||||
}
|
||||
|
||||
// 1. Let O be ? ToObject(this value).
|
||||
let o: Object = ToObject(context, receiver);
|
||||
|
||||
// 2. Let len be ? ToLength(? Get(O, "length")).
|
||||
let len: Number =
|
||||
ToLength_Inline(context, GetProperty(context, o, 'length'));
|
||||
|
||||
// 3. If IsCallable(callbackfn) is false, throw a TypeError exception.
|
||||
if (arguments.length == 0) {
|
||||
goto TypeError;
|
||||
}
|
||||
let callbackfn: Callable =
|
||||
cast<Callable>(arguments[0]) otherwise TypeError;
|
||||
|
||||
// 4. If thisArg is present, let T be thisArg; else let T be undefined.
|
||||
let thisArg: Object = arguments.length > 1 ? arguments[1] : Undefined;
|
||||
|
||||
// Special cases.
|
||||
let k: Smi = 0;
|
||||
try {
|
||||
return FastArrayForEach(context, o, len, callbackfn, thisArg)
|
||||
otherwise Bailout;
|
||||
}
|
||||
label Bailout(k_value: Smi) {
|
||||
k = k_value;
|
||||
}
|
||||
|
||||
return ArrayForEachTorqueContinuation(
|
||||
context, o, len, callbackfn, thisArg, k);
|
||||
}
|
||||
label TypeError {
|
||||
ThrowTypeError(context, kCalledNonCallable, arguments[0]);
|
||||
}
|
||||
label NullOrUndefinedError {
|
||||
ThrowTypeError(
|
||||
context, kCalledOnNullOrUndefined, 'Array.prototype.forEach');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,15 +17,17 @@ type RawPtr generates 'TNode<RawPtrT>' constexpr 'void*';
|
||||
type Number extends Object generates 'TNode<Number>';
|
||||
type Smi extends Number generates 'TNode<Smi>';
|
||||
type HeapObject extends Object generates 'TNode<HeapObject>';
|
||||
type JSReceiver extends HeapObject generates 'TNode<JSReceiver>';
|
||||
type Context extends HeapObject generates 'TNode<Context>';
|
||||
type String extends HeapObject generates 'TNode<String>';
|
||||
type Oddball extends HeapObject generates 'TNode<Oddball>';
|
||||
type HeapNumber extends HeapObject generates 'TNode<HeapNumber>';
|
||||
type Boolean extends Oddball generates 'TNode<Oddball>';
|
||||
type JSArray extends Object generates 'TNode<JSArray>';
|
||||
type Callable extends HeapObject generates 'TNode<Object>';
|
||||
type JSArray extends HeapObject generates 'TNode<JSArray>';
|
||||
type Callable extends JSReceiver generates 'TNode<JSReceiver>';
|
||||
type JSFunction extends Callable;
|
||||
type Map extends Object generates 'TNode<Map>';
|
||||
type FixedArrayBase extends Object generates 'TNode<FixedArrayBase>';
|
||||
type Map extends HeapObject generates 'TNode<Map>';
|
||||
type FixedArrayBase extends HeapObject generates 'TNode<FixedArrayBase>';
|
||||
type FixedArray extends FixedArrayBase generates 'TNode<FixedArray>';
|
||||
type FixedDoubleArray extends FixedArrayBase generates
|
||||
'TNode<FixedDoubleArray>';
|
||||
@ -71,6 +73,10 @@ const kEmptyFixedArray: FixedArrayBase =
|
||||
|
||||
const kInvalidArrayLengthMessage: MessageTemplate =
|
||||
'MessageTemplate::kInvalidArrayLength';
|
||||
const kCalledNonCallable: MessageTemplate =
|
||||
'MessageTemplate::kCalledNonCallable';
|
||||
const kCalledOnNullOrUndefined: MessageTemplate =
|
||||
'MessageTemplate::kCalledOnNullOrUndefined';
|
||||
|
||||
const kHasProperty: HasPropertyFlag = 'kHasProperty';
|
||||
|
||||
@ -108,6 +114,7 @@ extern macro ArraySpeciesCreate(Context, Object, Number): Object;
|
||||
extern macro EnsureArrayPushable(Map): ElementsKind labels Bailout;
|
||||
|
||||
extern builtin ToObject(Context, Object): Object;
|
||||
extern macro IsNullOrUndefined(Object): bool;
|
||||
|
||||
extern runtime CreateDataProperty(Context, Object, Object, Object);
|
||||
extern runtime SetProperty(Context, Object, Object, Object, LanguageMode);
|
||||
@ -170,8 +177,8 @@ extern operator 'max' macro NumberMax(Number, Number): Number;
|
||||
|
||||
extern operator '!' macro Word32BinaryNot(bool): bool;
|
||||
|
||||
extern operator '.map' macro LoadMap(Object): Map;
|
||||
extern operator '.map=' macro StoreMap(Object, Map);
|
||||
extern operator '.map' macro LoadMap(HeapObject): Map;
|
||||
extern operator '.map=' macro StoreMap(HeapObject, Map);
|
||||
extern operator '.instanceType' macro LoadInstanceType(Object): InstanceType;
|
||||
|
||||
extern operator '.length' macro LoadStringLengthAsWord(String): intptr;
|
||||
@ -195,6 +202,8 @@ extern operator 'cast<>' macro ConvertFixedArrayBaseToFixedArray(
|
||||
extern operator 'cast<>' macro ConvertFixedArrayBaseToFixedDoubleArray(
|
||||
FixedArrayBase): FixedDoubleArray labels CastError;
|
||||
|
||||
extern macro AllocateHeapNumberWithValue(float64): HeapNumber;
|
||||
|
||||
extern implicit operator
|
||||
'convert<>' macro AllocateHeapNumberWithValue(constexpr float64): Number;
|
||||
extern implicit operator
|
||||
@ -253,6 +262,7 @@ extern operator
|
||||
extern macro IsFastElementsKind(ElementsKind): bool;
|
||||
extern macro IsFastSmiOrTaggedElementsKind(ElementsKind): bool;
|
||||
extern macro IsFastSmiElementsKind(ElementsKind): bool;
|
||||
extern macro IsHoleyFastElementsKind(ElementsKind): bool;
|
||||
|
||||
extern macro AllocateFixedArray(constexpr ElementsKind, Smi): FixedArray;
|
||||
extern macro AllocateFixedArray(constexpr ElementsKind, Smi, Map): FixedArray;
|
||||
@ -266,6 +276,11 @@ extern macro CopyFixedArrayElements(
|
||||
|
||||
extern macro AllocateJSArray(constexpr ElementsKind, Map, intptr, Smi): JSArray;
|
||||
extern macro AllocateJSArray(constexpr ElementsKind, Map, Smi, Smi): JSArray;
|
||||
extern macro IsElementsKindGreaterThan(
|
||||
ElementsKind, constexpr ElementsKind): bool;
|
||||
|
||||
extern macro LoadDoubleWithHoleCheck(FixedDoubleArray, Smi): float64
|
||||
labels IfHole;
|
||||
|
||||
extern macro Call(Context, Callable, Object, ...): Object;
|
||||
|
||||
|
@ -2236,72 +2236,6 @@ TF_BUILTIN(TypedArrayPrototypeFindIndex, ArrayBuiltinsAssembler) {
|
||||
&ArrayBuiltinsAssembler::NullPostLoopAction);
|
||||
}
|
||||
|
||||
TF_BUILTIN(ArrayForEachLoopContinuation, ArrayBuiltinsAssembler) {
|
||||
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
|
||||
TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
|
||||
Node* callbackfn = Parameter(Descriptor::kCallbackFn);
|
||||
Node* this_arg = Parameter(Descriptor::kThisArg);
|
||||
Node* array = Parameter(Descriptor::kArray);
|
||||
TNode<JSReceiver> object = CAST(Parameter(Descriptor::kObject));
|
||||
Node* initial_k = Parameter(Descriptor::kInitialK);
|
||||
TNode<Number> len = CAST(Parameter(Descriptor::kLength));
|
||||
Node* to = Parameter(Descriptor::kTo);
|
||||
|
||||
InitIteratingArrayBuiltinLoopContinuation(context, receiver, callbackfn,
|
||||
this_arg, array, object, initial_k,
|
||||
len, to);
|
||||
|
||||
GenerateIteratingArrayBuiltinLoopContinuation(
|
||||
&ArrayBuiltinsAssembler::ForEachProcessor,
|
||||
&ArrayBuiltinsAssembler::NullPostLoopAction, MissingPropertyMode::kSkip);
|
||||
}
|
||||
|
||||
TF_BUILTIN(ArrayForEachLoopEagerDeoptContinuation, ArrayBuiltinsAssembler) {
|
||||
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
|
||||
TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
|
||||
Node* callbackfn = Parameter(Descriptor::kCallbackFn);
|
||||
Node* this_arg = Parameter(Descriptor::kThisArg);
|
||||
Node* initial_k = Parameter(Descriptor::kInitialK);
|
||||
TNode<Number> len = CAST(Parameter(Descriptor::kLength));
|
||||
|
||||
Return(CallBuiltin(Builtins::kArrayForEachLoopContinuation, context, receiver,
|
||||
callbackfn, this_arg, UndefinedConstant(), receiver,
|
||||
initial_k, len, UndefinedConstant()));
|
||||
}
|
||||
|
||||
TF_BUILTIN(ArrayForEachLoopLazyDeoptContinuation, ArrayBuiltinsAssembler) {
|
||||
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
|
||||
TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
|
||||
Node* callbackfn = Parameter(Descriptor::kCallbackFn);
|
||||
Node* this_arg = Parameter(Descriptor::kThisArg);
|
||||
Node* initial_k = Parameter(Descriptor::kInitialK);
|
||||
TNode<Number> len = CAST(Parameter(Descriptor::kLength));
|
||||
|
||||
Return(CallBuiltin(Builtins::kArrayForEachLoopContinuation, context, receiver,
|
||||
callbackfn, this_arg, UndefinedConstant(), receiver,
|
||||
initial_k, len, UndefinedConstant()));
|
||||
}
|
||||
|
||||
TF_BUILTIN(ArrayForEach, ArrayBuiltinsAssembler) {
|
||||
TNode<IntPtrT> argc =
|
||||
ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount));
|
||||
CodeStubArguments args(this, argc);
|
||||
TNode<Context> context = CAST(Parameter(BuiltinDescriptor::kContext));
|
||||
TNode<Object> receiver = args.GetReceiver();
|
||||
Node* callbackfn = args.GetOptionalArgumentValue(0);
|
||||
Node* this_arg = args.GetOptionalArgumentValue(1);
|
||||
|
||||
InitIteratingArrayBuiltinBody(context, receiver, callbackfn, this_arg, argc);
|
||||
|
||||
GenerateIteratingArrayBuiltinBody(
|
||||
"Array.prototype.forEach",
|
||||
&ArrayBuiltinsAssembler::ForEachResultGenerator,
|
||||
&ArrayBuiltinsAssembler::ForEachProcessor,
|
||||
&ArrayBuiltinsAssembler::NullPostLoopAction,
|
||||
Builtins::CallableFor(isolate(), Builtins::kArrayForEachLoopContinuation),
|
||||
MissingPropertyMode::kSkip);
|
||||
}
|
||||
|
||||
TF_BUILTIN(TypedArrayPrototypeForEach, ArrayBuiltinsAssembler) {
|
||||
TNode<IntPtrT> argc =
|
||||
ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount));
|
||||
|
@ -278,14 +278,6 @@ namespace internal {
|
||||
/* Support for Array.from and other array-copying idioms */ \
|
||||
TFS(CloneFastJSArray, kSource) \
|
||||
TFS(ExtractFastJSArray, kSource, kBegin, kCount) \
|
||||
/* ES6 #sec-array.prototype.foreach */ \
|
||||
TFS(ArrayForEachLoopContinuation, kReceiver, kCallbackFn, kThisArg, kArray, \
|
||||
kObject, kInitialK, kLength, kTo) \
|
||||
TFJ(ArrayForEachLoopEagerDeoptContinuation, 4, kCallbackFn, kThisArg, \
|
||||
kInitialK, kLength) \
|
||||
TFJ(ArrayForEachLoopLazyDeoptContinuation, 5, kCallbackFn, kThisArg, \
|
||||
kInitialK, kLength, kResult) \
|
||||
TFJ(ArrayForEach, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
|
||||
/* ES6 #sec-array.prototype.every */ \
|
||||
TFS(ArrayEveryLoopContinuation, kReceiver, kCallbackFn, kThisArg, kArray, \
|
||||
kObject, kInitialK, kLength, kTo) \
|
||||
|
@ -857,6 +857,12 @@ void CodeStubAssembler::Bind(Label* label, AssemblerDebugInfo debug_info) {
|
||||
void CodeStubAssembler::Bind(Label* label) { CodeAssembler::Bind(label); }
|
||||
#endif // DEBUG
|
||||
|
||||
TNode<Float64T> CodeStubAssembler::LoadDoubleWithHoleCheck(
|
||||
TNode<FixedDoubleArray> array, TNode<Smi> index, Label* if_hole) {
|
||||
return TNode<Float64T>::UncheckedCast(LoadFixedDoubleArrayElement(
|
||||
array, index, MachineType::Float64(), 0, SMI_PARAMETERS, if_hole));
|
||||
}
|
||||
|
||||
void CodeStubAssembler::BranchIfPrototypesHaveNoElements(
|
||||
Node* receiver_map, Label* definitely_no_elements,
|
||||
Label* possibly_elements) {
|
||||
|
@ -777,6 +777,9 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
|
||||
ParameterMode parameter_mode = INTPTR_PARAMETERS);
|
||||
|
||||
TNode<IntPtrT> LoadFeedbackVectorLength(TNode<FeedbackVector>);
|
||||
TNode<Float64T> LoadDoubleWithHoleCheck(TNode<FixedDoubleArray> array,
|
||||
TNode<Smi> index,
|
||||
Label* if_hole = nullptr);
|
||||
|
||||
// Load Float64 value by |base| + |offset| address. If the value is a double
|
||||
// hole then jump to |if_hole|. If |machine_type| is None then only the hole
|
||||
|
@ -24,3 +24,15 @@
|
||||
}
|
||||
a.forEach(poison);
|
||||
})();
|
||||
|
||||
// Same, but for a double array.
|
||||
(()=>{
|
||||
let a = [1, 2.5, 3,,,, 7];
|
||||
function poison(v, i) {
|
||||
if (i === 2) {
|
||||
[].__proto__[4] = 3;
|
||||
}
|
||||
return v*v;
|
||||
}
|
||||
a.forEach(poison);
|
||||
})();
|
||||
|
Loading…
Reference in New Issue
Block a user