Skip to content

Commit 1a3d196

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 04dcefe commit 1a3d196

File tree

17 files changed

+775
-14
lines changed

17 files changed

+775
-14
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: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,14 @@ ecma_op_function_has_instance (ecma_object_t *func_obj_p, /**< Function object *
402402
return ecma_make_boolean_value (result);
403403
} /* ecma_op_function_has_instance */
404404

405+
406+
#ifndef CONFIG_DISABLE_ES2015_CLASS
407+
/**
408+
* Indicates whether the class has been invoked with 'new'.
409+
*/
410+
#define ECMA_CLASS_CONSTRUCT_FLAG ((uintptr_t) 0x01u)
411+
#endif /* !CONFIG_DISABLE_ES2015_CLASS */
412+
405413
/**
406414
* Sets the construct flag in the arguments list pointer.
407415
*
@@ -413,8 +421,10 @@ ecma_op_function_set_construct_flag (const ecma_value_t *arguments_list_p) /**<
413421
/* Any ecma value list must be aligned to 4 byte. */
414422
JERRY_ASSERT ((((uintptr_t) arguments_list_p) & 0x3) == 0);
415423

416-
/* Currently it returns with the same pointer. When classes
417-
* will be enabled, it will set the lowest bit. */
424+
#ifndef CONFIG_DISABLE_ES2015_CLASS
425+
arguments_list_p = (const ecma_value_t *)(((uintptr_t) arguments_list_p) | ECMA_CLASS_CONSTRUCT_FLAG);
426+
#endif /* !CONFIG_DISABLE_ES2015_CLASS */
427+
418428
return arguments_list_p;
419429
} /* ecma_op_function_set_construct_flag */
420430

@@ -426,8 +436,10 @@ ecma_op_function_set_construct_flag (const ecma_value_t *arguments_list_p) /**<
426436
static inline const ecma_value_t * JERRY_ATTR_ALWAYS_INLINE
427437
ecma_op_function_clear_construct_flag (const ecma_value_t *arguments_list_p) /**< modified arguments list pointer */
428438
{
429-
/* Currently it returns with the same pointer. When classes
430-
* will be enabled, the lowest bit will be cleared. */
439+
#ifndef CONFIG_DISABLE_ES2015_CLASS
440+
arguments_list_p = (const ecma_value_t *)(((uintptr_t) arguments_list_p) & ~ECMA_CLASS_CONSTRUCT_FLAG);
441+
#endif /* !CONFIG_DISABLE_ES2015_CLASS */
442+
431443
return arguments_list_p;
432444
} /* ecma_op_function_clear_construct_flag */
433445

@@ -439,8 +451,12 @@ ecma_op_function_clear_construct_flag (const ecma_value_t *arguments_list_p) /**
439451
static inline bool JERRY_ATTR_ALWAYS_INLINE
440452
ecma_op_function_has_construct_flag (const ecma_value_t *arguments_list_p) /**< modified arguments list pointer */
441453
{
454+
#ifndef CONFIG_DISABLE_ES2015_CLASS
455+
return (((uintptr_t) arguments_list_p) & ECMA_CLASS_CONSTRUCT_FLAG);
456+
#else
442457
JERRY_UNUSED (arguments_list_p);
443458
return false;
459+
#endif /* !CONFIG_DISABLE_ES2015_CLASS */
444460
} /* ecma_op_function_has_construct_flag */
445461

446462
/**
@@ -496,6 +512,14 @@ ecma_op_function_call (ecma_object_t *func_obj_p, /**< Function object */
496512

497513
const ecma_compiled_code_t *bytecode_data_p = ecma_op_function_get_compiled_code (ext_func_p);
498514

515+
#ifndef CONFIG_DISABLE_ES2015_CLASS
516+
if (bytecode_data_p->status_flags & CBC_CODE_FLAGS_CONSTRUCTOR &&
517+
!ecma_op_function_has_construct_flag (arguments_list_p))
518+
{
519+
return ecma_raise_type_error (ECMA_ERR_MSG ("Class constructor cannot be invoked without 'new'."));
520+
}
521+
#endif /* !CONFIG_DISABLE_ES2015_CLASS */
522+
499523
is_strict = (bytecode_data_p->status_flags & CBC_CODE_FLAGS_STRICT_MODE) ? true : false;
500524
is_no_lex_env = (bytecode_data_p->status_flags & CBC_CODE_FLAGS_LEXICAL_ENV_NOT_NEEDED) ? true : false;
501525

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: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,23 @@ lexer_skip_spaces (parser_context_t *context_p) /**< context */
277277
}
278278
} /* lexer_skip_spaces */
279279

