Skip to content

Commit 469d361

Browse files
Support for shorthand object literal notation.
JerryScript-DCO-1.0-Signed-off-by: Anthony Calandra [email protected]
1 parent 29f6ffc commit 469d361

File tree

8 files changed

+298
-34
lines changed

8 files changed

+298
-34
lines changed

jerry-core/config.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,10 @@
3737
*/
3838
#ifdef CONFIG_DISABLE_ES2015
3939
# define CONFIG_DISABLE_ES2015_ARROW_FUNCTION
40-
# define CONFIG_DISABLE_ES2015_CLASS
4140
# define CONFIG_DISABLE_ES2015_BUILTIN
41+
# define CONFIG_DISABLE_ES2015_CLASS
4242
# define CONFIG_DISABLE_ES2015_PROMISE_BUILTIN
43+
# define CONFIG_DISABLE_ES2015_OBJ_LITERAL
4344
# define CONFIG_DISABLE_ES2015_TEMPLATE_STRINGS
4445
# define CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN
4546
#endif /* CONFIG_DISABLE_ES2015 */

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

Lines changed: 71 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,45 @@ static const keyword_string_t * const keyword_string_list[9] =
448448
#undef LEXER_KEYWORD
449449
#undef LEXER_KEYWORD_END
450450

451+
#ifndef CONFIG_DISABLE_ES2015_OBJ_LITERAL
452+
/**
453+
* Checks if current identifier is a keyword or reserved word.
454+
*
455+
* @return true - if the current identifier is a keyword or reserved word
456+
* false - otherwise
457+
*/
458+
bool
459+
lexer_is_identifier_keyword (parser_context_t *context_p) /**< context */
460+
{
461+
lexer_lit_location_t *literal_p = &context_p->token.lit_location;
462+
463+
JERRY_ASSERT (literal_p->type == LEXER_IDENT_LITERAL);
464+
JERRY_ASSERT (literal_p->length <= PARSER_MAXIMUM_IDENT_LENGTH);
465+
466+
if (literal_p->has_escape || literal_p->length < 2 || literal_p->length > 10)
467+
{
468+
return false;
469+
}
470+
471+
const keyword_string_t *keyword_p = keyword_string_list[literal_p->length - 2];
472+
473+
do
474+
{
475+
if (literal_p->char_p[0] == keyword_p->keyword_p[0]
476+
&& literal_p->char_p[1] == keyword_p->keyword_p[1]
477+
&& memcmp (literal_p->char_p, keyword_p->keyword_p, literal_p->length) == 0)
478+
{
479+
return true;
480+
}
481+
482+
keyword_p++;
483+
}
484+
while (keyword_p->type != LEXER_EOS);
485+
486+
return false;
487+
} /* lexer_is_identifier_keyword */
488+
#endif /* !CONFIG_DISABLE_ES2015_OBJ_LITERAL */
489+
451490
/**
452491
* Parse identifier.
453492
*/
@@ -1326,7 +1365,7 @@ lexer_check_colon (parser_context_t *context_p) /**< context */
13261365
&& context_p->source_p[0] == (uint8_t) LIT_CHAR_COLON);
13271366
} /* lexer_check_colon */
13281367

1329-
#ifndef CONFIG_DISABLE_ES2015_CLASS
1368+
#if !defined (CONFIG_DISABLE_ES2015_CLASS) || !defined (CONFIG_DISABLE_ES2015_OBJ_LITERAL)
13301369
/**
13311370
* Checks whether the next token is a left parenthesis.
13321371
*
@@ -1343,7 +1382,7 @@ lexer_check_left_paren (parser_context_t *context_p) /**< context */
13431382
return (context_p->source_p < context_p->source_end_p
13441383
&& context_p->source_p[0] == (uint8_t) LIT_CHAR_LEFT_PAREN);
13451384
} /* lexer_check_left_paren */
1346-
#endif /* !CONFIG_DISABLE_ES2015_CLASS */
1385+
#endif /* !CONFIG_DISABLE_ES2015_CLASS || !CONFIG_DISABLE_ES2015_OBJ_LITERAL */
13471386

13481387
#ifndef CONFIG_DISABLE_ES2015_ARROW_FUNCTION
13491388

