Skip to content

Implement ES2015 class feature (part I.) #2404

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 12, 2018
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
25 changes: 21 additions & 4 deletions jerry-core/api/jerry-snapshot.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,11 @@
* @return configuration flags
*/
static inline uint32_t JERRY_ATTR_ALWAYS_INLINE
snapshot_get_global_flags (bool has_regex) /**< regex literal is present */
snapshot_get_global_flags (bool has_regex, /**< regex literal is present */
bool has_class) /**< class literal is present */
{
JERRY_UNUSED (has_regex);
JERRY_UNUSED (has_class);

uint32_t flags = 0;

Expand All @@ -45,7 +47,10 @@ snapshot_get_global_flags (bool has_regex) /**< regex literal is present */
#endif /* JERRY_CPOINTER_32_BIT */
#ifndef CONFIG_DISABLE_REGEXP_BUILTIN
flags |= (has_regex ? JERRY_SNAPSHOT_HAS_REGEX_LITERAL : 0);
#endif /* CONFIG_DISABLE_REGEXP_BUILTIN */
#endif /* !CONFIG_DISABLE_REGEXP_BUILTIN */
#ifndef CONFIG_DISABLE_ES2015_CLASS
flags |= (has_class ? JERRY_SNAPSHOT_HAS_CLASS_LITERAL : 0);
#endif /* !CONFIG_DISABLE_ES2015_CLASS */

return flags;
} /* snapshot_get_global_flags */
Expand All @@ -61,8 +66,11 @@ snapshot_check_global_flags (uint32_t global_flags) /**< global flags */
#ifndef CONFIG_DISABLE_REGEXP_BUILTIN
global_flags &= (uint32_t) ~JERRY_SNAPSHOT_HAS_REGEX_LITERAL;
#endif /* !CONFIG_DISABLE_REGEXP_BUILTIN */
#ifndef CONFIG_DISABLE_ES2015_CLASS
global_flags &= (uint32_t) ~JERRY_SNAPSHOT_HAS_CLASS_LITERAL;
#endif /* !CONFIG_DISABLE_ES2015_CLASS */

return global_flags == snapshot_get_global_flags (false);
return global_flags == snapshot_get_global_flags (false, false);
} /* snapshot_check_global_flags */

#endif /* JERRY_ENABLE_SNAPSHOT_SAVE || JERRY_ENABLE_SNAPSHOT_EXEC */
Expand All @@ -77,6 +85,7 @@ typedef struct
size_t snapshot_buffer_write_offset;
ecma_value_t snapshot_error;
bool regex_found;
bool class_found;
} snapshot_globals_t;

/** \addtogroup jerrysnapshot Jerry snapshot operations
Expand Down Expand Up @@ -154,6 +163,13 @@ snapshot_add_compiled_code (ecma_compiled_code_t *compiled_code_p, /**< compiled
uint8_t *copied_code_start_p = snapshot_buffer_p + globals_p->snapshot_buffer_write_offset;
ecma_compiled_code_t *copied_code_p = (ecma_compiled_code_t *) copied_code_start_p;

#ifndef CONFIG_DISABLE_ES2015_CLASS
if (compiled_code_p->status_flags & CBC_CODE_FLAGS_CONSTRUCTOR)
{
globals_p->class_found = true;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not necessary true. However I would not add another flag for classes at the moment, so keep this code.

}
#endif /* !CONFIG_DISABLE_ES2015_CLASS */

