Skip to content

Commit f08ad99

Browse files
Support for shorthand object literal notation.
JerryScript-DCO-1.0-Signed-off-by: Anthony Calandra [email protected]
1 parent 76ff084 commit f08ad99

File tree

8 files changed

+219
-22
lines changed

8 files changed

+219
-22
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: 66 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

@@ -2290,8 +2329,17 @@ lexer_expect_object_literal_id (parser_context_t *context_p, /**< context */
22902329
{
22912330
lexer_skip_spaces (context_p);
22922331

2293-
if (context_p->source_p < context_p->source_end_p
2294-
&& context_p->source_p[0] != LIT_CHAR_COLON)
2332+
#ifndef CONFIG_DISABLE_ES2015_OBJ_LITERAL
2333+
bool not_end_of_literal = (context_p->source_p < context_p->source_end_p
2334+
&& context_p->source_p[0] != LIT_CHAR_COLON
2335+
&& context_p->source_p[0] != LIT_CHAR_RIGHT_BRACE
2336+
&& context_p->source_p[0] != LIT_CHAR_COMMA);
2337+
#else /* !CONFIG_DISABLE_ES2015_OBJ_LITERAL */
2338+
bool not_end_of_literal = (context_p->source_p < context_p->source_end_p
2339+
&& context_p->source_p[0] != LIT_CHAR_COLON);
2340+
#endif /* CONFIG_DISABLE_ES2015_OBJ_LITERAL */
2341+
2342+
if (not_end_of_literal)
22952343
{
22962344
if (lexer_compare_raw_identifier_to_current (context_p, "get", 3))
22972345
{
@@ -2330,6 +2378,12 @@ lexer_expect_object_literal_id (parser_context_t *context_p, /**< context */
23302378
context_p->column++;
23312379
return;
23322380
}
2381+
#ifndef CONFIG_DISABLE_ES2015_OBJ_LITERAL
2382+
else if (context_p->source_p[0] == LIT_CHAR_LEFT_PAREN)
2383+
{
2384+
create_literal_object = true;
2385+
}
2386+
#endif /* !CONFIG_DISABLE_ES2015_OBJ_LITERAL */
23332387
else
23342388
{
23352389
const uint8_t *char_p = context_p->source_p;
@@ -2358,6 +2412,14 @@ lexer_expect_object_literal_id (parser_context_t *context_p, /**< context */
23582412
}
23592413
#endif /* !CONFIG_DISABLE_ES2015_CLASS */
23602414

2415+
#ifndef CONFIG_DISABLE_ES2015_OBJ_LITERAL
2416+
if (!must_be_identifier && context_p->source_p[0] == LIT_CHAR_LEFT_PAREN)
2417+
{
2418+
context_p->token.type = LEXER_PROPERTY_METHOD;
2419+
return;
2420+
}
2421+
#endif /* !CONFIG_DISABLE_ES2015_OBJ_LITERAL */
2422+
23612423
if (create_literal_object)
23622424
{
23632425
lexer_construct_literal_object (context_p,

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

Lines changed: 3 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 */

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

Lines changed: 61 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -619,24 +619,41 @@ parser_parse_object_literal (parser_context_t *context_p) /**< context */
619619
break;
620620
}
621621

622-
if (context_p->token.type == LEXER_PROPERTY_GETTER
623-
|| context_p->token.type == LEXER_PROPERTY_SETTER)
622+
#ifndef CONFIG_DISABLE_ES2015_OBJ_LITERAL
623+
bool token_type_is_method = (context_p->token.type == LEXER_PROPERTY_GETTER
624+
|| context_p->token.type == LEXER_PROPERTY_SETTER
625+
|| context_p->token.type == LEXER_PROPERTY_METHOD);
626+
#else /* !CONFIG_DISABLE_ES2015_OBJ_LITERAL */
627+
bool token_type_is_method = (context_p->token.type == LEXER_PROPERTY_GETTER
628+
|| context_p->token.type == LEXER_PROPERTY_SETTER);
629+
#endif /* CONFIG_DISABLE_ES2015_OBJ_LITERAL */
630+
631+
if (token_type_is_method)
624632
{
625633
uint32_t status_flags;
626-
cbc_ext_opcode_t opcode;
634+
uint16_t opcode;
627635
uint16_t literal_index, function_literal_index;
628636
parser_object_literal_item_types_t item_type;
637+
lexer_token_type_t token_type = context_p->token.type;
629638

630-
if (context_p->token.type == LEXER_PROPERTY_GETTER)
639+
if (token_type == LEXER_PROPERTY_GETTER)
631640
{
632641
status_flags = PARSER_IS_FUNCTION | PARSER_IS_CLOSURE | PARSER_IS_PROPERTY_GETTER;
633-
opcode = CBC_EXT_SET_GETTER;
642+
opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_SET_GETTER);
634643
item_type = PARSER_OBJECT_PROPERTY_GETTER;
635644
}
636-
else
645+
#ifndef CONFIG_DISABLE_ES2015_OBJ_LITERAL
646+
else if (token_type == LEXER_PROPERTY_METHOD)
647+
{
648+
status_flags = PARSER_IS_FUNCTION | PARSER_IS_CLOSURE | PARSER_IS_FUNC_EXPRESSION;
649+
opcode = CBC_SET_LITERAL_PROPERTY;
650+
item_type = PARSER_OBJECT_PROPERTY_VALUE;
651+
}
652+
#endif /* !CONFIG_DISABLE_ES2015_OBJ_LITERAL */
653+
else /* token_type == LEXER_PROPERTY_SETTER */
637654
{
638655
status_flags = PARSER_IS_FUNCTION | PARSER_IS_CLOSURE | PARSER_IS_PROPERTY_SETTER;
639-
opcode = CBC_EXT_SET_SETTER;
656+
opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_SET_SETTER);
640657
item_type = PARSER_OBJECT_PROPERTY_SETTER;
641658
}
642659

@@ -648,13 +665,23 @@ parser_parse_object_literal (parser_context_t *context_p) /**< context */
648665
parser_flush_cbc (context_p);
649666
function_literal_index = lexer_construct_function_object (context_p, status_flags);
650667

668+
uint16_t push_literal_index = literal_index;
669+
uint16_t literal_property_index = function_literal_index;
670+
#ifndef CONFIG_DISABLE_ES2015_OBJ_LITERAL
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+
651678
parser_emit_cbc_literal (context_p,
652679
CBC_PUSH_LITERAL,
653-
literal_index);
680+
push_literal_index);
654681

655682
JERRY_ASSERT (context_p->last_cbc_opcode == CBC_PUSH_LITERAL);
656-
context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (opcode);
657-
context_p->last_cbc.value = function_literal_index;
683+
context_p->last_cbc_opcode = opcode;
684+
context_p->last_cbc.value = literal_property_index;
658685

659686
lexer_next_token (context_p);
660687
}
@@ -667,13 +694,33 @@ parser_parse_object_literal (parser_context_t *context_p) /**< context */
667694
PARSER_OBJECT_PROPERTY_VALUE);
668695

669696
lexer_next_token (context_p);
670-
if (context_p->token.type != LEXER_COLON)
697+
698+
if (context_p->token.type == LEXER_COLON)
671699
{
672-
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);
673702
}
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+
if (lexer_is_identifier_keyword (context_p))
709+
{
710+
parser_raise_error (context_p, PARSER_ERR_COLON_EXPECTED);
711+
}
674712

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

678725
if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL)
679726
{

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

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

435435
/* Lexer functions */
436436

437+
#ifndef CONFIG_DISABLE_ES2015_OBJ_LITERAL
438+
bool lexer_is_identifier_keyword (parser_context_t *context_p);
439+
#endif /* !CONFIG_DISABLE_ES2015_OBJ_LITERAL */
437440
void lexer_next_token (parser_context_t *context_p);
438441
bool lexer_check_colon (parser_context_t *context_p);
439-
#ifndef CONFIG_DISABLE_ES2015_CLASS
442+
#if !defined (CONFIG_DISABLE_ES2015_CLASS) || !defined (CONFIG_DISABLE_ES2015_OBJ_LITERAL)
440443
bool lexer_check_left_paren (parser_context_t *context_p);
444+
#endif /* !CONFIG_DISABLE_ES2015_CLASS || !CONFIG_DISABLE_ES2015_OBJ_LITERAL */
445+
#ifndef CONFIG_DISABLE_ES2015_CLASS
441446
void lexer_skip_empty_statements (parser_context_t *context_p);
442447
#endif /* !CONFIG_DISABLE_ES2015_CLASS */
443448
#ifndef CONFIG_DISABLE_ES2015_ARROW_FUNCTION

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

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -841,7 +841,16 @@ parser_scan_until (parser_context_t *context_p, /**< context */
841841
}
842842

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

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`).
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// Copyright JS Foundation and other contributors, http://js.foundation
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
var x = 1, y = 2, z = 3;
16+
var obj = { x, y, z: { z } };
17+
assert(obj.x === 1);
18+
assert(obj.y === 2);
19+
assert(obj.z.z === 3);
20+
21+
try {
22+
var a = { get, set };
23+
assert(false);
24+
} catch (e) {
25+
assert(e instanceof ReferenceError);
26+
}
27+
28+
function checkReservedAsVars() {
29+
var get = 1, set = 2;
30+
var a = { get, set };
31+
assert(a.get + a.set === 3);
32+
}
33+
checkReservedAsVars();
34+
35+
var one = 1;
36+
assert({ one, one }.one === 1);
37+
assert({ one: 0, one }.one === 1);
38+
assert({ one, one: 0 }.one === 0);
39+
40+
var obj2 = { one };
41+
assert({ obj2 }.obj2.one === 1);
42+
43+
try {
44+
eval('({ true, false, null })');
45+
assert(false);
46+
} catch (e) {
47+
assert(e instanceof SyntaxError);
48+
}
49+
50+
var obj3 = { f() { return 1; } };
51+
assert(obj3.f() === 1);
52+
53+
var obj4 = { one, one() { return one; } };
54+
assert(typeof obj4.one === 'function');
55+
assert(obj4.one() === 1);
56+
57+
var obj5 = { x: 123, getX() { return this.x; } };
58+
assert(obj5.getX() === 123);
59+
60+
var obj6 = {
61+
if() { return 0; }, else() { return 1; }, try() { return 2; }, catch() { return 3; },
62+
finally() { return 4; }, let() { return 5; }, true() { return 6; }, false() { return 7; },
63+
null() { return 8; }
64+
};
65+
assert(
66+
obj6.if() + obj6.else() + obj6.try() + obj6.catch() + obj6.finally() + obj6.let() +
67+
obj6.true() + obj6.false() + obj6.null() === 36
68+
);

0 commit comments

Comments
 (0)