@@ -2271,6 +2310,7 @@ lexer_expect_object_literal_id (parser_context_t *context_p, /**< context */
22712310
#ifndef CONFIG_DISABLE_ES2015_CLASS
22722311
int is_class_method = ((ident_opts & LEXER_OBJ_IDENT_CLASS_METHOD)
22732312
&& !(ident_opts & LEXER_OBJ_IDENT_ONLY_IDENTIFIERS)
2313+
&& !(ident_opts & LEXER_OBJ_IDENT_OBJ_METHOD)
22742314
&& (context_p->token.type != LEXER_KEYW_STATIC));
22752315
#endif /* !CONFIG_DISABLE_ES2015_CLASS */
22762316

@@ -2290,8 +2330,19 @@ lexer_expect_object_literal_id (parser_context_t *context_p, /**< context */
22902330
{
22912331
lexer_skip_spaces (context_p);
22922332

2293-
if (context_p->source_p < context_p->source_end_p
2294-
&& context_p->source_p[0] != LIT_CHAR_COLON)
2333+
#ifndef CONFIG_DISABLE_ES2015_OBJ_LITERAL
2334+
bool not_end_of_literal = (context_p->source_p < context_p->source_end_p
2335+
&& context_p->source_p[0] != LIT_CHAR_COLON
2336+
&& context_p->source_p[0] != LIT_CHAR_RIGHT_BRACE
2337+
&& context_p->source_p[0] != LIT_CHAR_COMMA
2338+
/* Shorthand notation allows methods named `get` and allows getters. */
2339+
&& !lexer_check_left_paren (context_p));
2340+
#else /* !CONFIG_DISABLE_ES2015_OBJ_LITERAL */
2341+
bool not_end_of_literal = (context_p->source_p < context_p->source_end_p
2342+
&& context_p->source_p[0] != LIT_CHAR_COLON);
2343+
#endif /* CONFIG_DISABLE_ES2015_OBJ_LITERAL */
2344+
2345+
if (not_end_of_literal)
22952346
{
22962347
if (lexer_compare_raw_identifier_to_current (context_p, "get", 3))
22972348
{
@@ -2330,6 +2381,12 @@ lexer_expect_object_literal_id (parser_context_t *context_p, /**< context */
23302381
context_p->column++;
23312382
return;
23322383
}
2384+
#ifndef CONFIG_DISABLE_ES2015_OBJ_LITERAL
2385+
else if (context_p->source_p[0] == LIT_CHAR_LEFT_PAREN)
2386+
{
2387+
create_literal_object = true;
2388+
}
2389+
#endif /* !CONFIG_DISABLE_ES2015_OBJ_LITERAL */
23332390
else
23342391
{
23352392
const uint8_t *char_p = context_p->source_p;
@@ -2349,6 +2406,16 @@ lexer_expect_object_literal_id (parser_context_t *context_p, /**< context */
23492406
}
23502407
}
23512408

2409+
#ifndef CONFIG_DISABLE_ES2015_OBJ_LITERAL
2410+
if (!(ident_opts & LEXER_OBJ_IDENT_ONLY_IDENTIFIERS)
2411+
&& (ident_opts & LEXER_OBJ_IDENT_OBJ_METHOD)
2412+
&& context_p->source_p[0] == LIT_CHAR_LEFT_PAREN)
2413+
{
2414+
context_p->token.type = LEXER_PROPERTY_METHOD;
2415+
return;
2416+
}
2417+
#endif /* !CONFIG_DISABLE_ES2015_OBJ_LITERAL */
2418+
23522419
#ifndef CONFIG_DISABLE_ES2015_CLASS
23532420
if (is_class_method
23542421
&& lexer_compare_raw_identifier_to_current (context_p, "constructor", 11))

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,9 @@ typedef enum
147147
LEXER_EXPRESSION_START, /**< expression start */
148148
LEXER_PROPERTY_GETTER, /**< property getter function */
149149
LEXER_PROPERTY_SETTER, /**< property setter function */
150+
#ifndef CONFIG_DISABLE_ES2015_OBJ_LITERAL
151+
LEXER_PROPERTY_METHOD, /**< property method (ES6+) */
152+
#endif /* !CONFIG_DISABLE_ES2015_OBJ_LITERAL */
150153
LEXER_COMMA_SEP_LIST, /**< comma separated bracketed expression list */
151154
LEXER_SCAN_SWITCH, /**< special value for switch pre-scan */
152155
LEXER_CLASS_CONSTRUCTOR, /**< special value for class constructor method */
@@ -216,6 +219,7 @@ typedef enum
216219
LEXER_OBJ_IDENT_NO_OPTS = (1u << 0), /**< no options */
217220
LEXER_OBJ_IDENT_ONLY_IDENTIFIERS = (1u << 1), /**< only identifiers are accepted */
218221
LEXER_OBJ_IDENT_CLASS_METHOD = (1u << 2), /**< expect identifier inside a class body */
222+
LEXER_OBJ_IDENT_OBJ_METHOD = (1u << 3), /**< expect method identifier inside object */
219223
} lexer_obj_ident_opts_t;
220224

221225
/**

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

Lines changed: 66 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -610,49 +610,78 @@ parser_parse_object_literal (parser_context_t *context_p) /**< context */
610610

611611
while (true)
612612
{
613-
lexer_expect_object_literal_id (context_p, LEXER_OBJ_IDENT_NO_OPTS);
613+
lexer_expect_object_literal_id (context_p, LEXER_OBJ_IDENT_OBJ_METHOD);
614614

615615
if (context_p->token.type == LEXER_RIGHT_BRACE)
616616
{
617617
break;
618618
}
619619

620-
if (context_p->token.type == LEXER_PROPERTY_GETTER
621-
|| context_p->token.type == LEXER_PROPERTY_SETTER)
620+
#ifndef CONFIG_DISABLE_ES2015_OBJ_LITERAL
621+
bool token_type_is_method = (context_p->token.type == LEXER_PROPERTY_GETTER
622+
|| context_p->token.type == LEXER_PROPERTY_SETTER
623+
|| context_p->token.type == LEXER_PROPERTY_METHOD);
624+
#else /* !CONFIG_DISABLE_ES2015_OBJ_LITERAL */
625+
bool token_type_is_method = (context_p->token.type == LEXER_PROPERTY_GETTER
626+
|| context_p->token.type == LEXER_PROPERTY_SETTER);
627+
#endif /* CONFIG_DISABLE_ES2015_OBJ_LITERAL */
628+
629+
if (token_type_is_method)
622630
{
623631
uint32_t status_flags;
624-
cbc_ext_opcode_t opcode;
632+
uint16_t opcode;
625633
uint16_t literal_index, function_literal_index;
626634
parser_object_literal_item_types_t item_type;
635+
lexer_token_type_t token_type = context_p->token.type;
627636

628-
if (context_p->token.type == LEXER_PROPERTY_GETTER)
637+
if (token_type == LEXER_PROPERTY_GETTER)
629638
{
630639
status_flags = PARSER_IS_FUNCTION | PARSER_IS_CLOSURE | PARSER_IS_PROPERTY_GETTER;
631-
opcode = CBC_EXT_SET_GETTER;
640+
opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_SET_GETTER);
632641
item_type = PARSER_OBJECT_PROPERTY_GETTER;
633642
}
634-
else
643+
#ifndef CONFIG_DISABLE_ES2015_OBJ_LITERAL
644+
else if (token_type == LEXER_PROPERTY_METHOD)
645+
{
646+
status_flags = PARSER_IS_FUNCTION | PARSER_IS_CLOSURE | PARSER_IS_FUNC_EXPRESSION;
647+
opcode = CBC_SET_LITERAL_PROPERTY;
648+
item_type = PARSER_OBJECT_PROPERTY_VALUE;
649+
}
650+
#endif /* !CONFIG_DISABLE_ES2015_OBJ_LITERAL */
651+
else /* token_type == LEXER_PROPERTY_SETTER */
635652
{
636653
status_flags = PARSER_IS_FUNCTION | PARSER_IS_CLOSURE | PARSER_IS_PROPERTY_SETTER;
637-
opcode = CBC_EXT_SET_SETTER;
654+
opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_SET_SETTER);
638655
item_type = PARSER_OBJECT_PROPERTY_SETTER;
639656
}
640657

641-
lexer_expect_object_literal_id (context_p, LEXER_OBJ_IDENT_ONLY_IDENTIFIERS);
658+
lexer_expect_object_literal_id (context_p, LEXER_OBJ_IDENT_ONLY_IDENTIFIERS | LEXER_OBJ_IDENT_OBJ_METHOD);
642659
literal_index = context_p->lit_object.index;
643660

644661
parser_append_object_literal_item (context_p, literal_index, item_type);
645662

646663
parser_flush_cbc (context_p);
647664
function_literal_index = lexer_construct_function_object (context_p, status_flags);
648665

666+
uint16_t push_literal_index = literal_index;
667+
uint16_t literal_property_index = function_literal_index;
668+
669+
#ifndef CONFIG_DISABLE_ES2015_OBJ_LITERAL
670+
/* Property methods aren't extended opcodes, so swap the values here. */
671+
if (token_type == LEXER_PROPERTY_METHOD)
672+
{
673+
push_literal_index = function_literal_index;
674+
literal_property_index = literal_index;
675+
}
676+
#endif /* !CONFIG_DISABLE_ES2015_OBJ_LITERAL */
677+
649678
parser_emit_cbc_literal (context_p,
650679
CBC_PUSH_LITERAL,
651-
literal_index);
680+
push_literal_index);
652681

653682
JERRY_ASSERT (context_p->last_cbc_opcode == CBC_PUSH_LITERAL);
654-
context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (opcode);
655-
context_p->last_cbc.value = function_literal_index;
683+
context_p->last_cbc_opcode = opcode;
684+
context_p->last_cbc.value = literal_property_index;
656685

657686
lexer_next_token (context_p);
658687
}
@@ -665,13 +694,34 @@ parser_parse_object_literal (parser_context_t *context_p) /**< context */
665694
PARSER_OBJECT_PROPERTY_VALUE);
666695

667696
lexer_next_token (context_p);
668-
if (context_p->token.type != LEXER_COLON)
697+
698+
if (context_p->token.type == LEXER_COLON)
669699
{
670-
parser_raise_error (context_p, PARSER_ERR_COLON_EXPECTED);
700+
lexer_next_token (context_p);
701+
parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA);
671702
}
703+
#ifndef CONFIG_DISABLE_ES2015_OBJ_LITERAL
704+
else if (context_p->token.type == LEXER_COMMA
705+
|| context_p->token.type == LEXER_RIGHT_BRACE)
706+
{
707+
// TODO somehow clean this up -- is it possible to parse and check for keywords by this step?
708+
// TODO this currently allows objects like: {1}. WRONG
709+
if (lexer_is_identifier_keyword (context_p))
710+
{
711+
parser_raise_error (context_p, PARSER_ERR_COLON_EXPECTED);
712+
}
672713

673-
lexer_next_token (context_p);
674-
parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA);
714+
lexer_construct_literal_object (context_p,
715+
&context_p->token.lit_location,
716+
context_p->token.lit_location.type);
717+
parser_emit_cbc_literal_from_token (context_p, CBC_PUSH_LITERAL);
718+
JERRY_ASSERT (context_p->last_cbc_opcode == CBC_PUSH_LITERAL);
719+
}
720+
#endif /* !CONFIG_DISABLE_ES2015_OBJ_LITERAL */
721+
else
722+
{
723+
parser_raise_error (context_p, PARSER_ERR_COLON_EXPECTED);
724+
}
675725

