[iterator] Extend GetIterator to Check iterator type

This CL extends GetIterator to check whether the result of
calling @@iterator is JSReceiver and throw SymbolIteratorInvalid
if it's not JSReceiver.

GetIterator bytecode involves 3 steps now:
- method = GetMethod(obj, @@iterator)
- iterator = Call(method, obj)
- if(!IsJSReceiver(iterator)) throw SymbolIteratorInvalid [Added]

New Builtin: CallIteratorWithFeedbackLazyDeoptContinuation, which
is used when lazy deopt is triggered by call @@iterator.

Related spec: https://tc39.es/ecma262/#sec-getiterator.
Related doc: https://docs.google.com/document/d/1s67HC2f-4zxA_s1Bmm7dfwMFv_KDUfMiWIKkNSeQNKw/edit#heading=h.kdzv8mq4g4ks.

Bug: v8:9489
Change-Id: I17952c0f3e24e1e600ee1348809fb188c2c70f8e
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3563447
Reviewed-by: Leszek Swirski <leszeks@chromium.org>
Reviewed-by: Tobias Tebbi <tebbi@chromium.org>
Commit-Queue: 王澳 <wangao.james@bytedance.com>
Cr-Commit-Position: refs/heads/main@{#80112}
This commit is contained in:
jameslahm 2022-04-22 20:17:39 +08:00 committed by V8 LUCI CQ
parent 0603f8a953
commit 4a285a2549
21 changed files with 327 additions and 164 deletions

View File

@ -389,6 +389,7 @@ extern enum MessageTemplate {
kInvalidCountValue,
kConstructorNotFunction,
kSymbolToString,
kSymbolIteratorInvalid,
kPropertyNotFunction,
kBigIntTooBig,
kNotTypedArray,

View File

@ -590,6 +590,7 @@ namespace internal {
\
/* Iterator Protocol */ \
TFC(GetIteratorWithFeedbackLazyDeoptContinuation, GetIteratorStackParameter) \
TFC(CallIteratorWithFeedbackLazyDeoptContinuation, SingleParameterOnStack) \
\
/* Global object */ \
CPP(GlobalDecodeURI) \

View File

@ -472,6 +472,16 @@ TF_BUILTIN(GetIteratorWithFeedbackLazyDeoptContinuation,
Return(result);
}
TF_BUILTIN(CallIteratorWithFeedbackLazyDeoptContinuation,
IteratorBuiltinsAssembler) {
TNode<Context> context = Parameter<Context>(Descriptor::kContext);
TNode<Object> iterator = Parameter<Object>(Descriptor::kArgument);
ThrowIfNotJSReceiver(context, iterator,
MessageTemplate::kSymbolIteratorInvalid, "");
Return(iterator);
}
// This builtin creates a FixedArray based on an Iterable and doesn't have a
// fast path for anything.
TF_BUILTIN(IterableToFixedArrayWithSymbolLookupSlow,

View File

@ -111,7 +111,9 @@ transitioning builtin CallIteratorWithFeedback(
context, feedback, callSlotUnTagged);
const iteratorCallable: Callable = Cast<Callable>(iteratorMethod)
otherwise ThrowIteratorError(receiver);
return Call(context, iteratorCallable, receiver);
const iterator = Call(context, iteratorCallable, receiver);
ThrowIfNotJSReceiver(iterator, MessageTemplate::kSymbolIteratorInvalid, '');
return iterator;
}
// https://tc39.es/ecma262/#sec-iteratorclose

View File

@ -1454,6 +1454,44 @@ Reduction JSNativeContextSpecialization::ReduceJSGetIterator(Node* node) {
Effect effect = n.effect();
Control control = n.control();
Node* iterator_exception_node = nullptr;
Node* if_exception_merge = nullptr;
Node* if_exception_effect_phi = nullptr;
Node* if_exception_phi = nullptr;
bool has_exception_node =
NodeProperties::IsExceptionalCall(node, &iterator_exception_node);
int exception_node_index = 0;
if (has_exception_node) {
DCHECK_NOT_NULL(iterator_exception_node);
// If there exists an IfException node for the iterator node, we need
// to merge all the desugared nodes exception. The iterator node will be
// desugared to LoadNamed, Call, CallRuntime, we can pre-allocate the
// nodes with 4 inputs here and we use dead_node as a placeholder for the
// input, which will be replaced.
// We use dead_node as a placeholder for the original exception node before
// it's uses are rewired.
Node* dead_node = jsgraph()->Dead();
if_exception_merge = graph()->NewNode(common()->Merge(4), dead_node,
dead_node, dead_node, dead_node);
if_exception_effect_phi =
graph()->NewNode(common()->EffectPhi(4), dead_node, dead_node,
dead_node, dead_node, if_exception_merge);
if_exception_phi = graph()->NewNode(
common()->Phi(MachineRepresentation::kTagged, 4), dead_node, dead_node,
dead_node, dead_node, if_exception_merge);
// Rewire the original exception node uses.
ReplaceWithValue(iterator_exception_node, if_exception_phi,
if_exception_effect_phi, if_exception_merge);
if_exception_merge->ReplaceInput(exception_node_index,
iterator_exception_node);
if_exception_effect_phi->ReplaceInput(exception_node_index,
iterator_exception_node);
if_exception_phi->ReplaceInput(exception_node_index,
iterator_exception_node);
exception_node_index++;
}
// Load iterator property operator
NameRef iterator_symbol = MakeRef(broker(), factory()->iterator_symbol());
const Operator* load_op =
@ -1474,32 +1512,15 @@ Reduction JSNativeContextSpecialization::ReduceJSGetIterator(Node* node) {
effect = load_property;
control = load_property;
// Handle exception path for the load named property
Node* iterator_exception_node = nullptr;
if (NodeProperties::IsExceptionalCall(node, &iterator_exception_node)) {
// If there exists an exception node for the given iterator_node, create a
// pair of IfException/IfSuccess nodes on the current control path. The uses
// of new exception node are merged with the original exception node. The
// IfSuccess node is returned as a control path for further reduction.
Node* exception_node =
// Merge the exception path for LoadNamed.
if (has_exception_node) {
Node* if_exception =
graph()->NewNode(common()->IfException(), effect, control);
Node* if_success = graph()->NewNode(common()->IfSuccess(), control);
// Use dead_node as a placeholder for the original exception node until
// its uses are rewired to the nodes merging the exceptions
Node* dead_node = jsgraph()->Dead();
Node* merge_node =
graph()->NewNode(common()->Merge(2), dead_node, exception_node);
Node* effect_phi = graph()->NewNode(common()->EffectPhi(2), dead_node,
exception_node, merge_node);
Node* phi =
graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
dead_node, exception_node, merge_node);
ReplaceWithValue(iterator_exception_node, phi, effect_phi, merge_node);
phi->ReplaceInput(0, iterator_exception_node);
effect_phi->ReplaceInput(0, iterator_exception_node);
merge_node->ReplaceInput(0, iterator_exception_node);
control = if_success;
if_exception_merge->ReplaceInput(exception_node_index, if_exception);
if_exception_phi->ReplaceInput(exception_node_index, if_exception);
if_exception_effect_phi->ReplaceInput(exception_node_index, if_exception);
exception_node_index++;
control = graph()->NewNode(common()->IfSuccess(), control);
}
// Eager deopt of call iterator property
@ -1521,11 +1542,73 @@ Reduction JSNativeContextSpecialization::ReduceJSGetIterator(Node* node) {
JSCallNode::ArityForArgc(0), CallFrequency(), p.callFeedback(),
ConvertReceiverMode::kNotNullOrUndefined, mode,
CallFeedbackRelation::kTarget);
Node* call_property =
// Lazy deopt to check the call result is JSReceiver.
Node* call_lazy_deopt_frame_state = CreateStubBuiltinContinuationFrameState(
jsgraph(), Builtin::kCallIteratorWithFeedbackLazyDeoptContinuation,
context, nullptr, 0, frame_state, ContinuationFrameStateMode::LAZY);
Node* call_property = effect = control =
graph()->NewNode(call_op, load_property, receiver, n.feedback_vector(),
context, frame_state, effect, control);
context, call_lazy_deopt_frame_state, effect, control);
return Replace(call_property);
// Merge the exception path for Call.
if (has_exception_node) {
Node* if_exception =
graph()->NewNode(common()->IfException(), effect, control);
if_exception_merge->ReplaceInput(exception_node_index, if_exception);
if_exception_phi->ReplaceInput(exception_node_index, if_exception);
if_exception_effect_phi->ReplaceInput(exception_node_index, if_exception);
exception_node_index++;
control = graph()->NewNode(common()->IfSuccess(), control);
}
// If the result is not JSReceiver, throw invalid iterator exception.
Node* is_receiver =
graph()->NewNode(simplified()->ObjectIsReceiver(), call_property);
Node* branch_node = graph()->NewNode(common()->Branch(BranchHint::kTrue),
is_receiver, control);
{
Node* if_not_receiver = graph()->NewNode(common()->IfFalse(), branch_node);
Node* effect_not_receiver = effect;
Node* control_not_receiver = if_not_receiver;
Node* call_runtime = effect_not_receiver = control_not_receiver =
graph()->NewNode(
javascript()->CallRuntime(Runtime::kThrowSymbolIteratorInvalid, 0),
context, frame_state, effect_not_receiver, control_not_receiver);
// Merge the exception path for CallRuntime.
if (has_exception_node) {
Node* if_exception = graph()->NewNode(
common()->IfException(), effect_not_receiver, control_not_receiver);
if_exception_merge->ReplaceInput(exception_node_index, if_exception);
if_exception_phi->ReplaceInput(exception_node_index, if_exception);
if_exception_effect_phi->ReplaceInput(exception_node_index, if_exception);
exception_node_index++;
control_not_receiver =
graph()->NewNode(common()->IfSuccess(), control_not_receiver);
}
Node* throw_node =
graph()->NewNode(common()->Throw(), call_runtime, control_not_receiver);
NodeProperties::MergeControlToEnd(graph(), common(), throw_node);
}
Node* if_receiver = graph()->NewNode(common()->IfTrue(), branch_node);
ReplaceWithValue(node, call_property, effect, if_receiver);
if (has_exception_node) {
DCHECK_EQ(exception_node_index, if_exception_merge->InputCount());
DCHECK_EQ(exception_node_index, if_exception_effect_phi->InputCount() - 1);
DCHECK_EQ(exception_node_index, if_exception_phi->InputCount() - 1);
#ifdef DEBUG
for (Node* input : if_exception_merge->inputs()) {
DCHECK(!input->IsDead());
}
for (Node* input : if_exception_effect_phi->inputs()) {
DCHECK(!input->IsDead());
}
for (Node* input : if_exception_phi->inputs()) {
DCHECK(!input->IsDead());
}
#endif
}
return Replace(if_receiver);
}
Reduction JSNativeContextSpecialization::ReduceJSSetNamedProperty(Node* node) {

View File

@ -6342,16 +6342,11 @@ void BytecodeGenerator::BuildGetIterator(IteratorType hint) {
feedback_index(feedback_spec()->AddCallICSlot());
// Let method be GetMethod(obj, @@iterator) and
// iterator be Call(method, obj).
// iterator be Call(method, obj). If iterator is
// not JSReceiver, then throw TypeError.
builder()->StoreAccumulatorInRegister(obj).GetIterator(
obj, load_feedback_index, call_feedback_index);
}
// If Type(iterator) is not Object, throw a TypeError exception.
BytecodeLabel no_type_error;
builder()->JumpIfJSReceiver(&no_type_error);
builder()->CallRuntime(Runtime::kThrowSymbolIteratorInvalid);
builder()->Bind(&no_type_error);
}
}

View File

@ -2931,10 +2931,8 @@ IGNITION_HANDLER(ForInStep, InterpreterAssembler) {
// GetIterator <object>
//
// Retrieves the object[Symbol.iterator] method, calls it and stores
// the result in the accumulator
// TODO(swapnilgaikwad): Extend the functionality of the bytecode to
// check if the result is a JSReceiver else throw SymbolIteratorInvalid
// runtime exception
// the result in the accumulator. If the result is not JSReceiver,
// throw SymbolIteratorInvalid runtime exception.
IGNITION_HANDLER(GetIterator, InterpreterAssembler) {
TNode<Object> receiver = LoadRegisterAtOperandIndex(0);
TNode<Context> context = GetContext();

View File

@ -138,7 +138,7 @@ snippet: "
"
frame size: 6
parameter count: 1
bytecode array length: 68
bytecode array length: 61
bytecodes: [
/* 42 S> */ B(CreateArrayLiteral), U8(0), U8(0), U8(37),
B(Star0),
@ -147,8 +147,6 @@ bytecodes: [
B(LdaSmi), I8(1),
/* 67 S> */ B(Star1),
/* 67 E> */ B(GetIterator), R(0), U8(2), U8(4),
B(JumpIfJSReceiver), U8(7),
B(CallRuntime), U16(Runtime::kThrowSymbolIteratorInvalid), R(0), U8(0),
B(Star4),
B(GetNamedProperty), R(4), U8(2), U8(6),
B(Star3),

View File

@ -210,7 +210,7 @@ snippet: "
"
frame size: 18
parameter count: 1
bytecode array length: 311
bytecode array length: 304
bytecodes: [
B(SwitchOnGeneratorState), R(0), U8(0), U8(2),
B(Mov), R(closure), R(4),
@ -230,12 +230,10 @@ bytecodes: [
B(LdaSmi), I8(1),
B(Star4),
B(Mov), R(8), R(5),
B(Jump), U8(222),
B(Jump), U8(215),
/* 36 S> */ B(CreateArrayLiteral), U8(4), U8(0), U8(37),
B(Star10),
B(GetIterator), R(10), U8(1), U8(3),
B(JumpIfJSReceiver), U8(7),
B(CallRuntime), U16(Runtime::kThrowSymbolIteratorInvalid), R(0), U8(0),
B(Star9),
B(GetNamedProperty), R(9), U8(5), U8(5),
B(Star8),
@ -360,7 +358,7 @@ bytecodes: [
]
constant pool: [
Smi [28],
Smi [130],
Smi [123],
Smi [15],
Smi [7],
ARRAY_BOILERPLATE_DESCRIPTION_TYPE,
@ -378,10 +376,10 @@ constant pool: [
Smi [22],
]
handlers: [
[18, 269, 269],
[21, 240, 240],
[79, 160, 166],
[179, 200, 202],
[18, 262, 262],
[21, 233, 233],
[72, 153, 159],
[172, 193, 195],
]
---

View File

@ -65,7 +65,7 @@ snippet: "
"
frame size: 7
parameter count: 1
bytecode array length: 89
bytecode array length: 82
bytecodes: [
/* 34 S> */ B(LdaGlobal), U8(0), U8(0),
B(Star1),
@ -78,8 +78,6 @@ bytecodes: [
/* 49 S> */ B(CreateArrayLiteral), U8(3), U8(5), U8(37),
B(Star6),
/* 49 E> */ B(GetIterator), R(6), U8(6), U8(8),
B(JumpIfJSReceiver), U8(7),
B(CallRuntime), U16(Runtime::kThrowSymbolIteratorInvalid), R(0), U8(0),
B(Star5),
B(GetNamedProperty), R(5), U8(4), U8(10),
B(Star4),

View File

@ -12,18 +12,16 @@ snippet: "
"
frame size: 13
parameter count: 1
bytecode array length: 129
bytecode array length: 122
bytecodes: [
/* 45 S> */ B(CreateArrayLiteral), U8(0), U8(0), U8(37),
B(Star1),
/* 60 S> */ B(GetIterator), R(1), U8(1), U8(3),
B(Mov), R(1), R(2),
B(JumpIfJSReceiver), U8(7),
B(CallRuntime), U16(Runtime::kThrowSymbolIteratorInvalid), R(0), U8(0),
B(Star4),
B(GetNamedProperty), R(4), U8(1), U8(5),
B(Star3),
B(LdaFalse),
B(Mov), R(1), R(2),
B(Star5),
B(Mov), R(context), R(8),
/* 57 S> */ B(Ldar), R(5),
@ -89,8 +87,8 @@ constant pool: [
ONE_BYTE_INTERNALIZED_STRING_TYPE ["return"],
]
handlers: [
[30, 67, 73],
[86, 105, 107],
[23, 60, 66],
[79, 98, 100],
]
---
@ -100,18 +98,16 @@ snippet: "
"
frame size: 14
parameter count: 1
bytecode array length: 204
bytecode array length: 197
bytecodes: [
/* 48 S> */ B(CreateArrayLiteral), U8(0), U8(0), U8(37),
B(Star2),
/* 69 S> */ B(GetIterator), R(2), U8(1), U8(3),
B(Mov), R(2), R(3),
B(JumpIfJSReceiver), U8(7),
B(CallRuntime), U16(Runtime::kThrowSymbolIteratorInvalid), R(0), U8(0),
B(Star5),
B(GetNamedProperty), R(5), U8(1), U8(5),
B(Star4),
B(LdaFalse),
B(Mov), R(2), R(3),
B(Star6),
B(Mov), R(context), R(9),
B(Ldar), R(6),
@ -210,8 +206,8 @@ constant pool: [
ONE_BYTE_INTERNALIZED_STRING_TYPE ["return"],
]
handlers: [
[30, 142, 148],
[161, 180, 182],
[23, 135, 141],
[154, 173, 175],
]
---
@ -221,20 +217,18 @@ snippet: "
"
frame size: 15
parameter count: 1
bytecode array length: 175
bytecode array length: 168
bytecodes: [
/* 40 S> */ B(CreateEmptyObjectLiteral),
B(Star0),
/* 51 S> */ B(CreateArrayLiteral), U8(0), U8(0), U8(37),
B(Star2),
/* 68 S> */ B(GetIterator), R(2), U8(1), U8(3),
B(Mov), R(2), R(3),
B(JumpIfJSReceiver), U8(7),
B(CallRuntime), U16(Runtime::kThrowSymbolIteratorInvalid), R(0), U8(0),
B(Star5),
B(GetNamedProperty), R(5), U8(1), U8(5),
B(Star4),
B(LdaFalse),
B(Mov), R(2), R(3),
B(Star6),
B(Mov), R(context), R(9),
/* 59 S> */ B(Ldar), R(6),
@ -320,8 +314,8 @@ constant pool: [
ONE_BYTE_INTERNALIZED_STRING_TYPE ["return"],
]
handlers: [
[32, 113, 119],
[132, 151, 153],
[25, 106, 112],
[125, 144, 146],
]
---

View File

@ -485,7 +485,7 @@ snippet: "
"
frame size: 14
parameter count: 1
bytecode array length: 198
bytecode array length: 191
bytecodes: [
B(Mov), R(closure), R(2),
B(Mov), R(this), R(3),
@ -497,8 +497,6 @@ bytecodes: [
/* 68 S> */ B(CreateArrayLiteral), U8(1), U8(1), U8(37),
B(Star5),
B(GetIterator), R(5), U8(2), U8(4),
B(JumpIfJSReceiver), U8(7),
B(CallRuntime), U16(Runtime::kThrowSymbolIteratorInvalid), R(0), U8(0),
B(Star4),
B(GetNamedProperty), R(4), U8(2), U8(6),
B(Star3),
@ -594,8 +592,8 @@ constant pool: [
SCOPE_INFO_TYPE,
]
handlers: [
[14, 176, 176],
[46, 92, 98],
[111, 130, 132],
[14, 169, 169],
[39, 85, 91],
[104, 123, 125],
]

View File

@ -11,13 +11,11 @@ snippet: "
"
frame size: 12
parameter count: 1
bytecode array length: 128
bytecode array length: 121
bytecodes: [
/* 48 S> */ B(CreateArrayLiteral), U8(0), U8(0), U8(37),
B(Star4),
B(GetIterator), R(4), U8(1), U8(3),
B(JumpIfJSReceiver), U8(7),
B(CallRuntime), U16(Runtime::kThrowSymbolIteratorInvalid), R(0), U8(0),
B(Star3),
B(GetNamedProperty), R(3), U8(1), U8(5),
B(Star2),
@ -85,8 +83,8 @@ constant pool: [
ONE_BYTE_INTERNALIZED_STRING_TYPE ["return"],
]
handlers: [
[27, 66, 72],
[85, 104, 106],
[20, 59, 65],
[78, 97, 99],
]
---
@ -96,13 +94,11 @@ snippet: "
"
frame size: 13
parameter count: 1
bytecode array length: 134
bytecode array length: 127
bytecodes: [
/* 42 S> */ B(LdaConstant), U8(0),
B(Star0),
/* 68 S> */ B(GetIterator), R(0), U8(0), U8(2),
B(JumpIfJSReceiver), U8(7),
B(CallRuntime), U16(Runtime::kThrowSymbolIteratorInvalid), R(0), U8(0),
B(Star4),
B(GetNamedProperty), R(4), U8(1), U8(4),
B(Star3),
@ -176,8 +172,8 @@ constant pool: [
Smi [9],
]
handlers: [
[25, 66, 72],
[85, 104, 106],
[18, 59, 65],
[78, 97, 99],
]
---
@ -189,13 +185,11 @@ snippet: "
"
frame size: 12
parameter count: 1
bytecode array length: 144
bytecode array length: 137
bytecodes: [
/* 48 S> */ B(CreateArrayLiteral), U8(0), U8(0), U8(37),
B(Star4),
B(GetIterator), R(4), U8(1), U8(3),
B(JumpIfJSReceiver), U8(7),
B(CallRuntime), U16(Runtime::kThrowSymbolIteratorInvalid), R(0), U8(0),
B(Star3),
B(GetNamedProperty), R(3), U8(1), U8(5),
B(Star2),
@ -270,8 +264,8 @@ constant pool: [
ONE_BYTE_INTERNALIZED_STRING_TYPE ["return"],
]
handlers: [
[27, 82, 88],
[101, 120, 122],
[20, 75, 81],
[94, 113, 115],
]
---
@ -281,15 +275,13 @@ snippet: "
"
frame size: 12
parameter count: 1
bytecode array length: 146
bytecode array length: 139
bytecodes: [
/* 42 S> */ B(CreateObjectLiteral), U8(0), U8(0), U8(41),
B(Star0),
/* 77 S> */ B(CreateArrayLiteral), U8(1), U8(1), U8(37),
B(Star3),
B(GetIterator), R(3), U8(2), U8(4),
B(JumpIfJSReceiver), U8(7),
B(CallRuntime), U16(Runtime::kThrowSymbolIteratorInvalid), R(0), U8(0),
B(Star2),
B(GetNamedProperty), R(2), U8(2), U8(6),
B(Star1),
@ -367,7 +359,7 @@ constant pool: [
Smi [9],
]
handlers: [
[32, 78, 84],
[97, 116, 118],
[25, 71, 77],
[90, 109, 111],
]

View File

@ -15,11 +15,9 @@ snippet: "
"
frame size: 14
parameter count: 2
bytecode array length: 126
bytecode array length: 119
bytecodes: [
/* 34 S> */ B(GetIterator), R(arg0), U8(0), U8(2),
B(JumpIfJSReceiver), U8(7),
B(CallRuntime), U16(Runtime::kThrowSymbolIteratorInvalid), R(0), U8(0),
B(Star5),
B(GetNamedProperty), R(5), U8(0), U8(4),
B(Star4),
@ -87,8 +85,8 @@ constant pool: [
ONE_BYTE_INTERNALIZED_STRING_TYPE ["return"],
]
handlers: [
[22, 64, 70],
[83, 102, 104],
[15, 57, 63],
[76, 95, 97],
]
---
@ -100,7 +98,7 @@ snippet: "
"
frame size: 20
parameter count: 2
bytecode array length: 206
bytecode array length: 199
bytecodes: [
/* 10 E> */ B(CreateFunctionContext), U8(0), U8(5),
B(PushContext), R(2),
@ -119,8 +117,6 @@ bytecodes: [
/* 34 S> */ B(LdaContextSlot), R(3), U8(4), U8(0),
B(Star6),
B(GetIterator), R(6), U8(0), U8(2),
B(JumpIfJSReceiver), U8(7),
B(CallRuntime), U16(Runtime::kThrowSymbolIteratorInvalid), R(0), U8(0),
B(Star5),
B(GetNamedProperty), R(5), U8(2), U8(4),
B(Star4),
@ -215,8 +211,8 @@ constant pool: [
ONE_BYTE_INTERNALIZED_STRING_TYPE ["return"],
]
handlers: [
[54, 142, 148],
[161, 180, 182],
[47, 135, 141],
[154, 173, 175],
]
---
@ -228,11 +224,9 @@ snippet: "
"
frame size: 13
parameter count: 2
bytecode array length: 142
bytecode array length: 135
bytecodes: [
/* 34 S> */ B(GetIterator), R(arg0), U8(0), U8(2),
B(JumpIfJSReceiver), U8(7),
B(CallRuntime), U16(Runtime::kThrowSymbolIteratorInvalid), R(0), U8(0),
B(Star3),
B(GetNamedProperty), R(3), U8(0), U8(4),
B(Star2),
@ -310,8 +304,8 @@ constant pool: [
ONE_BYTE_INTERNALIZED_STRING_TYPE ["return"],
]
handlers: [
[22, 80, 86],
[99, 118, 120],
[15, 73, 79],
[92, 111, 113],
]
---
@ -323,11 +317,9 @@ snippet: "
"
frame size: 16
parameter count: 2
bytecode array length: 134
bytecode array length: 127
bytecodes: [
/* 41 S> */ B(GetIterator), R(arg0), U8(0), U8(2),
B(JumpIfJSReceiver), U8(7),
B(CallRuntime), U16(Runtime::kThrowSymbolIteratorInvalid), R(0), U8(0),
B(Star7),
B(GetNamedProperty), R(7), U8(0), U8(4),
B(Star6),
@ -401,8 +393,8 @@ constant pool: [
ONE_BYTE_INTERNALIZED_STRING_TYPE ["return"],
]
handlers: [
[22, 72, 78],
[91, 110, 112],
[15, 65, 71],
[84, 103, 105],
]
---
@ -414,7 +406,7 @@ snippet: "
"
frame size: 15
parameter count: 2
bytecode array length: 165
bytecode array length: 158
bytecodes: [
B(SwitchOnGeneratorState), R(0), U8(0), U8(1),
B(Mov), R(closure), R(5),
@ -431,8 +423,6 @@ bytecodes: [
B(Ldar), R(5),
B(Return),
/* 35 S> */ B(GetIterator), R(arg0), U8(0), U8(2),
B(JumpIfJSReceiver), U8(7),
B(CallRuntime), U16(Runtime::kThrowSymbolIteratorInvalid), R(0), U8(0),
B(Star6),
B(GetNamedProperty), R(6), U8(3), U8(4),
B(Star5),
@ -503,8 +493,8 @@ constant pool: [
ONE_BYTE_INTERNALIZED_STRING_TYPE ["return"],
]
handlers: [
[61, 103, 109],
[122, 141, 143],
[54, 96, 102],
[115, 134, 136],
]
---
@ -516,7 +506,7 @@ snippet: "
"
frame size: 14
parameter count: 2
bytecode array length: 206
bytecode array length: 199
bytecodes: [
B(SwitchOnGeneratorState), R(0), U8(0), U8(2),
B(Mov), R(closure), R(4),
@ -533,8 +523,6 @@ bytecodes: [
B(Ldar), R(4),
B(Return),
/* 35 S> */ B(GetIterator), R(arg0), U8(0), U8(2),
B(JumpIfJSReceiver), U8(7),
B(CallRuntime), U16(Runtime::kThrowSymbolIteratorInvalid), R(0), U8(0),
B(Star5),
B(GetNamedProperty), R(5), U8(4), U8(4),
B(Star4),
@ -613,7 +601,7 @@ bytecodes: [
]
constant pool: [
Smi [20],
Smi [108],
Smi [101],
Smi [10],
Smi [7],
ONE_BYTE_INTERNALIZED_STRING_TYPE ["next"],
@ -626,8 +614,8 @@ constant pool: [
Smi [9],
]
handlers: [
[61, 138, 144],
[157, 176, 178],
[54, 131, 137],
[150, 169, 171],
]
---
@ -639,7 +627,7 @@ snippet: "
"
frame size: 16
parameter count: 2
bytecode array length: 170
bytecode array length: 163
bytecodes: [
B(Mov), R(closure), R(5),
B(Mov), R(this), R(6),
@ -647,8 +635,6 @@ bytecodes: [
B(Star0),
B(Mov), R(context), R(5),
/* 40 S> */ B(GetIterator), R(arg0), U8(0), U8(2),
B(JumpIfJSReceiver), U8(7),
B(CallRuntime), U16(Runtime::kThrowSymbolIteratorInvalid), R(0), U8(0),
B(Star7),
B(GetNamedProperty), R(7), U8(0), U8(4),
B(Star6),
@ -732,9 +718,9 @@ constant pool: [
SCOPE_INFO_TYPE,
]
handlers: [
[14, 148, 148],
[36, 78, 84],
[97, 116, 118],
[14, 141, 141],
[29, 71, 77],
[90, 109, 111],
]
---
@ -746,7 +732,7 @@ snippet: "
"
frame size: 15
parameter count: 2
bytecode array length: 204
bytecode array length: 197
bytecodes: [
B(SwitchOnGeneratorState), R(0), U8(0), U8(1),
B(Mov), R(closure), R(4),
@ -755,8 +741,6 @@ bytecodes: [
B(Star0),
B(Mov), R(context), R(4),
/* 40 S> */ B(GetIterator), R(arg0), U8(0), U8(2),
B(JumpIfJSReceiver), U8(7),
B(CallRuntime), U16(Runtime::kThrowSymbolIteratorInvalid), R(0), U8(0),
B(Star6),
B(GetNamedProperty), R(6), U8(1), U8(4),
B(Star5),
@ -845,7 +829,7 @@ bytecodes: [
B(Return),
]
constant pool: [
Smi [88],
Smi [81],
ONE_BYTE_INTERNALIZED_STRING_TYPE ["next"],
ONE_BYTE_INTERNALIZED_STRING_TYPE ["done"],
ONE_BYTE_INTERNALIZED_STRING_TYPE ["value"],
@ -853,8 +837,8 @@ constant pool: [
SCOPE_INFO_TYPE,
]
handlers: [
[18, 182, 182],
[40, 112, 118],
[131, 150, 152],
[18, 175, 175],
[33, 105, 111],
[124, 143, 145],
]

View File

@ -98,7 +98,7 @@ snippet: "
"
frame size: 14
parameter count: 1
bytecode array length: 211
bytecode array length: 204
bytecodes: [
B(SwitchOnGeneratorState), R(0), U8(0), U8(2),
B(Mov), R(closure), R(4),
@ -117,8 +117,6 @@ bytecodes: [
/* 30 S> */ B(CreateArrayLiteral), U8(4), U8(0), U8(37),
B(Star6),
B(GetIterator), R(6), U8(1), U8(3),
B(JumpIfJSReceiver), U8(7),
B(CallRuntime), U16(Runtime::kThrowSymbolIteratorInvalid), R(0), U8(0),
B(Star5),
B(GetNamedProperty), R(5), U8(5), U8(5),
B(Star4),
@ -197,7 +195,7 @@ bytecodes: [
]
constant pool: [
Smi [20],
Smi [113],
Smi [106],
Smi [10],
Smi [7],
ARRAY_BOILERPLATE_DESCRIPTION_TYPE,
@ -211,8 +209,8 @@ constant pool: [
Smi [9],
]
handlers: [
[66, 143, 149],
[162, 181, 183],
[59, 136, 142],
[155, 174, 176],
]
---
@ -223,7 +221,7 @@ snippet: "
"
frame size: 7
parameter count: 1
bytecode array length: 189
bytecode array length: 182
bytecodes: [
B(SwitchOnGeneratorState), R(0), U8(0), U8(2),
B(Mov), R(closure), R(1),
@ -244,8 +242,6 @@ bytecodes: [
/* 50 E> */ B(CallUndefinedReceiver0), R(5), U8(2),
B(Star6),
B(GetIterator), R(6), U8(4), U8(6),
B(JumpIfJSReceiver), U8(7),
B(CallRuntime), U16(Runtime::kThrowSymbolIteratorInvalid), R(0), U8(0),
B(Star3),
B(GetNamedProperty), R(3), U8(5), U8(8),
B(Star5),
@ -302,7 +298,7 @@ bytecodes: [
]
constant pool: [
Smi [20],
Smi [159],
Smi [152],
Smi [10],
Smi [7],
ONE_BYTE_INTERNALIZED_STRING_TYPE ["g"],

View File

@ -90,7 +90,7 @@ snippet: "
"
frame size: 7
parameter count: 1
bytecode array length: 111
bytecode array length: 104
bytecodes: [
B(CreateBlockContext), U8(0),
B(PushContext), R(1),
@ -112,12 +112,10 @@ bytecodes: [
/* 101 S> */ B(CreateArrayLiteral), U8(4), U8(1), U8(37),
B(Star6),
/* 101 E> */ B(GetIterator), R(6), U8(2), U8(4),
B(Mov), R(4), R(1),
B(JumpIfJSReceiver), U8(7),
B(CallRuntime), U16(Runtime::kThrowSymbolIteratorInvalid), R(0), U8(0),
B(Star5),
B(GetNamedProperty), R(5), U8(5), U8(6),
B(Star4),
B(Mov), R(0), R(1),
B(CallProperty0), R(4), R(5), U8(15),
B(Star6),
B(JumpIfJSReceiver), U8(7),

View File

@ -93,7 +93,7 @@ snippet: "
"
frame size: 11
parameter count: 1
bytecode array length: 104
bytecode array length: 97
bytecodes: [
/* 128 E> */ B(CreateRestParameter),
B(Star3),
@ -106,12 +106,10 @@ bytecodes: [
B(LdaSmi), I8(1),
/* 152 S> */ B(Star6),
/* 152 E> */ B(GetIterator), R(3), U8(1), U8(3),
B(Mov), R(1), R(4),
B(JumpIfJSReceiver), U8(7),
B(CallRuntime), U16(Runtime::kThrowSymbolIteratorInvalid), R(0), U8(0),
B(Star9),
B(GetNamedProperty), R(9), U8(1), U8(5),
B(Star8),
B(Mov), R(1), R(4),
B(CallProperty0), R(8), R(9), U8(14),
B(Star10),
B(JumpIfJSReceiver), U8(7),

View File

@ -0,0 +1,66 @@
// Copyright 2022 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.
// The GetIterator bytecode is used to implement a part of the iterator
// protocol (https://tc39.es/ecma262/#sec-getiterator). Here, the
// bytecode performs multiple operations including some that have side-effects
// and may deoptimize eagerly or lazily.
// This test ensures the call @@iterator lazy deoptimization is handled correctly.
// Flags: --allow-natives-syntax --no-always-opt
let getIteratorCount = 0;
let iteratorCount = 0;
let iteratorAfterEagerDeoptCount = 0;
let triggerLazyDeopt = false;
function foo(obj) {
// The following for-of loop uses the iterator protocol to iterate
// over the 'obj'.
// The GetIterator bytecode involves 3 steps:
// 1. method = GetMethod(obj, @@iterator)
// 2. iterator = Call(method, obj).
// 3. if(!IsJSReceiver(iterator)) throw SymbolIteratorInvalid.
for (var x of obj) {
}
}
// The lazy deoptimization is triggerred by setting the
// 'triggerLazyDeopt' to true. And the lazy deopt should
// goto continuation to check the call result is JSReceiver.
var iterator =
function () {
if (triggerLazyDeopt) {
iteratorAfterEagerDeoptCount++;
%DeoptimizeFunction(foo);
// SymbolIteratorInvalid should be throwed.
return 1;
}
iteratorCount++;
return {
next: function () {
return { done: true };
}
}
}
let y = {
get [Symbol.iterator]() {
getIteratorCount++;
return iterator;
}
};
%PrepareFunctionForOptimization(foo);
foo(y);
foo(y);
%OptimizeFunctionOnNextCall(foo);
triggerLazyDeopt = true;
assertThrows(() => {
foo(y)
}, TypeError, "Result of the Symbol.iterator method is not an object")
assertUnoptimized(foo);
assertEquals(getIteratorCount, 3);
assertEquals(iteratorCount, 2);
assertEquals(iteratorAfterEagerDeoptCount, 1);

View File

@ -17,9 +17,10 @@ var iteratorAfterEagerDeoptCount = 0;
function foo(obj) {
// The following for-of loop uses the iterator protocol to iterate
// over the 'obj'.
// The GetIterator bytecode invovlves 2 steps:
// The GetIterator bytecode involves 3 steps:
// 1. method = GetMethod(obj, @@iterator)
// 2. iterator = Call(method, obj).
// 3. if(!IsJSReceiver(iterator)) throw SymbolIteratorInvalid.
for(var x of obj){}
}

View File

@ -6,7 +6,7 @@
// protocol (https://tc39.es/ecma262/#sec-getiterator). Here, the
// bytecode performs multiple operations including some that have side-effects
// and may deoptimize eagerly or lazily.
// This test ensures the lazy deoptimization is handled correctly.
// This test ensures the get @@iterator lazy deoptimization is handled correctly.
// Flags: --allow-natives-syntax --no-always-opt
@ -18,9 +18,10 @@ var getIteratorCount = 0;
function foo(obj) {
// The following for-of loop uses the iterator protocol to iterate
// over the 'obj'.
// The GetIterator bytecode invovlves 2 steps:
// The GetIterator bytecode involves 3 steps:
// 1. method = GetMethod(obj, @@iterator)
// 2. iterator = Call(method, obj).
// 3. if(!IsJSReceiver(iterator)) throw SymbolIteratorInvalid.
for(var x of obj){}
}

View File

@ -0,0 +1,51 @@
// Copyright 2022 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.
// The GetIterator bytecode is used to implement a part of the iterator
// protocol (https://tc39.es/ecma262/#sec-getiterator).
// Here, calling @@iterator property returns invalid JS receiver.
// This test ensures that the optimized version of the GetIterator bytecode
// correctly handle the SymbolIteratorInvalid exception without deoptimizing.
// Flags: --allow-natives-syntax --opt
var iteratorCount = 0;
var exceptionCount = 0;
function foo(obj) {
// The following for-of loop uses the iterator protocol to iterate
// over the 'obj'.
// The GetIterator bytecode involves 3 steps:
// 1. method = GetMethod(obj, @@iterator)
// 2. iterator = Call(method, obj)
// 3. if(!IsJSReceiver(iterator)) throw SymbolIteratorInvalid.
try{
for(let a of obj){
assertUnreachable();
}
} catch(e){
exceptionCount++;
}
}
// This iterator retuns '1' which is not a valid JSReceiver
var iterator = function() {
iteratorCount++;
return 1;
}
let y = {
get [Symbol.iterator]() {
return iterator;
}
};
%PrepareFunctionForOptimization(foo);
foo(y);
foo(y);
%OptimizeFunctionOnNextCall(foo);
foo(y);
assertOptimized(foo);
assertEquals(iteratorCount, 3);
assertEquals(exceptionCount, 3);