-
Notifications
You must be signed in to change notification settings - Fork 684
[WIP] Support for shorthand object literal notation. #2432
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
[WIP] Support for shorthand object literal notation. #2432
Conversation
Currently there is one bug that I'm aware of. The following will throw a try {
var a = { true, false, null };
assert(false);
} catch (e) {
assert(e instanceof SyntaxError);
} One obvious way I can fix this is by checking to see if the literal is reserved/keyword when I know I have shorthand notation. Looks like there currently isn't a helper function available to do this, so I would probably pull out what is in Also, it looks like I'm failing two test262 tests. I think the second one is just a side-effect of failing the first one, so the first test is here. I've copied that file over locally and ran it using my local build. The resulting bytecode looks identical to the result when I run on master:
At this point I can't figure out why the test would fail. Any ideas? |
4f2b16f
to
089ac6a
Compare
|
@zherczeg Yes that's correct. No reserved words or keywords allowed. |
@@ -1343,7 +1342,6 @@ lexer_check_left_paren (parser_context_t *context_p) /**< context */ | |||
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 */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do you remove this check? Isn't this an ES6 feature?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It came from the ES6 classes pull request but I also use it for checking if we have a method (it checks to see if there is a left parenthesis after the identifier). If I keep the ifdef around it then if the user disables classes I won't have this function available.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mean this shorthand notation isn/t an es6 feature? Then we might need to change the ifdef.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. I'm planning on adding computed properties at some point, so all these new language features to object initializers could fit nicely into this macro.
jerry-core/parser/js/js-lexer.c
Outdated
&& context_p->source_p[0] != LIT_CHAR_COLON) | ||
&& context_p->source_p[0] != LIT_CHAR_COLON | ||
&& context_p->source_p[0] != LIT_CHAR_RIGHT_BRACE | ||
&& context_p->source_p[0] != LIT_CHAR_COMMA) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about the pre scanner?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry I'm not sure I understand this comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mean the pre-scanner which checks JS code early. It is used by switch
, for-in
, etc.
@@ -666,23 +666,58 @@ parser_parse_object_literal (parser_context_t *context_p) /**< context */ | |||
literal_index, | |||
PARSER_OBJECT_PROPERTY_VALUE); | |||
|
|||
lexer_next_token (context_p); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do you remove lexer_next_token
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't really remove it, I just moved it down into the if statement. If we have the shorthand notation, I cant skip this early because I need to create a literal object from the current token.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You have to create a literal regardless what is after the identifier, doesn't it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently that's what I'm doing, but I won't emit bytecode if I see what's after the identifier makes the expression ill-formed. I thought maybe emitting the bytecode was why I'm failing the test262 test but it didn't make the test pass. Doing it this way though, I match what master outputs for this test.
1565ca9
to
4a2a11d
Compare
Update: Fixed the issue with keywords/reserved words as identifiers not throwing Also, I've noticed that running test262 with all ES2015 features enabled fail five tests (two of them are from these changes). Looking at the code, we only pull in from their ECMAScript 5.1 branch, which means we only test for that particular version? Should we fix the failures or leave them? For context on what my PR breaks, refer to my first comment^. |
82c2666
to
922111a
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When we add new code, it is not just a technical problem, it is also a quality maintaining problem. This seemingly simple task is actually quite a big challenge (even for me!), since you need to figure out a way how to make this part of the existing code and existing design principles. That requires a lot of thinking on implementation variations and selecting the best one.
Unfortunately I don't have time thinking on this task, but I hope you will find a nicer solution. Just ask @rerobika how many hours we spent on discussing the class
implementation. And how many times he had to refactor his code. Many times even I throw away my own code and restart it from scratch because it does not look quality enough.
jerry-core/config.h
Outdated
# define CONFIG_DISABLE_ES2015_PROMISE_BUILTIN | ||
# define CONFIG_DISABLE_ES2015_SHORTHAND_OBJ_LITERAL_NOTATION |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would prefer a single guard for all object literal extensions. Perhaps CONFIG_DISABLE_ES2015_OBJ_LITERAL
is enough.
* false - otherwise | ||
*/ | ||
bool | ||
lexer_is_identifier_keyword (parser_context_t *context_p) /**< context */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a similar code for this. It would be good to not duplicate that.
You need to be smart here, invent a way which can be effectively optimized by tail merging or just "reparse" the string or whatever.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My updated patch doesn't yet address this; still thinking on it.
jerry-core/parser/js/js-lexer.c
Outdated
bool not_end_of_literal = (context_p->source_p < context_p->source_end_p | ||
&& context_p->source_p[0] != LIT_CHAR_COLON | ||
&& context_p->source_p[0] != LIT_CHAR_RIGHT_BRACE | ||
&& context_p->source_p[0] != LIT_CHAR_COMMA); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Align these lines after the left bracket.
{ | ||
/* Else, we have something that isn't a valid object literal. */ | ||
parser_raise_error (context_p, PARSER_ERR_COLON_EXPECTED); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't really like this implementation. It is chaotic. You probably need a helper such as lexer_check_arrow, and add a switch.
jerry-core/parser/js/js-lexer.c
Outdated
&& context_p->source_p[0] != LIT_CHAR_COLON); | ||
#endif /* CONFIG_DISABLE_ES2015_SHORTHAND_OBJ_LITERAL_NOTATION */ | ||
|
||
if (not_end_of_literal) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add test for the pre-parser such as:
switch (0) {
default:
var a = { foo, foo() {}, bar:5 }
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should throw a ReferenceError
because foo
isn't defined initially. Is that intended?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The main reason of this test to check, whether the scanner is able to pre-scan the new syntax element. As it seems it can so the thrown Reference Error
is correct.
Thanks @zherczeg. I will come back to this PR with a different approach. |
922111a
to
f08ad99
Compare
33a803e
to
469d361
Compare
@zherczeg I have tried to make the patch less chaotic, please take a look if my design looks reasonable this time. I still can't figure out why I'm failing those test262 tests (refer to my second post)... Any ideas? |
80ae0cb
to
ef2ef79
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Getting closer, only a few remarks.
@@ -610,49 +610,78 @@ parser_parse_object_literal (parser_context_t *context_p) /**< context */ | |||
|
|||
while (true) | |||
{ | |||
lexer_expect_object_literal_id (context_p, LEXER_OBJ_IDENT_NO_OPTS); | |||
lexer_expect_object_literal_id (context_p, LEXER_OBJ_IDENT_OBJ_METHOD); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't we keep the original flag when the short hand literal notation is disabled?
My proposal:
#ifndef CONFIG_DISABLE_ES2015_OBJ_LITERAL
lexer_expect_object_literal_id (context_p, LEXER_OBJ_IDENT_OBJ_METHOD);
#else /* CONFIG_DISABLE_ES2015_OBJ_LITERAL */
lexer_expect_object_literal_id (context_p, LEXER_OBJ_IDENT_NO_OPTS);
#endif /* !CONFIG_DISABLE_ES2015_OBJ_LITERAL */
#else /* !CONFIG_DISABLE_ES2015_OBJ_LITERAL */ | ||
bool token_type_is_method = (context_p->token.type == LEXER_PROPERTY_GETTER | ||
|| context_p->token.type == LEXER_PROPERTY_SETTER); | ||
#endif /* CONFIG_DISABLE_ES2015_OBJ_LITERAL */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about this?
bool token_type_is_method = (context_p->token.type == LEXER_PROPERTY_GETTER
|| context_p->token.type == LEXER_PROPERTY_SETTER);
#ifndef CONFIG_DISABLE_ES2015_OBJ_LITERAL
token_type_is_method = token_type_is_method || context_p->token.type == LEXER_PROPERTY_METHOD);
#endif /* CONFIG_DISABLE_ES2015_OBJ_LITERAL */
&& context_p->token.type != LEXER_COMMA); | ||
#else /* !CONFIG_DISABLE_ES2015_OBJ_LITERAL */ | ||
bool not_end_of_property_name = context_p->token.type != LEXER_COLON; | ||
#endif /* CONFIG_DISABLE_ES2015_OBJ_LITERAL */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comment mismatch ditto.
jerry-core/parser/js/js-lexer.c
Outdated
#else /* !CONFIG_DISABLE_ES2015_OBJ_LITERAL */ | ||
bool not_end_of_literal = (context_p->source_p < context_p->source_end_p | ||
&& context_p->source_p[0] != LIT_CHAR_COLON); | ||
#endif /* CONFIG_DISABLE_ES2015_OBJ_LITERAL */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comment mismatch, it should be:
#ifndef CONFIG_DISABLE_ES2015_OBJ_LITERAL
...
#else /* CONFIG_DISABLE_ES2015_OBJ_LITERAL */
...
#endif /* !CONFIG_DISABLE_ES2015_OBJ_LITERAL */
I would like to help you a bit @AnthonyCalandra, so I started to implement computed properties. This patch contains all around changes, which should simplify your work. The patch has not been finished yet. |
Forgot to add the patch number: it is #2481. |
ef2ef79
to
9366d38
Compare
JerryScript-DCO-1.0-Signed-off-by: Anthony Calandra [email protected]
9366d38
to
40c5751
Compare
@@ -260,7 +260,6 @@ parser_parse_array_literal (parser_context_t *context_p) /**< context */ | |||
} | |||
} /* parser_parse_array_literal */ | |||
|
|||
#ifdef CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@zherczeg I noticed that everything in this macro were needed for my code so I removed the ifdefs here. I'm guessing this change in your computed properties PR was to save binary size?
Rebased and resolved conflicts that arose due to computed properties addition. EDIT: Nevermind, lots of stuff is broken and I don't know why... I might have to abandon this PR -- I just don't have time to work on this anymore and the code is starting to turn into macro soup. |
Since there are two things in this patch (function and keyword) maybe splitting it into two patches could help. When a patch is hard to maintain, I usually just put it away and use it for inspiration and redo the work from scratch. Anyway let us know if you want to work on this, because we also need these features and we have to work on it if you don't have time. Sorry for the inconvenience. Unfortunately the parser is by far the most challenging part of the engine from development perspective. |
This pull request adds support for object literal shorthand notation (properties and methods). In code:
JerryScript-DCO-1.0-Signed-off-by: Anthony Calandra [email protected]