#ifndef CONFIG_DISABLE_REGEXP_BUILTIN
if (!(compiled_code_p->status_flags & CBC_CODE_FLAGS_FUNCTION))
{
Expand Down Expand Up @@ -739,6 +755,7 @@ jerry_generate_snapshot_with_args (const jerry_char_t *resource_name_p, /**< scr
globals.snapshot_buffer_write_offset = aligned_header_size;
globals.snapshot_error = ECMA_VALUE_EMPTY;
globals.regex_found = false;
globals.class_found = false;

parse_status = parser_parse_script (args_p,
args_size,
Expand Down Expand Up @@ -769,7 +786,7 @@ jerry_generate_snapshot_with_args (const jerry_char_t *resource_name_p, /**< scr
jerry_snapshot_header_t header;
header.magic = JERRY_SNAPSHOT_MAGIC;
header.version = JERRY_SNAPSHOT_VERSION;
header.global_flags = snapshot_get_global_flags (globals.regex_found);
header.global_flags = snapshot_get_global_flags (globals.regex_found, globals.class_found);
header.lit_table_offset = (uint32_t) globals.snapshot_buffer_write_offset;
header.number_of_funcs = 1;
header.func_offsets[0] = aligned_header_size;
Expand Down
3 changes: 2 additions & 1 deletion jerry-core/api/jerry-snapshot.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ typedef struct
/**
* Jerry snapshot format version.
*/
#define JERRY_SNAPSHOT_VERSION (14u)
#define JERRY_SNAPSHOT_VERSION (15u)

/**
* Snapshot configuration flags.
Expand All @@ -50,6 +50,7 @@ typedef enum
{
/* 8 bits are reserved for dynamic features */
JERRY_SNAPSHOT_HAS_REGEX_LITERAL = (1u << 0), /**< byte code has regex literal */
JERRY_SNAPSHOT_HAS_CLASS_LITERAL = (1u << 1), /**< byte code has class literal */
/* 24 bits are reserved for compile time features */
JERRY_SNAPSHOT_FOUR_BYTE_CPOINTER = (1u << 8) /**< compressed pointers are four byte long */
} jerry_snapshot_global_flags_t;
Expand Down
1 change: 1 addition & 0 deletions jerry-core/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
*/
#ifdef CONFIG_DISABLE_ES2015
# define CONFIG_DISABLE_ES2015_ARROW_FUNCTION
# define CONFIG_DISABLE_ES2015_CLASS
# define CONFIG_DISABLE_ES2015_BUILTIN
# define CONFIG_DISABLE_ES2015_PROMISE_BUILTIN
# define CONFIG_DISABLE_ES2015_TEMPLATE_STRINGS
Expand Down
32 changes: 28 additions & 4 deletions jerry-core/ecma/operations/ecma-function-object.c
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,14 @@ ecma_op_function_has_instance (ecma_object_t *func_obj_p, /**< Function object *
return ecma_make_boolean_value (result);
} /* ecma_op_function_has_instance */


#ifndef CONFIG_DISABLE_ES2015_CLASS
/**
* Indicates whether the class has been invoked with 'new'.
*/
#define ECMA_CLASS_CONSTRUCT_FLAG ((uintptr_t) 0x01u)
#endif /* !CONFIG_DISABLE_ES2015_CLASS */

/**
* Sets the construct flag in the arguments list pointer.
*
Expand All @@ -413,8 +421,10 @@ ecma_op_function_set_construct_flag (const ecma_value_t *arguments_list_p) /**<
/* Any ecma value list must be aligned to 4 byte. */
JERRY_ASSERT ((((uintptr_t) arguments_list_p) & 0x3) == 0);

/* Currently it returns with the same pointer. When classes
* will be enabled, it will set the lowest bit. */
#ifndef CONFIG_DISABLE_ES2015_CLASS
arguments_list_p = (const ecma_value_t *)(((uintptr_t) arguments_list_p) | ECMA_CLASS_CONSTRUCT_FLAG);
#endif /* !CONFIG_DISABLE_ES2015_CLASS */

return arguments_list_p;
} /* ecma_op_function_set_construct_flag */

Expand All @@ -426,8 +436,10 @@ ecma_op_function_set_construct_flag (const ecma_value_t *arguments_list_p) /**<
static inline const ecma_value_t * JERRY_ATTR_ALWAYS_INLINE
ecma_op_function_clear_construct_flag (const ecma_value_t *arguments_list_p) /**< modified arguments list pointer */
{
/* Currently it returns with the same pointer. When classes
* will be enabled, the lowest bit will be cleared. */
#ifndef CONFIG_DISABLE_ES2015_CLASS
arguments_list_p = (const ecma_value_t *)(((uintptr_t) arguments_list_p) & ~ECMA_CLASS_CONSTRUCT_FLAG);
#endif /* !CONFIG_DISABLE_ES2015_CLASS */

return arguments_list_p;
} /* ecma_op_function_clear_construct_flag */

Expand All @@ -439,8 +451,12 @@ ecma_op_function_clear_construct_flag (const ecma_value_t *arguments_list_p) /**
static inline bool JERRY_ATTR_ALWAYS_INLINE
ecma_op_function_has_construct_flag (const ecma_value_t *arguments_list_p) /**< modified arguments list pointer */
{
#ifndef CONFIG_DISABLE_ES2015_CLASS
return (((uintptr_t) arguments_list_p) & ECMA_CLASS_CONSTRUCT_FLAG);
#else
JERRY_UNUSED (arguments_list_p);
return false;
#endif /* !CONFIG_DISABLE_ES2015_CLASS */
} /* ecma_op_function_has_construct_flag */

/**
Expand Down Expand Up @@ -496,6 +512,14 @@ ecma_op_function_call (ecma_object_t *func_obj_p, /**< Function object */

const ecma_compiled_code_t *bytecode_data_p = ecma_op_function_get_compiled_code (ext_func_p);

#ifndef CONFIG_DISABLE_ES2015_CLASS
if (bytecode_data_p->status_flags & CBC_CODE_FLAGS_CONSTRUCTOR &&
!ecma_op_function_has_construct_flag (arguments_list_p))
{
return ecma_raise_type_error (ECMA_ERR_MSG ("Class constructor cannot be invoked without 'new'."));
}
#endif /* !CONFIG_DISABLE_ES2015_CLASS */

is_strict = (bytecode_data_p->status_flags & CBC_CODE_FLAGS_STRICT_MODE) ? true : false;
is_no_lex_env = (bytecode_data_p->status_flags & CBC_CODE_FLAGS_LEXICAL_ENV_NOT_NEEDED) ? true : false;

Expand Down
7 changes: 7 additions & 0 deletions jerry-core/parser/js/byte-code.h
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,12 @@
VM_OC_RESOURCE_NAME) \
CBC_OPCODE (CBC_EXT_LINE, CBC_NO_FLAG, 0, \
VM_OC_LINE) \
CBC_OPCODE (CBC_EXT_SET_STATIC_PROPERTY, CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, 0, \
VM_OC_SET_PROPERTY | VM_OC_GET_LITERAL_LITERAL) \
CBC_OPCODE (CBC_EXT_SET_STATIC_SETTER, CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, 0, \
VM_OC_SET_SETTER | VM_OC_GET_LITERAL_LITERAL) \
CBC_OPCODE (CBC_EXT_SET_STATIC_GETTER, CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, 0, \
VM_OC_SET_GETTER | VM_OC_GET_LITERAL_LITERAL) \
\
/* Binary compound assignment opcodes with pushing the result. */ \
CBC_EXT_BINARY_LVALUE_OPERATION (CBC_EXT_ASSIGN_ADD, \
Expand Down Expand Up @@ -667,6 +673,7 @@ typedef enum
CBC_CODE_FLAGS_ARROW_FUNCTION = (1u << 7), /**< this function is an arrow function */
CBC_CODE_FLAGS_STATIC_FUNCTION = (1u << 8), /**< this function is a static snapshot function */
CBC_CODE_FLAGS_DEBUGGER_IGNORE = (1u << 9), /**< this function should be ignored by debugger */
CBC_CODE_FLAGS_CONSTRUCTOR = (1u << 10), /**< this function is a constructor */
} cbc_code_flags;

