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

Conversation

rerobika
Copy link
Member

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]

Copy link
Member

@zherczeg zherczeg left a comment

Choose a reason for hiding this comment

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

I worry about as snapshots, what happens if a snapshot has classes and it is executed by a jerry without snapshots.

@@ -690,6 +701,10 @@ ecma_op_function_construct_simple_or_external (ecma_object_t *func_obj_p, /**< F
arguments_list_len),
ret_value);

#ifndef CONFIG_DISABLE_ES2015_CLASS
JERRY_CONTEXT (class_invoked_with_new) = false;
Copy link
Member

Choose a reason for hiding this comment

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

Who sets this value to false if ecma_op_function_call throws an error? Using a global value is risky since it always finds way to escape :(

Copy link
Member Author

Choose a reason for hiding this comment

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

This can be improved after #2414 lands.

@@ -549,6 +549,12 @@
VM_OC_RESOURCE_NAME) \
CBC_OPCODE (CBC_EXT_LINE, CBC_NO_FLAG, 0, \
VM_OC_LINE) \
CBC_OPCODE (CBC_EXT_SET_UP_PROPERTY, CBC_HAS_LITERAL_ARG, -1, \
Copy link
Member

Choose a reason for hiding this comment

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

setup property :)

I would rather use CBC_EXT_SET_STATIC_PROPERTY since it is only used by static functions.

@@ -665,6 +671,9 @@ 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 */
#ifndef CONFIG_DISABLE_ES2015_CLASS
CBC_CODE_FLAGS_CLASS_CONSTRUCTOR = (1u << 10), /**< this function is a class constructor */
Copy link
Member

Choose a reason for hiding this comment

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

I would just use CBC_CODE_FLAGS_CONSTRUCTOR since we might use it later for other purposes.

*/
static const lexer_lit_location_t lexer_get_literal =
{
(const uint8_t *) "get", 3, LEXER_IDENT_LITERAL, false
Copy link
Member

Choose a reason for hiding this comment

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

I think get and set are already there.

@@ -2238,6 +2259,8 @@ static const lexer_lit_location_t lexer_set_literal =
(const uint8_t *) "set", 3, LEXER_IDENT_LITERAL, false
};

#endif /* CONFIG_DISABLE_ES2015_CLASS */
Copy link
Member

Choose a reason for hiding this comment

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

Just keep these for classes.

@@ -531,7 +541,7 @@ lexer_parse_identifier (parser_context_t *context_p, /**< context */
{
if (keyword_p->type >= LEXER_FIRST_FUTURE_STRICT_RESERVED_WORD)
{
if (context_p->status_flags & PARSER_IS_STRICT)
if (context_p->status_flags & PARSER_IS_STRICT && !LEXER_INSIDE_CLASS)
Copy link
Member

Choose a reason for hiding this comment

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

I have a feeling we should not require this.

lexer_skip_spaces (context_p);

if (context_p->source_p < context_p->source_end_p
&& context_p->source_p[0] != LIT_CHAR_COLON)
Copy link
Member

Choose a reason for hiding this comment

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

What is the purpose of this check. colons are allowed in classes?


while (true)
{
if (!is_static)
Copy link
Member

Choose a reason for hiding this comment

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

First function cannot be static?

@@ -1046,8 +1046,14 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */
continue;
}
case VM_OC_SET_PROPERTY:
case VM_OC_SET_UP_PROPERTY:
Copy link
Member

Choose a reason for hiding this comment

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

Add a define around this, so this byte code is not executed when the feature is disabled.

@rerobika
Copy link
Member Author

The patch is updated based on your thoughts except the class invoking part. I'm looking for a better solution to handle this problem.

Copy link
Member

@zherczeg zherczeg left a comment

Choose a reason for hiding this comment

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

Good patch, only some minor things remained.

#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.

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

#ifndef CONFIG_DISABLE_ES2015_CLASS
/* Skip empty statements */
while (context_p->status_flags & PARSER_IS_CLASS && *context_p->source_p == LIT_CHAR_SEMICOLON)
Copy link
Member

Choose a reason for hiding this comment

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

Add brackets around the and operation.

if (!must_be_identifier
&& context_p->token.lit_location.length == 6
&& (context_p->status_flags & PARSER_IS_CLASS)
&& lexer_compare_raw_identifier_to_current (context_p, "static", 6))
Copy link
Member

Choose a reason for hiding this comment

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

Check (context_p->status_flags & PARSER_IS_CLASS) first.

@@ -2297,6 +2335,15 @@ 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.

Brackets again.

break;
}
#endif /* !CONFIG_DISABLE_ES2015_CLASS */

Copy link
Member

Choose a reason for hiding this comment

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

Cannot we return early form this function? Or not even call it?

Copy link
Member Author

Choose a reason for hiding this comment

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

True, calling it is unnecessary.


if (type == LEXER_RIGHT_BRACE && stack_top == SCAN_STACK_HEAD)
{
mode = SCAN_MODE_STATEMENT;
Copy link
Member

Choose a reason for hiding this comment

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

Cant be an expression?

Copy link
Member Author

Choose a reason for hiding this comment

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

True, it can.

}
case PARSER_ERR_CLASS_STATIC_PROPERTY_NAME_PROTOTYPE:
{
return "Classes may not have a static property named 'prototype'.";
Copy link
Member

Choose a reason for hiding this comment

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

called prototype

}
else
{
JERRY_DEBUG_MSG ("\n--- Function parsing start ---\n\n");
Copy link
Member

Choose a reason for hiding this comment

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

Does it look better with a ?: or the line is too long?

Copy link
Member Author

Choose a reason for hiding this comment

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

It looks better but the lines are too long :/

JERRY_ASSERT (byte_code_start_p[0] == CBC_EXT_OPCODE);
#ifndef CONFIG_DISABLE_ES2015_CLASS
opfunc_set_accessor (VM_OC_GROUP_GET_INDEX (opcode_data) == VM_OC_SET_GETTER,
stack_top_p[(byte_code_start_p[1] > CBC_EXT_SET_SETTER) ? -2 : -1],
Copy link
Member

Choose a reason for hiding this comment

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

I would use a const int index just like above instead of duplicating the code.

@@ -1047,7 +1047,12 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */
}
case VM_OC_SET_PROPERTY:
{
#ifndef CONFIG_DISABLE_ES2015_CLASS
const int index = (byte_code_start_p[0] == CBC_EXT_OPCODE) ? -2 : -1;
Copy link
Member

Choose a reason for hiding this comment

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

Just create a const int index = - 1 and use the index for accessing the stack top.

@rerobika rerobika force-pushed the es2015_class branch 2 times, most recently from b20b452 to 523148c Compare July 11, 2018 13:56
&& !is_static_method
&& lexer_compare_raw_identifier_to_current (context_p, "constructor", 11))
{
context_p->status_flags |= PARSER_CLASS_CONSTRUCTOR;
Copy link
Member

Choose a reason for hiding this comment

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

It would be good to introduce a virtual token here.


while (true)
{
lexer_expect_object_literal_id (context_p, false);
Copy link
Member

Choose a reason for hiding this comment

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

Add a skip empty semicolons here (if not static).

@@ -2297,6 +2338,16 @@ 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

* Parse class as an object literal.
*/
void
parser_parse_class_literal (parser_context_t *context_p, /**< context */
Copy link
Member

Choose a reason for hiding this comment

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

Make this static!

break;
}

if (type == LEXER_RIGHT_BRACE && stack_top == SCAN_STACK_HEAD)
Copy link
Member

Choose a reason for hiding this comment

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

switch(1) { default: (class A{} % 1) }


parser_emit_cbc_literal (context_p, CBC_SET_PROPERTY, context_p->lit_object.index);

if (is_statement && class_ident_index != PARSER_MAXIMUM_NUMBER_OF_LITERALS)
Copy link
Member

Choose a reason for hiding this comment

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

class_ident_index != PARSER_MAXIMUM_NUMBER_OF_LITERALS unnecessary

@@ -2596,7 +2646,18 @@ parser_parse_function (parser_context_t *context_p, /**< context */
#ifdef PARSER_DUMP_BYTE_CODE
if (context_p->is_show_opcodes)
{
#ifndef CONFIG_DISABLE_ES2015_CLASS
if (context_p->status_flags & PARSER_CLASS_CONSTRUCTOR)
Copy link
Member

Choose a reason for hiding this comment

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

Bool value

const int index = (byte_code_start_p[0] == CBC_EXT_OPCODE) ? -2 : -1;
#else
const int index = -1;
#endif /* !CONFIG_DISABLE_ES2015_CLASS */
Copy link
Member

Choose a reason for hiding this comment

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

Newline after.

const int index = (byte_code_start_p[1] > CBC_EXT_SET_SETTER) ? -2 : -1;
#else
const int index = -1;
#endif /* !CONFIG_DISABLE_ES2015_CLASS */
Copy link
Member

Choose a reason for hiding this comment

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

Ditto.

@rerobika
Copy link
Member Author

@zherczeg Thanks for the reviews, the PR is updated.

(is_constructor ? JERRY_DEBUG_MSG ("\n--- Class constructor parsing start ---\n\n")
: JERRY_DEBUG_MSG ("\n--- Function parsing start ---\n\n"));

#else
Copy link
Member

Choose a reason for hiding this comment

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

JERRY_DEBUG_MSG (is_constructor ? "\n--- Class constructor parsing start ---\n\n"
                                : "\n--- Function parsing start ---\n\n");

Remove newline after it.

bool is_constructor = context_p->status_flags & PARSER_CLASS_CONSTRUCTOR;
(is_constructor ? JERRY_DEBUG_MSG ("\n--- Class constructor parsing end ---\n\n")
: JERRY_DEBUG_MSG ("\n--- Function parsing end ---\n\n"));
#else
Copy link
Member

Choose a reason for hiding this comment

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

Ditto.

Copy link
Member

@zherczeg zherczeg left a comment

Choose a reason for hiding this comment

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

LGTM

/* Class expression may contain an identifier. */
if (context_p->token.type == LEXER_LITERAL && context_p->token.lit_location.type == LEXER_IDENT_LITERAL)
{
//NOTE: If 'Function.name' will be supported, the current literal object must be set to 'name' property.
Copy link
Contributor

Choose a reason for hiding this comment

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

/* NOTE: If 'Function.name' will be supported, the current literal object must be set to 'name' property. */

Copy link
Member Author

Choose a reason for hiding this comment

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

Good catch, I haven't noticed it.

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]
Copy link
Contributor

@LaszloLango LaszloLango left a comment

Choose a reason for hiding this comment

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

LGTM

@yichoi yichoi merged commit 43aae19 into jerryscript-project:master Jul 12, 2018
yorkie added a commit to yodaos-project/ShadowNode that referenced this pull request Oct 25, 2018
This includes the following syntax:
- Class statement.
- Class expression.
- Static methods.

Author: Robert Fancsik <[email protected]>
PR-URL: jerryscript-project/jerryscript#2404
legendecas pushed a commit to yodaos-project/ShadowNode that referenced this pull request Nov 13, 2018
This includes the following syntax:
- Class statement.
- Class expression.
- Static methods.

Author: Robert Fancsik <[email protected]>
PR-URL: jerryscript-project/jerryscript#2404
@rerobika rerobika deleted the es2015_class branch February 28, 2019 11:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants