Skip to content

Commit 1342693

Browse files
committed
Implement ES2015 class feature (part I.)
This patch is the first milestone of the implementation of this new language element. Currently supported: - Class statement - Class expression - Static methods JerryScript-DCO-1.0-Signed-off-by: Robert Fancsik [email protected]
1 parent 34c0810 commit 1342693

File tree

17 files changed

+764
-9
lines changed

17 files changed

+764
-9
lines changed

jerry-core/api/jerry-snapshot.c

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,11 @@
3434
* @return configuration flags
3535
*/
3636
static inline uint32_t JERRY_ATTR_ALWAYS_INLINE
37-
snapshot_get_global_flags (bool has_regex) /**< regex literal is present */
37+
snapshot_get_global_flags (bool has_regex, /**< regex literal is present */
38+
bool has_class) /**< class literal is present */
3839
{
3940
JERRY_UNUSED (has_regex);
41+
JERRY_UNUSED (has_class);
4042

4143
uint32_t flags = 0;
4244

@@ -45,7 +47,10 @@ snapshot_get_global_flags (bool has_regex) /**< regex literal is present */
4547
#endif /* JERRY_CPOINTER_32_BIT */
4648
#ifndef CONFIG_DISABLE_REGEXP_BUILTIN
4749
flags |= (has_regex ? JERRY_SNAPSHOT_HAS_REGEX_LITERAL : 0);
48-
#endif /* CONFIG_DISABLE_REGEXP_BUILTIN */
50+
#endif /* !CONFIG_DISABLE_REGEXP_BUILTIN */
51+
#ifndef CONFIG_DISABLE_ES2015_CLASS
52+
flags |= (has_class ? JERRY_SNAPSHOT_HAS_CLASS_LITERAL : 0);
53+
#endif /* !CONFIG_DISABLE_ES2015_CLASS */
4954

5055
return flags;
5156
} /* snapshot_get_global_flags */
@@ -61,8 +66,11 @@ snapshot_check_global_flags (uint32_t global_flags) /**< global flags */
6166
#ifndef CONFIG_DISABLE_REGEXP_BUILTIN
6267
global_flags &= (uint32_t) ~JERRY_SNAPSHOT_HAS_REGEX_LITERAL;
6368
#endif /* !CONFIG_DISABLE_REGEXP_BUILTIN */
69+
#ifndef CONFIG_DISABLE_ES2015_CLASS
70+
global_flags &= (uint32_t) ~JERRY_SNAPSHOT_HAS_CLASS_LITERAL;
71+
#endif /* !CONFIG_DISABLE_ES2015_CLASS */
6472

65-
return global_flags == snapshot_get_global_flags (false);
73+
return global_flags == snapshot_get_global_flags (false, false);
6674
} /* snapshot_check_global_flags */
6775

6876
#endif /* JERRY_ENABLE_SNAPSHOT_SAVE || JERRY_ENABLE_SNAPSHOT_EXEC */
@@ -77,6 +85,7 @@ typedef struct
7785
size_t snapshot_buffer_write_offset;
7886
ecma_value_t snapshot_error;
7987
bool regex_found;
88+
bool class_found;
8089
} snapshot_globals_t;
8190

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

