Fix inner classes in class heritage environment (#2686)

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
This commit is contained in:
Robert Fancsik 2019-01-16 14:36:54 +01:00 committed by Akos Kiss
parent cfdb5eddb3
commit 86e60ddf8d
7 changed files with 129 additions and 25 deletions

View File

@ -99,11 +99,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;
/**

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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);
}

View File

@ -1336,7 +1336,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
{
@ -1541,7 +1543,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;

View File

@ -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 ();