[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:
parent
0603f8a953
commit
4a285a2549
@ -389,6 +389,7 @@ extern enum MessageTemplate {
|
||||
kInvalidCountValue,
|
||||
kConstructorNotFunction,
|
||||
kSymbolToString,
|
||||
kSymbolIteratorInvalid,
|
||||
kPropertyNotFunction,
|
||||
kBigIntTooBig,
|
||||
kNotTypedArray,
|
||||
|
@ -590,6 +590,7 @@ namespace internal {
|
||||
\
|
||||
/* Iterator Protocol */ \
|
||||
TFC(GetIteratorWithFeedbackLazyDeoptContinuation, GetIteratorStackParameter) \
|
||||
TFC(CallIteratorWithFeedbackLazyDeoptContinuation, SingleParameterOnStack) \
|
||||
\
|
||||
/* Global object */ \
|
||||
CPP(GlobalDecodeURI) \
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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),
|
||||
|
@ -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],
|
||||
]
|
||||
|
||||
---
|
||||
|
@ -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),
|
||||
|
@ -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],
|
||||
]
|
||||
|
||||
---
|
||||
|
@ -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],
|
||||
]
|
||||
|
||||
|
@ -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],
|
||||
]
|
||||
|
||||
|
@ -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],
|
||||
]
|
||||
|
||||
|
@ -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"],
|
||||
|
@ -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),
|
||||
|
@ -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),
|
||||
|
66
test/mjsunit/es6/iterator-call-lazy-deopt.js
Normal file
66
test/mjsunit/es6/iterator-call-lazy-deopt.js
Normal 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);
|
@ -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){}
|
||||
}
|
||||
|
||||
|
@ -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){}
|
||||
}
|
||||
|
51
test/mjsunit/es6/iterator-invalid-receiver-opt.js
Normal file
51
test/mjsunit/es6/iterator-invalid-receiver-opt.js
Normal 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);
|
Loading…
Reference in New Issue
Block a user