166+
#ifndef CONFIG_DISABLE_ES2015_CLASS
167+
if (compiled_code_p->status_flags & CBC_CODE_FLAGS_CONSTRUCTOR)
168+
{
169+
globals_p->class_found = true;
170+
}
171+
#endif /* !CONFIG_DISABLE_ES2015_CLASS */
172+
157173
#ifndef CONFIG_DISABLE_REGEXP_BUILTIN
158174
if (!(compiled_code_p->status_flags & CBC_CODE_FLAGS_FUNCTION))
159175
{
@@ -739,6 +755,7 @@ jerry_generate_snapshot_with_args (const jerry_char_t *resource_name_p, /**< scr
739755
globals.snapshot_buffer_write_offset = aligned_header_size;
740756
globals.snapshot_error = ECMA_VALUE_EMPTY;
741757
globals.regex_found = false;
758+
globals.class_found = false;
742759

743760
parse_status = parser_parse_script (args_p,
744761
args_size,
@@ -769,7 +786,7 @@ jerry_generate_snapshot_with_args (const jerry_char_t *resource_name_p, /**< scr
769786
jerry_snapshot_header_t header;
770787
header.magic = JERRY_SNAPSHOT_MAGIC;
771788
header.version = JERRY_SNAPSHOT_VERSION;
772-
header.global_flags = snapshot_get_global_flags (globals.regex_found);
789+
header.global_flags = snapshot_get_global_flags (globals.regex_found, globals.class_found);
773790
header.lit_table_offset = (uint32_t) globals.snapshot_buffer_write_offset;
774791
header.number_of_funcs = 1;
775792
header.func_offsets[0] = aligned_header_size;

jerry-core/api/jerry-snapshot.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ typedef struct
4141
/**
4242
* Jerry snapshot format version.
4343
*/
44-
#define JERRY_SNAPSHOT_VERSION (14u)
44+
#define JERRY_SNAPSHOT_VERSION (15u)
4545

4646
/**
4747
* Snapshot configuration flags.
@@ -50,6 +50,7 @@ typedef enum
5050
{
5151
/* 8 bits are reserved for dynamic features */
5252
JERRY_SNAPSHOT_HAS_REGEX_LITERAL = (1u << 0), /**< byte code has regex literal */
53+
JERRY_SNAPSHOT_HAS_CLASS_LITERAL = (1u << 1), /**< byte code has class literal */
5354
/* 24 bits are reserved for compile time features */
5455
JERRY_SNAPSHOT_FOUR_BYTE_CPOINTER = (1u << 8) /**< compressed pointers are four byte long */
5556
} jerry_snapshot_global_flags_t;

jerry-core/config.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
*/
3838
#ifdef CONFIG_DISABLE_ES2015
3939
# define CONFIG_DISABLE_ES2015_ARROW_FUNCTION
40+
# define CONFIG_DISABLE_ES2015_CLASS
4041
# define CONFIG_DISABLE_ES2015_BUILTIN
4142
# define CONFIG_DISABLE_ES2015_PROMISE_BUILTIN
4243
# define CONFIG_DISABLE_ES2015_TEMPLATE_STRINGS

jerry-core/ecma/operations/ecma-function-object.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,13 @@ ecma_op_function_call (ecma_object_t *func_obj_p, /**< Function object */
448448

449449
const ecma_compiled_code_t *bytecode_data_p = ecma_op_function_get_compiled_code (ext_func_p);
450450

451+
#ifndef CONFIG_DISABLE_ES2015_CLASS
452+
if (bytecode_data_p->status_flags & CBC_CODE_FLAGS_CONSTRUCTOR && !JERRY_CONTEXT (class_invoked_with_new))
453+
{
454+
return ecma_raise_type_error (ECMA_ERR_MSG ("Class constructor cannot be invoked without 'new'."));
455+
}
456+
#endif /* !CONFIG_DISABLE_ES2015_CLASS */
457+
451458
is_strict = (bytecode_data_p->status_flags & CBC_CODE_FLAGS_STRICT_MODE) ? true : false;
452459
is_no_lex_env = (bytecode_data_p->status_flags & CBC_CODE_FLAGS_LEXICAL_ENV_NOT_NEEDED) ? true : false;
453460

@@ -682,6 +689,10 @@ ecma_op_function_construct_simple_or_external (ecma_object_t *func_obj_p, /**< F
682689
* See also: ecma_object_get_class_name.
683690
*/
684691

692+
#ifndef CONFIG_DISABLE_ES2015_CLASS
693+
JERRY_CONTEXT (class_invoked_with_new) = true;
694+
#endif /* !CONFIG_DISABLE_ES2015_CLASS */
695+
685696
/* 8. */
686697
ECMA_TRY_CATCH (call_completion,
687698
ecma_op_function_call (func_obj_p,
@@ -690,6 +701,10 @@ ecma_op_function_construct_simple_or_external (ecma_object_t *func_obj_p, /**< F
690701
arguments_list_len),
691702
ret_value);
692703

704+
#ifndef CONFIG_DISABLE_ES2015_CLASS
705+
JERRY_CONTEXT (class_invoked_with_new) = false;
706+
#endif /* !CONFIG_DISABLE_ES2015_CLASS */
707+
693708
/* 9. */
694709
if (ecma_is_value_object (call_completion))
695710
{

jerry-core/jcontext/jcontext.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,9 @@ typedef struct
131131
#ifdef JMEM_STATS
132132
jmem_heap_stats_t jmem_heap_stats; /**< heap's memory usage statistics */
133133
#endif /* JMEM_STATS */
134+
#ifndef CONFIG_DISABLE_ES2015_CLASS
135+
bool class_invoked_with_new; /**< true if class invoked with 'new', false otherwise */
136+
#endif /* !CONFIG_DISABLE_ES2015_CLASS */
134137
} jerry_context_t;
135138

136139
#ifndef CONFIG_ECMA_LCACHE_DISABLE

jerry-core/parser/js/byte-code.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -551,6 +551,12 @@
551551
VM_OC_RESOURCE_NAME) \
552552
CBC_OPCODE (CBC_EXT_LINE, CBC_NO_FLAG, 0, \
553553
VM_OC_LINE) \
554+
CBC_OPCODE (CBC_EXT_SET_STATIC_PROPERTY, CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, 0, \
555+
VM_OC_SET_PROPERTY | VM_OC_GET_LITERAL_LITERAL) \
556+
CBC_OPCODE (CBC_EXT_SET_STATIC_SETTER, CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, 0, \
557+
VM_OC_SET_SETTER | VM_OC_GET_LITERAL_LITERAL) \
558+
CBC_OPCODE (CBC_EXT_SET_STATIC_GETTER, CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, 0, \
559+
VM_OC_SET_GETTER | VM_OC_GET_LITERAL_LITERAL) \
554560
\
555561
/* Binary compound assignment opcodes with pushing the result. */ \
556562
CBC_EXT_BINARY_LVALUE_OPERATION (CBC_EXT_ASSIGN_ADD, \
@@ -667,6 +673,7 @@ typedef enum
667673
CBC_CODE_FLAGS_ARROW_FUNCTION = (1u << 7), /**< this function is an arrow function */
668674
CBC_CODE_FLAGS_STATIC_FUNCTION = (1u << 8), /**< this function is a static snapshot function */
669675
CBC_CODE_FLAGS_DEBUGGER_IGNORE = (1u << 9), /**< this function should be ignored by debugger */
676+
CBC_CODE_FLAGS_CONSTRUCTOR = (1u << 10), /**< this function is a constructor */
670677
} cbc_code_flags;
671678

672679
#define CBC_OPCODE(arg1, arg2, arg3, arg4) arg1,

jerry-core/parser/js/js-lexer.c

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1308,6 +1308,25 @@ lexer_check_colon (parser_context_t *context_p) /**< context */
13081308
&& context_p->source_p[0] == (uint8_t) LIT_CHAR_COLON);
13091309
} /* lexer_check_colon */
13101310

1311+
#ifndef CONFIG_DISABLE_ES2015_CLASS
1312+
/**
1313+
* Checks whether the next token is a left parenthesis.
1314+
*
1315+
* @return true - if the next token is a left parenthesis
1316+
* false - otherwise
1317+
*/
1318+
bool
1319+
lexer_check_left_paren (parser_context_t *context_p) /**< context */
1320+
{
1321+
lexer_skip_spaces (context_p);
1322+
1323+
context_p->token.flags = (uint8_t) (context_p->token.flags | LEXER_NO_SKIP_SPACES);
1324+
1325+
return (context_p->source_p < context_p->source_end_p
1326+
&& context_p->source_p[0] == (uint8_t) LIT_CHAR_LEFT_PAREN);
1327+
} /* lexer_check_left_paren */
1328+
#endif /* !CONFIG_DISABLE_ES2015_CLASS */
1329+
13111330
#ifndef CONFIG_DISABLE_ES2015_ARROW_FUNCTION
13121331

13131332
/**
@@ -2231,6 +2250,15 @@ lexer_expect_object_literal_id (parser_context_t *context_p, /**< context */
22312250
{
22322251
lexer_skip_spaces (context_p);
22332252

2253+
#ifndef CONFIG_DISABLE_ES2015_CLASS
2254+
/* Skip empty statements */
2255+
while (context_p->status_flags & PARSER_IS_CLASS && *context_p->source_p == LIT_CHAR_SEMICOLON)
2256+
{
2257+
lexer_next_token (context_p);
2258+
lexer_skip_spaces (context_p);
2259+
}
2260+
#endif /* !CONFIG_DISABLE_ES2015_CLASS */
2261+
22342262
context_p->token.line = context_p->line;
22352263
context_p->token.column = context_p->column;
22362264

@@ -2262,6 +2290,16 @@ lexer_expect_object_literal_id (parser_context_t *context_p, /**< context */
22622290
}
22632291
}
22642292
}
2293+
#ifndef CONFIG_DISABLE_ES2015_CLASS
2294+
if (!must_be_identifier
2295+
&& context_p->token.lit_location.length == 6
2296+
&& (context_p->status_flags & PARSER_IS_CLASS)
2297+
&& lexer_compare_raw_identifier_to_current (context_p, "static", 6))
2298+
{
2299+
context_p->token.type = LEXER_KEYW_STATIC;
2300+
return;
2301+
}
2302+
#endif /* !CONFIG_DISABLE_ES2015_CLASS */
22652303

22662304
create_literal_object = true;
22672305
}
@@ -2297,6 +2335,15 @@ lexer_expect_object_literal_id (parser_context_t *context_p, /**< context */
22972335
}
22982336
}
22992337

2338+
#ifndef CONFIG_DISABLE_ES2015_CLASS
2339+
if (context_p->status_flags & PARSER_IS_CLASS
2340+
&& lexer_compare_raw_identifier_to_current (context_p, "constructor", 11))
2341+
{
2342+
context_p->status_flags |= PARSER_CLASS_CONSTRUCTOR;
2343+
return;
2344+
}
2345+
#endif /* !CONFIG_DISABLE_ES2015_CLASS */
2346+
23002347
if (create_literal_object)
23012348
{
23022349
lexer_construct_literal_object (context_p,

0 commit comments

Comments
 (0)