Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion jerry-core/ecma/base/ecma-gc.c
Original file line number Diff line number Diff line change
Expand Up @@ -815,7 +815,7 @@ ecma_gc_free_executable_object (ecma_object_t *object_p) /**< object */

do
{
context_top_p[-1] &= (uint32_t) ~VM_CONTEXT_HAS_LEX_ENV;
context_top_p[-1] &= (uint32_t) ~(VM_CONTEXT_HAS_LEX_ENV | VM_CONTEXT_CLOSE_ITERATOR);

uint32_t offsets = vm_get_context_value_offsets (context_top_p);

Expand Down
10 changes: 9 additions & 1 deletion jerry-core/vm/vm-stack.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "ecma-helpers.h"
#include "vm-defines.h"
#include "vm-stack.h"
#include "ecma-iterator-object.h"

/** \addtogroup vm Virtual machine
* @{
Expand Down Expand Up @@ -79,8 +80,15 @@ vm_stack_context_abort (vm_frame_ctx_t *frame_ctx_p, /**< frame context */
#if ENABLED (JERRY_ES2015)
case VM_CONTEXT_FOR_OF:
{
ecma_value_t iterator = vm_stack_top_p[-3];

if (context_info & VM_CONTEXT_CLOSE_ITERATOR)
{
ecma_op_iterator_close (iterator);
}
ecma_free_value (iterator);

ecma_free_value (vm_stack_top_p[-2]);
ecma_free_value (vm_stack_top_p[-3]);
VM_MINUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION);
vm_stack_top_p -= PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION;
break;
Expand Down
11 changes: 8 additions & 3 deletions jerry-core/vm/vm-stack.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,13 @@
/**
* Create context on the vm stack.
*/
#define VM_CREATE_CONTEXT(type, end_offset) ((ecma_value_t) ((type) | ((end_offset) << 6)))
#define VM_CREATE_CONTEXT(type, end_offset) ((ecma_value_t) ((type) | ((end_offset) << 7)))

/**
* Create context on the vm stack with environment.
*/
#define VM_CREATE_CONTEXT_WITH_ENV(type, end_offset) \
((ecma_value_t) ((type) | ((end_offset) << 6) | VM_CONTEXT_HAS_LEX_ENV))
(VM_CREATE_CONTEXT ((type),(end_offset)) | VM_CONTEXT_HAS_LEX_ENV)

/**
* Get type of a vm context.
Expand All @@ -44,13 +44,18 @@
/**
* Get the end position of a vm context.
*/
#define VM_GET_CONTEXT_END(value) ((value) >> 6)
#define VM_GET_CONTEXT_END(value) ((value) >> 7)

/**
* This flag is set if the context has a lexical environment.
*/
#define VM_CONTEXT_HAS_LEX_ENV 0x20

/**
* This flag is set if the iterator close operation should be invoked during a for-of context break.
*/
#define VM_CONTEXT_CLOSE_ITERATOR 0x40

/**
* Context types for the vm stack.
*/
Expand Down
29 changes: 28 additions & 1 deletion jerry-core/vm/vm.c
Original file line number Diff line number Diff line change
Expand Up @@ -3485,7 +3485,7 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */

VM_PLUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION);
stack_top_p += PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION;
stack_top_p[-1] = VM_CREATE_CONTEXT (VM_CONTEXT_FOR_OF, branch_offset);
stack_top_p[-1] = VM_CREATE_CONTEXT (VM_CONTEXT_FOR_OF, branch_offset) | VM_CONTEXT_CLOSE_ITERATOR;
stack_top_p[-2] = next_value;
stack_top_p[-3] = iterator;

Expand Down Expand Up @@ -3587,6 +3587,7 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */
case VM_OC_CONTEXT_END:
{
JERRY_ASSERT (VM_GET_REGISTERS (frame_ctx_p) + register_end + frame_ctx_p->context_depth == stack_top_p);
JERRY_ASSERT (!(stack_top_p[-1] & VM_CONTEXT_CLOSE_ITERATOR));

ecma_value_t context_type = VM_GET_CONTEXT_TYPE (stack_top_p[-1]);

Expand Down Expand Up @@ -3653,6 +3654,7 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */
case VM_OC_JUMP_AND_EXIT_CONTEXT:
{
JERRY_ASSERT (VM_GET_REGISTERS (frame_ctx_p) + register_end + frame_ctx_p->context_depth == stack_top_p);
JERRY_ASSERT (!jcontext_has_pending_exception ());

branch_offset += (int32_t) (byte_code_start_p - frame_ctx_p->byte_code_start_p);

Expand All @@ -3670,6 +3672,14 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */
byte_code_p = frame_ctx_p->byte_code_start_p + branch_offset;
}

#if ENABLED (JERRY_ES2015)
if (jcontext_has_pending_exception ())
{
result = ECMA_VALUE_ERROR;
goto error;
}
#endif /* ENABLED (JERRY_ES2015) */

JERRY_ASSERT (VM_GET_REGISTERS (frame_ctx_p) + register_end + frame_ctx_p->context_depth == stack_top_p);
continue;
}
Expand Down Expand Up @@ -3944,10 +3954,27 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */
JERRY_ASSERT (VM_GET_CONTEXT_TYPE (stack_top_p[-1]) == VM_CONTEXT_FINALLY_RETURN);
JERRY_ASSERT (VM_GET_REGISTERS (frame_ctx_p) + register_end + frame_ctx_p->context_depth == stack_top_p);

