From 51622f752b707b337516c4611ef3c72d86d63372 Mon Sep 17 00:00:00 2001 From: Robert Fancsik Date: Wed, 9 Jan 2019 10:55:32 +0100 Subject: [PATCH] Fix inner classes in class heritage environment This patch is the proper fix for #2667, since #2269 did not fix the problem entirely. JerryScript-DCO-1.0-Signed-off-by: Robert Fancsik frobert@inf.u-szeged.hu --- jerry-core/ecma/base/ecma-globals.h | 5 +- .../ecma/operations/ecma-function-object.c | 2 +- jerry-core/parser/js/js-parser-expr.c | 33 ++++++++--- jerry-core/parser/js/js-parser-internal.h | 7 ++- jerry-core/parser/js/js-parser-statm.c | 41 ++++++++++--- jerry-core/vm/vm.c | 7 ++- .../es2015/class-inheritance-inner-class.js | 59 +++++++++++++++++++ 7 files changed, 129 insertions(+), 25 deletions(-) create mode 100644 tests/jerry/es2015/class-inheritance-inner-class.js diff --git a/jerry-core/ecma/base/ecma-globals.h b/jerry-core/ecma/base/ecma-globals.h index 375e7d17d7..04ddfbbd68 100644 --- a/jerry-core/ecma/base/ecma-globals.h +++ b/jerry-core/ecma/base/ecma-globals.h @@ -98,11 +98,12 @@ typedef enum ECMA_PARSE_NO_OPTS = 0, /**< no options passed */ ECMA_PARSE_STRICT_MODE = (1u << 0), /**< enable strict mode */ ECMA_PARSE_DIRECT_EVAL = (1u << 1), /**< eval is called directly (ECMA-262 v5, 15.1.2.1.1) */ - /* These three status flags must be in this order. See PARSER_CLASS_PARSE_OPTS_OFFSET. */ + /* These four status flags must be in this order. See PARSER_CLASS_PARSE_OPTS_OFFSET. */ ECMA_PARSE_CLASS_CONSTRUCTOR = (1u << 2), /**< a class constructor is being parsed (this value must be kept in * in sync with PARSER_CLASS_CONSTRUCTOR) */ ECMA_PARSE_HAS_SUPER = (1u << 3), /**< the current context has super reference */ - ECMA_PARSE_HAS_STATIC_SUPER = (1u << 4), /**< the current context is a static class method */ + ECMA_PARSE_HAS_IMPL_SUPER = (1u << 4), /**< the current context has implicit parent class */ + ECMA_PARSE_HAS_STATIC_SUPER = (1u << 5), /**< the current context is a static class method */ } ecma_parse_opts_t; /** diff --git a/jerry-core/ecma/operations/ecma-function-object.c b/jerry-core/ecma/operations/ecma-function-object.c index aab21153aa..71119971d8 100644 --- a/jerry-core/ecma/operations/ecma-function-object.c +++ b/jerry-core/ecma/operations/ecma-function-object.c @@ -1053,7 +1053,7 @@ ecma_op_function_construct (ecma_object_t *func_obj_p, /**< Function object */ /* Catch the special case when a the class extends value in null and the class has no explicit constructor to raise TypeError.*/ JERRY_ASSERT (!ecma_op_function_has_construct_flag (arguments_list_p)); - JERRY_ASSERT (ecma_get_object_prototype (func_obj_p) == NULL); + JERRY_ASSERT (ecma_get_object_prototype (func_obj_p) == ecma_builtin_get (ECMA_BUILTIN_ID_OBJECT_PROTOTYPE)); ret_value = ecma_raise_type_error (ECMA_ERR_MSG ("Super constructor null is not a constructor.")); break; diff --git a/jerry-core/parser/js/js-parser-expr.c b/jerry-core/parser/js/js-parser-expr.c index 9dd7902dec..3d4f9dbc79 100644 --- a/jerry-core/parser/js/js-parser-expr.c +++ b/jerry-core/parser/js/js-parser-expr.c @@ -364,7 +364,8 @@ parser_parse_class_literal (parser_context_t *context_p) /**< context */ parser_emit_cbc (context_p, CBC_CREATE_OBJECT); bool super_called = false; - uint32_t status_flags = PARSER_IS_FUNCTION | PARSER_IS_CLOSURE | (context_p->status_flags & PARSER_CLASS_HAS_SUPER); + uint32_t status_flags = PARSER_IS_FUNCTION | PARSER_IS_CLOSURE; + status_flags |= context_p->status_flags & (PARSER_CLASS_HAS_SUPER | PARSER_CLASS_IMPLICIT_SUPER); while (true) { @@ -472,12 +473,13 @@ parser_parse_class_literal (parser_context_t *context_p) /**< context */ parser_raise_error (context_p, PARSER_ERR_LITERAL_LIMIT_REACHED); } + uint16_t result_index = context_p->literal_count; lexer_literal_t *literal_p = (lexer_literal_t *) parser_list_append (context_p, &context_p->literal_pool); literal_p->type = LEXER_UNUSED_LITERAL; literal_p->status_flags = 0; literal_p->u.bytecode_p = parser_parse_function (context_p, constructor_status_flags); literal_p->type = LEXER_FUNCTION_LITERAL; - parser_emit_cbc_literal (context_p, PARSER_TO_EXT_OPCODE (CBC_EXT_SET_CLASS_LITERAL), context_p->literal_count); + parser_emit_cbc_literal (context_p, PARSER_TO_EXT_OPCODE (CBC_EXT_SET_CLASS_LITERAL), result_index); context_p->literal_count++; continue; } @@ -555,8 +557,6 @@ parser_parse_class (parser_context_t *context_p, /**< context */ { JERRY_ASSERT (context_p->token.type == LEXER_KEYW_CLASS); - context_p->status_flags &= (uint32_t) ~PARSER_CLASS_HAS_SUPER; - uint16_t class_ident_index = PARSER_MAXIMUM_NUMBER_OF_LITERALS; if (is_statement) @@ -582,7 +582,10 @@ parser_parse_class (parser_context_t *context_p, /**< context */ } } - if (context_p->token.type == LEXER_KEYW_EXTENDS) + bool create_class_env = (context_p->token.type == LEXER_KEYW_EXTENDS + || (context_p->status_flags & PARSER_CLASS_HAS_SUPER)); + + if (create_class_env) { parser_parse_super_class_context_start (context_p); } @@ -615,10 +618,10 @@ parser_parse_class (parser_context_t *context_p, /**< context */ parser_emit_cbc_literal (context_p, CBC_ASSIGN_SET_IDENT, class_ident_index); } - if (context_p->status_flags & PARSER_CLASS_HAS_SUPER) + if (create_class_env) { parser_parse_super_class_context_end (context_p, is_statement); - context_p->status_flags &= (uint32_t) ~PARSER_CLASS_HAS_SUPER; + context_p->status_flags &= (uint32_t) ~(PARSER_CLASS_HAS_SUPER | PARSER_CLASS_IMPLICIT_SUPER); } parser_flush_cbc (context_p); @@ -1366,7 +1369,14 @@ parser_parse_unary_expression (parser_context_t *context_p, /**< context */ #ifndef CONFIG_DISABLE_ES2015_CLASS if (PARSER_IS_CLASS_CONSTRUCTOR_SUPER (context_p->status_flags)) { - parser_emit_cbc_ext (context_p, CBC_EXT_PUSH_CONSTRUCTOR_THIS); + if (context_p->status_flags & PARSER_CLASS_IMPLICIT_SUPER) + { + parser_emit_cbc (context_p, CBC_PUSH_THIS); + } + else + { + parser_emit_cbc_ext (context_p, CBC_EXT_PUSH_CONSTRUCTOR_THIS); + } } else { @@ -1415,6 +1425,12 @@ parser_parse_unary_expression (parser_context_t *context_p, /**< context */ break; } + if (context_p->status_flags & PARSER_CLASS_IMPLICIT_SUPER) + { + parser_emit_cbc_ext (context_p, CBC_EXT_PUSH_STATIC_SUPER); + break; + } + bool is_static = (context_p->status_flags & PARSER_CLASS_STATIC_FUNCTION) != 0; parser_emit_cbc_ext (context_p, is_static ? CBC_EXT_PUSH_STATIC_SUPER : CBC_EXT_PUSH_SUPER); break; @@ -1422,6 +1438,7 @@ parser_parse_unary_expression (parser_context_t *context_p, /**< context */ if (lexer_check_next_character (context_p, LIT_CHAR_LEFT_PAREN) && (context_p->status_flags & PARSER_CLASS_HAS_SUPER) + && !(context_p->status_flags & PARSER_CLASS_IMPLICIT_SUPER) && (context_p->status_flags & (PARSER_IS_ARROW_FUNCTION | PARSER_CLASS_CONSTRUCTOR))) { parser_emit_cbc_ext (context_p, CBC_EXT_PUSH_CONSTRUCTOR_SUPER); diff --git a/jerry-core/parser/js/js-parser-internal.h b/jerry-core/parser/js/js-parser-internal.h index 19ed767488..9896b9a0c8 100644 --- a/jerry-core/parser/js/js-parser-internal.h +++ b/jerry-core/parser/js/js-parser-internal.h @@ -67,12 +67,13 @@ typedef enum PARSER_ARROW_PARSE_ARGS = (1u << 19), /**< parse the argument list of an arrow function */ #endif /* !CONFIG_DISABLE_ES2015_ARROW_FUNCTION */ #ifndef CONFIG_DISABLE_ES2015_CLASS - /* These three status flags must be in this order. See PARSER_CLASS_PARSE_OPTS_OFFSET. */ + /* These four status flags must be in this order. See PARSER_CLASS_PARSE_OPTS_OFFSET. */ PARSER_CLASS_CONSTRUCTOR = (1u << 20), /**< a class constructor is parsed (this value must be kept in * in sync with ECMA_PARSE_CLASS_CONSTRUCTOR) */ PARSER_CLASS_HAS_SUPER = (1u << 21), /**< class has super reference */ - PARSER_CLASS_STATIC_FUNCTION = (1u << 22), /**< this function is a static class method */ - PARSER_CLASS_SUPER_PROP_REFERENCE = (1u << 23), /**< super property call or assignment */ + PARSER_CLASS_IMPLICIT_SUPER = (1u << 22), /**< class has implicit parent class */ + PARSER_CLASS_STATIC_FUNCTION = (1u << 23), /**< this function is a static class method */ + PARSER_CLASS_SUPER_PROP_REFERENCE = (1u << 24), /**< super property call or assignment */ #endif /* !CONFIG_DISABLE_ES2015_CLASS */ } parser_general_flags_t; diff --git a/jerry-core/parser/js/js-parser-statm.c b/jerry-core/parser/js/js-parser-statm.c index d17061c337..df5a078ad0 100644 --- a/jerry-core/parser/js/js-parser-statm.c +++ b/jerry-core/parser/js/js-parser-statm.c @@ -606,16 +606,25 @@ parser_parse_with_statement_end (parser_context_t *context_p) /**< context */ void parser_parse_super_class_context_start (parser_context_t *context_p) /**< context */ { - JERRY_ASSERT (context_p->token.type == LEXER_KEYW_EXTENDS); - + JERRY_ASSERT (context_p->token.type == LEXER_KEYW_EXTENDS + || (context_p->status_flags & PARSER_CLASS_HAS_SUPER)); parser_with_statement_t with_statement; - lexer_next_token (context_p); + if (context_p->token.type == LEXER_KEYW_EXTENDS) + { + lexer_next_token (context_p); - /* NOTE: Currently there is no proper way to check whether the currently parsed expression - is a valid lefthand-side expression or not, so we do not throw syntax error and parse - the class extending value as an expression. */ - parser_parse_expression (context_p, PARSE_EXPR | PARSE_EXPR_NO_COMMA); + /* NOTE: Currently there is no proper way to check whether the currently parsed expression + is a valid lefthand-side expression or not, so we do not throw syntax error and parse + the class extending value as an expression. */ + parser_parse_expression (context_p, PARSE_EXPR | PARSE_EXPR_NO_COMMA); + } + else + { + JERRY_ASSERT (context_p->status_flags & PARSER_CLASS_HAS_SUPER); + parser_emit_cbc (context_p, CBC_PUSH_NULL); + context_p->status_flags |= PARSER_CLASS_IMPLICIT_SUPER; + } #ifndef JERRY_NDEBUG PARSER_PLUS_EQUAL_U16 (context_p->context_stack_depth, PARSER_SUPER_CLASS_CONTEXT_STACK_ALLOCATION); @@ -2022,7 +2031,14 @@ parser_parse_statements (parser_context_t *context_p) /**< context */ #ifndef CONFIG_DISABLE_ES2015_CLASS if (JERRY_UNLIKELY (PARSER_IS_CLASS_CONSTRUCTOR_SUPER (context_p->status_flags))) { - parser_emit_cbc_ext (context_p, CBC_EXT_PUSH_CONSTRUCTOR_THIS); + if (context_p->status_flags & PARSER_CLASS_IMPLICIT_SUPER) + { + parser_emit_cbc (context_p, CBC_PUSH_THIS); + } + else + { + parser_emit_cbc_ext (context_p, CBC_EXT_PUSH_CONSTRUCTOR_THIS); + } parser_emit_cbc (context_p, CBC_RETURN); } else @@ -2164,7 +2180,14 @@ parser_parse_statements (parser_context_t *context_p) /**< context */ #ifndef CONFIG_DISABLE_ES2015_CLASS if (JERRY_UNLIKELY (PARSER_IS_CLASS_CONSTRUCTOR_SUPER (context_p->status_flags))) { - parser_emit_cbc_ext (context_p, CBC_EXT_PUSH_CONSTRUCTOR_THIS); + if (context_p->status_flags & PARSER_CLASS_IMPLICIT_SUPER) + { + parser_emit_cbc (context_p, CBC_PUSH_THIS); + } + else + { + parser_emit_cbc_ext (context_p, CBC_EXT_PUSH_CONSTRUCTOR_THIS); + } parser_emit_cbc (context_p, CBC_RETURN); parser_flush_cbc (context_p); } diff --git a/jerry-core/vm/vm.c b/jerry-core/vm/vm.c index 42e3aa2c2b..b8b594241c 100644 --- a/jerry-core/vm/vm.c +++ b/jerry-core/vm/vm.c @@ -1328,7 +1328,9 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ if (ecma_is_value_null (super_value)) { - super_class_p = ecma_create_object (NULL, 0, ECMA_OBJECT_TYPE_GENERAL); + super_class_p = ecma_create_object (ecma_builtin_get (ECMA_BUILTIN_ID_OBJECT_PROTOTYPE), + 0, + ECMA_OBJECT_TYPE_GENERAL); } else { @@ -1533,7 +1535,8 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ else { ecma_object_t *super_class_p = ecma_op_resolve_super_reference_value (frame_ctx_p->lex_env_p); - *stack_top_p++ = ecma_fast_copy_value (ecma_make_object_value (super_class_p)); + ecma_ref_object (super_class_p); + *stack_top_p++ = ecma_make_object_value (super_class_p); } continue; diff --git a/tests/jerry/es2015/class-inheritance-inner-class.js b/tests/jerry/es2015/class-inheritance-inner-class.js new file mode 100644 index 0000000000..f92b047faf --- /dev/null +++ b/tests/jerry/es2015/class-inheritance-inner-class.js @@ -0,0 +1,59 @@ +/* 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. + */ + +var console = { assert : assert }; + +class C1 { + f () { + return 5; + } +} + +class C2 extends C1 { + f () { + assert (super.f () === 5); + + class G { + g () { + assert (super.f === undefined); + assert (super.toString () === "[object Object]"); + var a = super.valueOf (); + try { + a (); + assert (false); + } catch (e) { + assert (e instanceof TypeError); + } + } + + constructor () { + // Test to overwrite the current lit-object + console.assert (Object.getPrototypeOf (this) === G.prototype); + + try { + eval ("super ()"); + assert (false); + } catch (e) { + assert (e instanceof SyntaxError); + } + } + } + + var g = new G (); + g.g (); + } +} + +(new C2).f ();