#define CBC_OPCODE(arg1, arg2, arg3, arg4) arg1,
Expand Down
62 changes: 62 additions & 0 deletions jerry-core/parser/js/js-lexer.c
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,23 @@ lexer_skip_spaces (parser_context_t *context_p) /**< context */
}
} /* lexer_skip_spaces */

#ifndef CONFIG_DISABLE_ES2015_CLASS
/**
* Skip all the continuous empty statements.
*/
void
lexer_skip_empty_statements (parser_context_t *context_p) /**< context */
{
lexer_skip_spaces (context_p);

while (*context_p->source_p == LIT_CHAR_SEMICOLON)
{
lexer_next_token (context_p);
lexer_skip_spaces (context_p);
}
} /* lexer_skip_empty_statements */
#endif /* !CONFIG_DISABLE_ES2015_CLASS */

/**
* Keyword data.
*/
Expand Down Expand Up @@ -1308,6 +1325,25 @@ lexer_check_colon (parser_context_t *context_p) /**< context */
&& context_p->source_p[0] == (uint8_t) LIT_CHAR_COLON);
} /* lexer_check_colon */

#ifndef CONFIG_DISABLE_ES2015_CLASS
/**
* Checks whether the next token is a left parenthesis.
*
* @return true - if the next token is a left parenthesis
* false - otherwise
*/
bool
lexer_check_left_paren (parser_context_t *context_p) /**< context */
{
lexer_skip_spaces (context_p);

context_p->token.flags = (uint8_t) (context_p->token.flags | LEXER_NO_SKIP_SPACES);

return (context_p->source_p < context_p->source_end_p
&& context_p->source_p[0] == (uint8_t) LIT_CHAR_LEFT_PAREN);
} /* lexer_check_left_paren */
#endif /* !CONFIG_DISABLE_ES2015_CLASS */