#if ENABLED (JERRY_ES2015)
if (jcontext_has_pending_exception ())
{
stack_top_p[-1] = (ecma_value_t) (stack_top_p[-1] - VM_CONTEXT_FINALLY_RETURN + VM_CONTEXT_FINALLY_THROW);
ecma_free_value (result);
result = jcontext_take_exception ();
}
#endif /* ENABLED (JERRY_ES2015) */

byte_code_p = frame_ctx_p->byte_code_p;
stack_top_p[-2] = result;
continue;
}

#if ENABLED (JERRY_ES2015)
if (jcontext_has_pending_exception ())
{
ecma_free_value (result);
result = ECMA_VALUE_ERROR;
}
#endif /* ENABLED (JERRY_ES2015) */
}
else if (jcontext_has_pending_exception () && !jcontext_has_pending_abort ())
{
Expand Down
244 changes: 244 additions & 0 deletions tests/jerry/es2015/for-of-iterator-close.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
// Copyright JS Foundation and other contributors, http://js.foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

function createIterable(arr, methods = {}) {
let iterable = function *() {
let idx = 0;
while (idx < arr.length) {
yield arr[idx];
idx++;
}
}();
iterable['return'] = methods['return'];
iterable['throw'] = methods['throw'];

return iterable;
};

function close1() {
var closed = false;
var iter = createIterable([1, 2, 3], {
'return': function() { closed = true; return {}; }
});
for (var it of iter) break;
return closed;
}

assert(close1());

function close2() {
var closed = false;
var iter = createIterable([1, 2, 3], {
'return': function() { closed = true; return {}; }
});
try {
for (var it of iter) throw 0;
assert(false);
} catch(e){
assert(e === 0);
}
return closed;
}

assert(close2());

function close3() {
var closed = false;
var iter = createIterable([1, 2, 3], {
'return': function() { closed = true; return {}; }
});
for (var it of iter) continue;
return closed;
}

assert(!close3());

function close4() {
var closed = false;
var iter = createIterable([1, 2, 3], {
'return': function() { closed = true; throw 6; }
});
try {
for (var it of iter) throw 5;
assert(false);
} catch(e) {
assert(e === 5);
}
return closed;
}

assert(close4());

function close5() {
var closed_called = 0;
var iter = createIterable([1, 2, 3], {
'return': function() { closed_called++; throw 6; }
});
try {
for (var it of iter) {
for (var it of iter) {
throw 5;
}
assert(false);
}
assert(false);
} catch(e) {
assert(e === 5);
}
return closed_called === 2;
}

assert(close5());

function close6() {
var closed = false;
var iter = createIterable([1, 2, 3], {
'return': function() { closed = true; return {}; }
});
for (var it of iter) {};

return closed;
}

assert(!close6());

var close7_result = false;
function close7() {
var iter = createIterable([1, 2, 3], {
'return': function() { close7_result = true; throw "5"; }
});

for (var it of iter) {
return "foo";
}
}

try {
close7();
assert(false);
} catch (e) {
assert(close7_result);
assert(e === "5");
}

function close8() {
var iter = createIterable([1, 2, 3], {
'return': function() { close8_result = true; throw "5"; }
});

for (var it of iter) {
throw "foo";
}
}

var close8_result = false;
try {
close8();
assert(false);
} catch (e) {
assert(e === "foo");
assert(close8_result);
}

function close9() {
var closed = false;
var iter = createIterable([1, 2, 3], {
'return': function() { closed = true; throw "5"; }
});

try {
for (var it of iter) {
break;
}
} finally {
assert(closed);
throw "foo"
}
}

try {
close9();
assert(false);
} catch (e) {
assert(e === "foo");
}

function close10() {
var closed = false;
var iter = createIterable([1, 2, 3], {
'return': function() { closed = true; return {}; }
});

try {
for (var it of iter) {
return "foo";
}
} finally {
assert(closed);
throw "bar";
}
}

try {
close10();
assert(false);
} catch (e) {
assert(e === "bar");
}

function close11() {
var closed = false;
var iter = createIterable([1, 2, 3], {
'return': function() { closed = true; throw "5"; }
});

try {
for (var it of iter) {
return "foo";
}
} finally {
assert(closed);
throw "bar";
}
}

try {
close11();
assert(false);
} catch (e) {
assert(e === "bar");
}

function close12() {
var closed = false;
var iter = createIterable([1, 2, 3], {
'return': function() { closed = true; throw "5"; }
});

try {
for (var it of iter) {
throw "foo";
}
} finally {
assert(closed);
throw "bar";
}
}

try {
close12();
assert(false);
} catch (e) {
assert(e === "bar");
}