diff --git a/jerry-core/parser/js/js-parser-internal.h b/jerry-core/parser/js/js-parser-internal.h
index a57ce22d0..9e487ab4e 100644
--- a/jerry-core/parser/js/js-parser-internal.h
+++ b/jerry-core/parser/js/js-parser-internal.h
@@ -823,7 +823,6 @@ void scanner_cleanup (parser_context_t *context_p);
bool scanner_is_context_needed (parser_context_t *context_p, parser_check_context_type_t check_type);
#if ENABLED (JERRY_ESNEXT)
-bool scanner_scope_find_let_declaration (parser_context_t *context_p, lexer_lit_location_t *literal_p);
bool scanner_try_scan_new_target (parser_context_t *context_p);
void scanner_check_variables (parser_context_t *context_p);
#endif /* ENABLED (JERRY_ESNEXT) */
diff --git a/jerry-core/parser/js/js-parser.c b/jerry-core/parser/js/js-parser.c
index f8aed0980..ac5aa559e 100644
--- a/jerry-core/parser/js/js-parser.c
+++ b/jerry-core/parser/js/js-parser.c
@@ -1992,7 +1992,7 @@ parser_parse_source (const uint8_t *arg_list_p, /**< function argument list */
context.status_flags |= PARSER_LEXICAL_BLOCK_NEEDED;
}
- if ((parse_opts & ECMA_PARSE_EVAL) == 0)
+ if (!(parse_opts & ECMA_PARSE_EVAL))
{
scanner_check_variables (&context);
}
diff --git a/jerry-core/parser/js/js-scanner-util.c b/jerry-core/parser/js/js-scanner-util.c
index 2f8840e4d..cb7d9a954 100644
--- a/jerry-core/parser/js/js-scanner-util.c
+++ b/jerry-core/parser/js/js-scanner-util.c
@@ -14,6 +14,7 @@
*/
#include "ecma-helpers.h"
+#include "ecma-lex-env.h"
#include "jcontext.h"
#include "js-parser-internal.h"
#include "js-scanner-internal.h"
@@ -385,6 +386,85 @@ scanner_seek (parser_context_t *context_p) /**< context */
context_p->next_scanner_info_p = prev_p->next_p;
} /* scanner_seek */
+#if ENABLED (JERRY_ESNEXT)
+
+/**
+ * Find any let/const declaration of a given literal.
+ *
+ * @return true - if the literal is found, false - otherwise
+ */
+static bool
+scanner_scope_find_lexical_declaration (parser_context_t *context_p, /**< context */
+ lexer_lit_location_t *literal_p) /**< literal */
+{
+ ecma_string_t *name_p;
+ uint32_t flags = context_p->global_status_flags;
+
+ if (!(flags & ECMA_PARSE_EVAL)
+ || (!(flags & ECMA_PARSE_DIRECT_EVAL) && (context_p->status_flags & PARSER_IS_STRICT)))
+ {
+ return false;
+ }
+
+ if (JERRY_LIKELY (!literal_p->has_escape))
+ {
+ name_p = ecma_new_ecma_string_from_utf8 (literal_p->char_p, literal_p->length);
+ }
+ else
+ {
+ uint8_t *destination_p = (uint8_t *) scanner_malloc (context_p, literal_p->length);
+
+ lexer_convert_ident_to_cesu8 (destination_p, literal_p->char_p, literal_p->length);
+
+ name_p = ecma_new_ecma_string_from_utf8 (destination_p, literal_p->length);
+ scanner_free (destination_p, literal_p->length);
+ }
+
+ ecma_object_t *lex_env_p;
+
+ if (flags & ECMA_PARSE_DIRECT_EVAL)
+ {
+ lex_env_p = JERRY_CONTEXT (vm_top_context_p)->lex_env_p;
+
+ while (lex_env_p->type_flags_refs & ECMA_OBJECT_FLAG_BLOCK)
+ {
+ if (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE)
+ {
+ ecma_property_t *property_p = ecma_find_named_property (lex_env_p, name_p);
+
+ if (property_p != NULL && ecma_is_property_enumerable (*property_p))
+ {
+ ecma_deref_ecma_string (name_p);
+ return true;
+ }
+ }
+
+ JERRY_ASSERT (lex_env_p->u2.outer_reference_cp != JMEM_CP_NULL);
+ lex_env_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, lex_env_p->u2.outer_reference_cp);
+ }
+ }
+ else
+ {
+ lex_env_p = ecma_get_global_scope (ecma_builtin_get_global ());
+ }
+
+ if (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE)
+ {
+ ecma_property_t *property_p = ecma_find_named_property (lex_env_p, name_p);
+
+ if (property_p != NULL && ecma_is_property_enumerable (*property_p))
+ {
+ ecma_deref_ecma_string (name_p);
+ return true;
+ }
+ }
+
+ ecma_deref_ecma_string (name_p);
+ return false;
+} /* scanner_scope_find_lexical_declaration */
+
+#endif /* ENABLED (JERRY_ESNEXT) */
+
/**
* Push a new literal pool.
*
@@ -694,9 +774,7 @@ scanner_pop_literal_pool (parser_context_t *context_p, /**< context */
if ((status_flags & SCANNER_LITERAL_POOL_FUNCTION)
&& (type & SCANNER_LITERAL_IS_LOCAL_FUNC) == SCANNER_LITERAL_IS_FUNC)
{
- if (prev_literal_pool_p == NULL
- && (context_p->global_status_flags & ECMA_PARSE_DIRECT_EVAL)
- && scanner_scope_find_let_declaration (context_p, literal_p))
+ if (prev_literal_pool_p == NULL && scanner_scope_find_lexical_declaration (context_p, literal_p))
{
literal_p->type = 0;
continue;
@@ -1504,65 +1582,6 @@ scanner_detect_eval_call (parser_context_t *context_p, /**< context */
#if ENABLED (JERRY_ESNEXT)
-/**
- * Find a let/const declaration of a given literal.
- *
- * @return true - if the literal is found, false - otherwise
- */
-bool
-scanner_scope_find_let_declaration (parser_context_t *context_p, /**< context */
- lexer_lit_location_t *literal_p) /**< literal */
-{
- ecma_string_t *name_p;
-
- if (JERRY_LIKELY (!literal_p->has_escape))
- {
- name_p = ecma_new_ecma_string_from_utf8 (literal_p->char_p, literal_p->length);
- }
- else
- {
- uint8_t *destination_p = (uint8_t *) scanner_malloc (context_p, literal_p->length);
-
- lexer_convert_ident_to_cesu8 (destination_p, literal_p->char_p, literal_p->length);
-
- name_p = ecma_new_ecma_string_from_utf8 (destination_p, literal_p->length);
- scanner_free (destination_p, literal_p->length);
- }
-
- ecma_object_t *lex_env_p = JERRY_CONTEXT (vm_top_context_p)->lex_env_p;
-
- while (lex_env_p->type_flags_refs & ECMA_OBJECT_FLAG_BLOCK)
- {
- if (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE)
- {
- ecma_property_t *property_p = ecma_find_named_property (lex_env_p, name_p);
-
- if (property_p != NULL && ecma_is_property_enumerable (*property_p))
- {
- ecma_deref_ecma_string (name_p);
- return true;
- }
- }
-
- JERRY_ASSERT (lex_env_p->u2.outer_reference_cp != JMEM_CP_NULL);
- lex_env_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, lex_env_p->u2.outer_reference_cp);
- }
-
- if (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE)
- {
- ecma_property_t *property_p = ecma_find_named_property (lex_env_p, name_p);
-
- if (property_p != NULL && ecma_is_property_enumerable (*property_p))
- {
- ecma_deref_ecma_string (name_p);
- return true;
- }
- }
-
- ecma_deref_ecma_string (name_p);
- return false;
-} /* scanner_scope_find_let_declaration */
-
/**
* Throws an error for invalid var statements.
*/
@@ -1642,8 +1661,7 @@ scanner_detect_invalid_var (parser_context_t *context_p, /**< context */
}
}
- if ((context_p->global_status_flags & ECMA_PARSE_DIRECT_EVAL)
- && scanner_scope_find_let_declaration (context_p, var_literal_p))
+ if (scanner_scope_find_lexical_declaration (context_p, var_literal_p))
{
scanner_raise_redeclaration_error (context_p);
}
@@ -2072,7 +2090,7 @@ scanner_is_context_needed (parser_context_t *context_p, /**< context */
if (JERRY_UNLIKELY (check_type == PARSER_CHECK_GLOBAL_CONTEXT)
&& (type == SCANNER_STREAM_TYPE_VAR
- || (type == SCANNER_STREAM_TYPE_FUNC && !(context_p->global_status_flags & ECMA_PARSE_DIRECT_EVAL))
+ || (type == SCANNER_STREAM_TYPE_FUNC && !(context_p->global_status_flags & ECMA_PARSE_EVAL))
|| is_import))
{
continue;
@@ -2768,8 +2786,8 @@ scanner_create_variables (parser_context_t *context_p, /**< context */
{
#if ENABLED (JERRY_ESNEXT)
literal.char_p -= data_p[1];
- if (!(context_p->global_status_flags & ECMA_PARSE_DIRECT_EVAL)
- || !scanner_scope_find_let_declaration (context_p, &literal))
+
+ if (!scanner_scope_find_lexical_declaration (context_p, &literal))
{
func_init_opcode = CBC_CREATE_VAR_FUNC_EVAL;
diff --git a/tests/jerry/es.next/regression-test-issue-4149.js b/tests/jerry/es.next/regression-test-issue-4149.js
new file mode 100644
index 000000000..602964ad8
--- /dev/null
+++ b/tests/jerry/es.next/regression-test-issue-4149.js
@@ -0,0 +1,24 @@
+// 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.
+
+let x;
+try {
+ (0,eval)('var x');
+ assert(false)
+} catch (e) {
+ assert(e instanceof SyntaxError)
+}
+
+(0,eval)('function x() {};');
+
diff --git a/tests/test262-esnext-excludelist.xml b/tests/test262-esnext-excludelist.xml
index e6bcc830b..298306eb1 100644
--- a/tests/test262-esnext-excludelist.xml
+++ b/tests/test262-esnext-excludelist.xml
@@ -390,7 +390,6 @@
-