280+
#ifndef CONFIG_DISABLE_ES2015_CLASS
281+
/**
282+
* Skip all the continuous empty statements.
283+
*/
284+
void
285+
lexer_skip_empty_statements (parser_context_t *context_p) /**< context */
286+
{
287+
lexer_skip_spaces (context_p);
288+
289+
while (*context_p->source_p == LIT_CHAR_SEMICOLON)
290+
{
291+
lexer_next_token (context_p);
292+
lexer_skip_spaces (context_p);
293+
}
294+
} /* lexer_skip_empty_statements */
295+
#endif /* !CONFIG_DISABLE_ES2015_CLASS */
296+
280297
/**
281298
* Keyword data.
282299
*/
@@ -1308,6 +1325,25 @@ lexer_check_colon (parser_context_t *context_p) /**< context */
13081325
&& context_p->source_p[0] == (uint8_t) LIT_CHAR_COLON);
13091326
} /* lexer_check_colon */
13101327

1328+
#ifndef CONFIG_DISABLE_ES2015_CLASS
1329+
/**
1330+
* Checks whether the next token is a left parenthesis.
1331+
*
1332+
* @return true - if the next token is a left parenthesis
1333+
* false - otherwise
1334+
*/
1335+
bool
1336+
lexer_check_left_paren (parser_context_t *context_p) /**< context */
1337+
{
1338+
lexer_skip_spaces (context_p);
1339+
1340+
context_p->token.flags = (uint8_t) (context_p->token.flags | LEXER_NO_SKIP_SPACES);
1341+
1342+
return (context_p->source_p < context_p->source_end_p
1343+
&& context_p->source_p[0] == (uint8_t) LIT_CHAR_LEFT_PAREN);
1344+
} /* lexer_check_left_paren */
1345+
#endif /* !CONFIG_DISABLE_ES2015_CLASS */
1346+
13111347
#ifndef CONFIG_DISABLE_ES2015_ARROW_FUNCTION
13121348

13131349
/**
@@ -2231,6 +2267,10 @@ lexer_expect_object_literal_id (parser_context_t *context_p, /**< context */
22312267
{
22322268
lexer_skip_spaces (context_p);
22332269

2270+
#ifndef CONFIG_DISABLE_ES2015_CLASS
2271+
bool is_static_method = (context_p->token.type == LEXER_KEYW_STATIC);
2272+
#endif /* !CONFIG_DISABLE_ES2015_CLASS */
2273+
22342274
context_p->token.line = context_p->line;
22352275
context_p->token.column = context_p->column;
22362276

@@ -2262,6 +2302,17 @@ lexer_expect_object_literal_id (parser_context_t *context_p, /**< context */
22622302
}
22632303
}
22642304
}
2305+
#ifndef CONFIG_DISABLE_ES2015_CLASS
2306+
if ((context_p->status_flags & PARSER_IS_CLASS)
2307+
&& !must_be_identifier
2308+
&& !is_static_method
2309+
&& context_p->token.lit_location.length == 6
2310+
&& lexer_compare_raw_identifier_to_current (context_p, "static", 6))
2311+
{
2312+
context_p->token.type = LEXER_KEYW_STATIC;
2313+
return;
2314+
}
2315+
#endif /* !CONFIG_DISABLE_ES2015_CLASS */
22652316

22662317
create_literal_object = true;
22672318
}
@@ -2297,6 +2348,17 @@ lexer_expect_object_literal_id (parser_context_t *context_p, /**< context */
22972348
}
22982349
}
22992350

2351+
#ifndef CONFIG_DISABLE_ES2015_CLASS
2352+
if ((context_p->status_flags & PARSER_IS_CLASS)
2353+
&& !must_be_identifier
2354+
&& !is_static_method
2355+
&& lexer_compare_raw_identifier_to_current (context_p, "constructor", 11))
2356+
{
2357+
context_p->token.type = LEXER_CLASS_CONSTRUCTOR;
2358+
return;
2359+
}
2360+
#endif /* !CONFIG_DISABLE_ES2015_CLASS */
2361+
23002362
if (create_literal_object)
23012363
{
23022364
lexer_construct_literal_object (context_p,

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ typedef enum
149149
LEXER_PROPERTY_SETTER, /**< property setter function */
150150
LEXER_COMMA_SEP_LIST, /**< comma separated bracketed expression list */
151151
LEXER_SCAN_SWITCH, /**< special value for switch pre-scan */
152+
LEXER_CLASS_CONSTRUCTOR, /**< special value for class constructor method */
152153

153154
/* Future reserved words: these keywords
154155
* must form a group after all other keywords. */

0 commit comments

Comments
 (0)