676726
if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL)
677727
{

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -433,10 +433,15 @@ void parser_set_continues_to_current_position (parser_context_t *context_p, pars
433433

434434
/* Lexer functions */
435435

436+
#ifndef CONFIG_DISABLE_ES2015_OBJ_LITERAL
437+
bool lexer_is_identifier_keyword (parser_context_t *context_p);
438+
#endif /* !CONFIG_DISABLE_ES2015_OBJ_LITERAL */
436439
void lexer_next_token (parser_context_t *context_p);
437440
bool lexer_check_colon (parser_context_t *context_p);
438-
#ifndef CONFIG_DISABLE_ES2015_CLASS
441+
#if !defined (CONFIG_DISABLE_ES2015_CLASS) || !defined (CONFIG_DISABLE_ES2015_OBJ_LITERAL)
439442
bool lexer_check_left_paren (parser_context_t *context_p);
443+
#endif /* !CONFIG_DISABLE_ES2015_CLASS || !CONFIG_DISABLE_ES2015_OBJ_LITERAL */
444+
#ifndef CONFIG_DISABLE_ES2015_CLASS
440445
void lexer_skip_empty_statements (parser_context_t *context_p);
441446
#endif /* !CONFIG_DISABLE_ES2015_CLASS */
442447
#ifndef CONFIG_DISABLE_ES2015_ARROW_FUNCTION

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

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -762,16 +762,16 @@ parser_scan_until (parser_context_t *context_p, /**< context */
762762
}
763763
case SCAN_MODE_FUNCTION_ARGUMENTS:
764764
{
765+
const bool correct_stack_top = (stack_top == SCAN_STACK_BLOCK_STATEMENT
766+
|| stack_top == SCAN_STACK_BLOCK_EXPRESSION
765767
#ifndef CONFIG_DISABLE_ES2015_CLASS
766-
JERRY_ASSERT (stack_top == SCAN_STACK_BLOCK_STATEMENT
767-
|| stack_top == SCAN_STACK_BLOCK_EXPRESSION
768-
|| stack_top == SCAN_STACK_CLASS
769-
|| stack_top == SCAN_STACK_BLOCK_PROPERTY);
770-
#else /* CONFIG_DISABLE_ES2015_CLASS */
771-
JERRY_ASSERT (stack_top == SCAN_STACK_BLOCK_STATEMENT
772-
|| stack_top == SCAN_STACK_BLOCK_EXPRESSION
773-
|| stack_top == SCAN_STACK_BLOCK_PROPERTY);
774-
#endif /* !CONFIG_DISABLE_ES2015_CLASS */
768+
|| stack_top == SCAN_STACK_CLASS
769+
#endif /* CONFIG_DISABLE_ES2015_CLASS */
770+
#ifndef CONFIG_DISABLE_ES2015_OBJ_LITERALS
771+
|| stack_top == SCAN_STACK_OBJECT_LITERAL
772+
#endif /* CONFIG_DISABLE_ES2015_OBJ_LITERALS */
773+
|| stack_top == SCAN_STACK_BLOCK_PROPERTY);
774+
JERRY_ASSERT (correct_stack_top);
775775

776776
if (context_p->token.type == LEXER_LITERAL
777777
&& (context_p->token.lit_location.type == LEXER_IDENT_LITERAL
@@ -833,15 +833,25 @@ parser_scan_until (parser_context_t *context_p, /**< context */
833833
}
834834

835835
if (context_p->token.type == LEXER_PROPERTY_GETTER
836-
|| context_p->token.type == LEXER_PROPERTY_SETTER)
836+
|| context_p->token.type == LEXER_PROPERTY_SETTER
837+
|| context_p->token.type == LEXER_PROPERTY_METHOD)
837838
{
838839
parser_stack_push_uint8 (context_p, SCAN_STACK_BLOCK_PROPERTY);
839840
mode = SCAN_MODE_FUNCTION_ARGUMENTS;
840841
break;
841842
}
842843

843844
lexer_next_token (context_p);
844-
if (context_p->token.type != LEXER_COLON)
845+
846+
#ifndef CONFIG_DISABLE_ES2015_OBJ_LITERAL
847+
bool not_end_of_property_name = (context_p->token.type != LEXER_COLON
848+
&& context_p->token.type != LEXER_RIGHT_BRACE
849+
&& context_p->token.type != LEXER_COMMA);
850+
#else /* !CONFIG_DISABLE_ES2015_OBJ_LITERAL */
851+
bool not_end_of_property_name = context_p->token.type != LEXER_COLON;
852+
#endif /* CONFIG_DISABLE_ES2015_OBJ_LITERAL */
853+
854+
if (not_end_of_property_name)
845855
{
846856
parser_raise_error (context_p, PARSER_ERR_COLON_EXPECTED);
847857
}

jerry-core/profiles/README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,5 +93,7 @@ In JerryScript all of the features are enabled by default, so an empty profile f
9393
Disable the [template strings](http://www.ecma-international.org/ecma-262/6.0/#sec-static-semantics-templatestrings).
9494
* `CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN`:
9595
Disable the [ArrayBuffer](http://www.ecma-international.org/ecma-262/6.0/#sec-arraybuffer-objects) and [TypedArray](http://www.ecma-international.org/ecma-262/6.0/#sec-typedarray-objects) built-ins.
96+
* `CONFIG_DISABLE_ES2015_OBJ_LITERAL`:
97+
Disable the [object literal shorthand notation](https://www.ecma-international.org/ecma-262/6.0/#sec-object-initializer).
9698
* `CONFIG_DISABLE_ES2015`: Disable all of the implemented [ECMAScript2015 features](http://www.ecma-international.org/ecma-262/6.0/).
97-
(equivalent to `CONFIG_DISABLE_ES2015_ARROW_FUNCTION`, `CONFIG_DISABLE_ES2015_BUILTIN`, `CONFIG_DISABLE_ES2015_CLASS`, `CONFIG_DISABLE_ES2015_PROMISE_BUILTIN`, `CONFIG_DISABLE_ES2015_TEMPLATE_STRINGS`, and `CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN`).
99+
(equivalent to `CONFIG_DISABLE_ES2015_ARROW_FUNCTION`, `CONFIG_DISABLE_ES2015_BUILTIN`, `CONFIG_DISABLE_ES2015_CLASS`, `CONFIG_DISABLE_ES2015_PROMISE_BUILTIN`, `CONFIG_DISABLE_ES2015_TEMPLATE_STRINGS`, `CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN`, and `CONFIG_DISABLE_ES2015_OBJ_LITERAL`).

0 commit comments

Comments
 (0)