#ifndef CONFIG_DISABLE_ES2015_ARROW_FUNCTION

/**
Expand Down Expand Up @@ -2231,6 +2267,10 @@ lexer_expect_object_literal_id (parser_context_t *context_p, /**< context */
{
lexer_skip_spaces (context_p);

#ifndef CONFIG_DISABLE_ES2015_CLASS
bool is_static_method = (context_p->token.type == LEXER_KEYW_STATIC);
#endif /* !CONFIG_DISABLE_ES2015_CLASS */

context_p->token.line = context_p->line;
context_p->token.column = context_p->column;

Expand Down Expand Up @@ -2262,6 +2302,17 @@ lexer_expect_object_literal_id (parser_context_t *context_p, /**< context */
}
}
}
#ifndef CONFIG_DISABLE_ES2015_CLASS
if ((context_p->status_flags & PARSER_IS_CLASS)
&& !must_be_identifier
&& !is_static_method
&& context_p->token.lit_location.length == 6
&& lexer_compare_raw_identifier_to_current (context_p, "static", 6))
{
context_p->token.type = LEXER_KEYW_STATIC;
return;
}
#endif /* !CONFIG_DISABLE_ES2015_CLASS */

create_literal_object = true;
}
Expand Down Expand Up @@ -2297,6 +2348,17 @@ lexer_expect_object_literal_id (parser_context_t *context_p, /**< context */
}
}

#ifndef CONFIG_DISABLE_ES2015_CLASS
if ((context_p->status_flags & PARSER_IS_CLASS)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add !must_be_identifier

&& !must_be_identifier
&& !is_static_method
&& lexer_compare_raw_identifier_to_current (context_p, "constructor", 11))
{
context_p->token.type = LEXER_CLASS_CONSTRUCTOR;
return;
}
#endif /* !CONFIG_DISABLE_ES2015_CLASS */

if (create_literal_object)
{
lexer_construct_literal_object (context_p,
Expand Down
1 change: 1 addition & 0 deletions jerry-core/parser/js/js-lexer.h
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ typedef enum
LEXER_PROPERTY_SETTER, /**< property setter function */
LEXER_COMMA_SEP_LIST, /**< comma separated bracketed expression list */
LEXER_SCAN_SWITCH, /**< special value for switch pre-scan */
LEXER_CLASS_CONSTRUCTOR, /**< special value for class constructor method */

/* Future reserved words: these keywords
* must form a group after all other keywords. */
Expand Down
Loading