diff --git a/jerry-core/ecma/operations/ecma-get-put-value.c b/jerry-core/ecma/operations/ecma-get-put-value.c index 9a341039d..64d252c54 100644 --- a/jerry-core/ecma/operations/ecma-get-put-value.c +++ b/jerry-core/ecma/operations/ecma-get-put-value.c @@ -257,6 +257,12 @@ ecma_op_put_value_lex_env_base (ecma_object_t *lex_env_p, /**< lexical environme ecma_named_data_property_assign_value (lex_env_p, property_value_p, value); } +#if ENABLED (JERRY_ES2015) + else if (ecma_is_property_enumerable (*property_p)) + { + return ecma_raise_type_error (ECMA_ERR_MSG ("Constant bindings cannot be reassigned.")); + } +#endif /* ENABLED (JERRY_ES2015) */ else if (is_strict) { return ecma_raise_type_error (ECMA_ERR_MSG ("Binding cannot be set.")); diff --git a/jerry-core/ecma/operations/ecma-lex-env.c b/jerry-core/ecma/operations/ecma-lex-env.c index 4e314e10e..c0dc6efce 100644 --- a/jerry-core/ecma/operations/ecma-lex-env.c +++ b/jerry-core/ecma/operations/ecma-lex-env.c @@ -239,6 +239,12 @@ ecma_op_set_mutable_binding (ecma_object_t *lex_env_p, /**< lexical environment { ecma_named_data_property_assign_value (lex_env_p, ECMA_PROPERTY_VALUE_PTR (property_p), value); } +#if ENABLED (JERRY_ES2015) + else if (ecma_is_property_enumerable (*property_p)) + { + return ecma_raise_type_error (ECMA_ERR_MSG ("Constant bindings cannot be reassigned.")); + } +#endif /* ENABLED (JERRY_ES2015) */ else if (is_strict) { return ecma_raise_type_error (ECMA_ERR_MSG ("Binding cannot be set.")); diff --git a/jerry-core/include/jerryscript-snapshot.h b/jerry-core/include/jerryscript-snapshot.h index a26cf9e1a..bc32ed8b0 100644 --- a/jerry-core/include/jerryscript-snapshot.h +++ b/jerry-core/include/jerryscript-snapshot.h @@ -30,7 +30,7 @@ extern "C" /** * Jerry snapshot format version. */ -#define JERRY_SNAPSHOT_VERSION (41u) +#define JERRY_SNAPSHOT_VERSION (42u) /** * Flags for jerry_generate_snapshot and jerry_generate_function_snapshot. diff --git a/jerry-core/parser/js/byte-code.h b/jerry-core/parser/js/byte-code.h index 4c922e188..2a7c1311b 100644 --- a/jerry-core/parser/js/byte-code.h +++ b/jerry-core/parser/js/byte-code.h @@ -578,6 +578,8 @@ VM_OC_ERROR) \ CBC_OPCODE (CBC_EXT_THROW_REFERENCE_ERROR, CBC_NO_FLAG, 1, \ VM_OC_THROW_REFERENCE_ERROR) \ + CBC_OPCODE (CBC_EXT_THROW_ASSIGN_CONST_ERROR, CBC_NO_FLAG, 0, \ + VM_OC_THROW_CONST_ERROR) \ CBC_OPCODE (CBC_EXT_REQUIRE_OBJECT_COERCIBLE, CBC_NO_FLAG, 0, \ VM_OC_REQUIRE_OBJECT_COERCIBLE) \ \ diff --git a/jerry-core/parser/js/js-lexer.h b/jerry-core/parser/js/js-lexer.h index 50a3afccb..8fd4af206 100644 --- a/jerry-core/parser/js/js-lexer.h +++ b/jerry-core/parser/js/js-lexer.h @@ -173,10 +173,10 @@ typedef enum LEXER_PROPERTY_GETTER, /**< property getter function */ LEXER_PROPERTY_SETTER, /**< property setter function */ LEXER_COMMA_SEP_LIST, /**< comma separated bracketed expression list */ - LEXER_SCAN_SWITCH, /**< special value for switch pre-scan */ +#if ENABLED (JERRY_ES2015) + LEXER_ASSIGN_CONST, /**< a const binding is reassigned */ LEXER_CLASS_CONSTRUCTOR, /**< special value for class constructor method */ LEXER_INVALID_PATTERN, /**< special value for invalid destructuring pattern */ -#if ENABLED (JERRY_ES2015) LEXER_ARROW_LEFT_PAREN, /**< start of arrow function argument list */ #endif /* ENABLED (JERRY_ES2015) */ diff --git a/jerry-core/parser/js/js-parser-expr.c b/jerry-core/parser/js/js-parser-expr.c index 48b7b919c..fbf242b85 100644 --- a/jerry-core/parser/js/js-parser-expr.c +++ b/jerry-core/parser/js/js-parser-expr.c @@ -133,6 +133,7 @@ parser_check_invalid_assign (parser_context_t *context_p) /**< context */ } /* parser_check_invalid_assign */ #if ENABLED (JERRY_ES2015) + /** * Check and throw an error if the "new.target" is invalid as a left-hand side expression. */ @@ -153,6 +154,7 @@ parser_check_invalid_new_target (parser_context_t *context_p, /**< parser contex parser_raise_error (context_p, PARSER_ERR_NEW_TARGET_NOT_ALLOWED); } } /* parser_check_invalid_new_target */ + #endif /* ENABLED (JERRY_ES2015) */ /** @@ -220,6 +222,16 @@ parser_emit_unary_lvalue_opcode (parser_context_t *context_p, /**< context */ } parser_emit_ident_reference (context_p, unary_opcode); + +#if ENABLED (JERRY_ES2015) + if (unary_opcode != CBC_DELETE_IDENT_PUSH_RESULT + && scanner_literal_is_const_reg (context_p, context_p->last_cbc.literal_index)) + { + /* The current value must be read, but it cannot be changed. */ + context_p->last_cbc_opcode = CBC_PUSH_LITERAL; + parser_emit_cbc_ext (context_p, CBC_EXT_THROW_ASSIGN_CONST_ERROR); + } +#endif /* ENABLED (JERRY_ES2015) */ return; } @@ -2228,10 +2240,9 @@ parser_process_unary_expression (parser_context_t *context_p, /**< context */ */ static uint8_t parser_append_binary_single_assignment_token (parser_context_t *context_p, /**< context */ - uint8_t assign_ident_opcode) /**< assign ident opcode */ + bool is_lexical) /**< assign lexical declaration */ { - JERRY_ASSERT (assign_ident_opcode == CBC_ASSIGN_SET_IDENT - || assign_ident_opcode == CBC_ASSIGN_LET_CONST); + JERRY_UNUSED (is_lexical); /* Unlike other tokens, the whole byte code is saved for binary * assignment, since it has multiple forms depending on the @@ -2242,8 +2253,6 @@ parser_append_binary_single_assignment_token (parser_context_t *context_p, /**< if (PARSER_IS_PUSH_LITERALS_WITH_THIS (context_p->last_cbc_opcode) && context_p->last_cbc.literal_type == LEXER_IDENT_LITERAL) { - JERRY_ASSERT (CBC_SAME_ARGS (CBC_PUSH_LITERAL, assign_ident_opcode)); - parser_check_invalid_assign (context_p); uint16_t literal_index; @@ -2278,8 +2287,24 @@ parser_append_binary_single_assignment_token (parser_context_t *context_p, /**< } } + assign_opcode = CBC_ASSIGN_SET_IDENT; + +#if ENABLED (JERRY_ES2015) + if (!is_lexical) + { + if (scanner_literal_is_const_reg (context_p, literal_index)) + { + parser_stack_push_uint8 (context_p, LEXER_ASSIGN_CONST); + } + } + else if (literal_index < PARSER_REGISTER_START) + { + assign_opcode = CBC_ASSIGN_LET_CONST; + } +#endif /* ENABLED (JERRY_ES2015) */ + parser_stack_push_uint16 (context_p, literal_index); - assign_opcode = assign_ident_opcode; + JERRY_ASSERT (CBC_SAME_ARGS (CBC_PUSH_LITERAL, assign_opcode)); } else if (context_p->last_cbc_opcode == CBC_PUSH_PROP) { @@ -2359,7 +2384,7 @@ parser_append_binary_token (parser_context_t *context_p) /**< context */ if (context_p->token.type == LEXER_ASSIGN) { - parser_append_binary_single_assignment_token (context_p, CBC_ASSIGN_SET_IDENT); + parser_append_binary_single_assignment_token (context_p, false); return; } @@ -2371,6 +2396,13 @@ parser_append_binary_token (parser_context_t *context_p) /**< context */ parser_check_invalid_assign (context_p); parser_emit_ident_reference (context_p, CBC_PUSH_IDENT_REFERENCE); + +#if ENABLED (JERRY_ES2015) + if (scanner_literal_is_const_reg (context_p, context_p->last_cbc.literal_index)) + { + parser_stack_push_uint8 (context_p, LEXER_ASSIGN_CONST); + } +#endif /* ENABLED (JERRY_ES2015) */ } else if (PARSER_IS_PUSH_PROP (context_p->last_cbc_opcode)) { @@ -2437,20 +2469,39 @@ parser_process_binary_opcodes (parser_context_t *context_p, /**< context */ opcode = (cbc_opcode_t) context_p->stack_top_uint8; parser_stack_pop_uint8 (context_p); - if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL - && opcode == CBC_ASSIGN_SET_IDENT) - { - JERRY_ASSERT (CBC_ARGS_EQ (CBC_ASSIGN_LITERAL_SET_IDENT, - CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2)); - context_p->last_cbc.value = parser_stack_pop_uint16 (context_p); - context_p->last_cbc_opcode = CBC_ASSIGN_LITERAL_SET_IDENT; - continue; - } - + int32_t index = -1; if (cbc_flags[opcode] & CBC_HAS_LITERAL_ARG) { - uint16_t index = parser_stack_pop_uint16 (context_p); - parser_emit_cbc_literal (context_p, (uint16_t) opcode, index); + JERRY_ASSERT (opcode == CBC_ASSIGN_SET_IDENT + || opcode == CBC_ASSIGN_PROP_LITERAL + || opcode == CBC_ASSIGN_PROP_THIS_LITERAL + || opcode == CBC_ASSIGN_LET_CONST); + + index = parser_stack_pop_uint16 (context_p); + } + +#if ENABLED (JERRY_ES2015) + if (JERRY_UNLIKELY (context_p->stack_top_uint8 == LEXER_ASSIGN_CONST)) + { + parser_stack_pop_uint8 (context_p); + parser_emit_cbc_ext (context_p, CBC_EXT_THROW_ASSIGN_CONST_ERROR); + } +#endif /* ENABLED (JERRY_ES2015) */ + + if (index >= 0) + { + if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL + && opcode == CBC_ASSIGN_SET_IDENT) + { + JERRY_ASSERT (CBC_ARGS_EQ (CBC_ASSIGN_LITERAL_SET_IDENT, + CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2)); + + context_p->last_cbc.value = (uint16_t) index; + context_p->last_cbc_opcode = CBC_ASSIGN_LITERAL_SET_IDENT; + continue; + } + + parser_emit_cbc_literal (context_p, (uint16_t) opcode, (uint16_t) index); if (opcode == CBC_ASSIGN_PROP_THIS_LITERAL && (context_p->stack_depth >= context_p->stack_limit)) @@ -2644,16 +2695,9 @@ parser_pattern_form_assignment (parser_context_t *context_p, /**< context */ { JERRY_UNUSED (ident_line_counter); - uint8_t assign_opcode = CBC_ASSIGN_SET_IDENT; - - if (flags & (PARSER_PATTERN_LEXICAL | PARSER_PATTERN_LOCAL) - && context_p->lit_object.index < PARSER_REGISTER_START) - { - assign_opcode = CBC_ASSIGN_LET_CONST; - } - parser_stack_push_uint8 (context_p, LEXER_EXPRESSION_START); - assign_opcode = parser_append_binary_single_assignment_token (context_p, assign_opcode); + bool is_lexical = (flags & (PARSER_PATTERN_LEXICAL | PARSER_PATTERN_LOCAL)) != 0; + uint8_t assign_opcode = parser_append_binary_single_assignment_token (context_p, is_lexical); if (flags & PARSER_PATTERN_ARRAY) { diff --git a/jerry-core/parser/js/js-parser-internal.h b/jerry-core/parser/js/js-parser-internal.h index 9d3cdb61d..d86875e0c 100644 --- a/jerry-core/parser/js/js-parser-internal.h +++ b/jerry-core/parser/js/js-parser-internal.h @@ -750,6 +750,9 @@ void scanner_create_variables (parser_context_t *context_p, uint32_t option_flag void scanner_get_location (scanner_location_t *location_p, parser_context_t *context_p); void scanner_set_location (parser_context_t *context_p, scanner_location_t *location_p); uint16_t scanner_decode_map_to (parser_scope_stack_t *stack_item_p); +#if ENABLED (JERRY_ES2015) +bool scanner_literal_is_const_reg (parser_context_t *context_p, uint16_t literal_index); +#endif /* ENABLED (JERRY_ES2015) */ void scanner_scan_all (parser_context_t *context_p, const uint8_t *arg_list_p, const uint8_t *arg_list_end_p, const uint8_t *source_p, const uint8_t *source_end_p); diff --git a/jerry-core/parser/js/js-parser-statm.c b/jerry-core/parser/js/js-parser-statm.c index 3fc20b443..18e0f0805 100644 --- a/jerry-core/parser/js/js-parser-statm.c +++ b/jerry-core/parser/js/js-parser-statm.c @@ -558,24 +558,22 @@ parser_parse_var_statement (parser_context_t *context_p) /**< context */ } #endif /* ENABLED (JERRY_LINE_INFO) */ + uint16_t index = context_p->lit_object.index; + + lexer_next_token (context_p); + parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA); + + cbc_opcode_t opcode = CBC_ASSIGN_SET_IDENT; + #if ENABLED (JERRY_ES2015) if (declaration_type != LEXER_KEYW_VAR - && context_p->lit_object.index < PARSER_REGISTER_START) + && (index < PARSER_REGISTER_START)) { - uint16_t index = context_p->lit_object.index; + opcode = CBC_ASSIGN_LET_CONST; + } +#endif /* ENABLED (JERRY_ES2015) */ - lexer_next_token (context_p); - parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA); - parser_emit_cbc_literal (context_p, CBC_ASSIGN_LET_CONST, index); - } - else - { -#endif /* ENABLED (JERRY_ES2015) */ - parser_emit_cbc_literal_from_token (context_p, CBC_PUSH_LITERAL); - parser_parse_expression_statement (context_p, PARSE_EXPR_NO_COMMA | PARSE_EXPR_HAS_LITERAL); -#if ENABLED (JERRY_ES2015) - } -#endif /* ENABLED (JERRY_ES2015) */ + parser_emit_cbc_literal (context_p, opcode, index); } #if ENABLED (JERRY_ES2015) else if (declaration_type == LEXER_KEYW_LET) diff --git a/jerry-core/parser/js/js-parser.c b/jerry-core/parser/js/js-parser.c index 6651b65e9..982f40355 100644 --- a/jerry-core/parser/js/js-parser.c +++ b/jerry-core/parser/js/js-parser.c @@ -2473,11 +2473,9 @@ parser_parse_arrow_function (parser_context_t *context_p, /**< context */ JERRY_ASSERT (status_flags & PARSER_IS_ARROW_FUNCTION); parser_save_context (context_p, &saved_context); context_p->status_flags |= status_flags; -#if ENABLED (JERRY_ES2015) context_p->status_flags |= saved_context.status_flags & (PARSER_ALLOW_NEW_TARGET | PARSER_ALLOW_SUPER | PARSER_ALLOW_SUPER_CALL); -#endif /* ENABLED (JERRY_ES2015) */ #if ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) if (context_p->is_show_opcodes) @@ -2529,14 +2527,12 @@ parser_parse_arrow_function (parser_context_t *context_p, /**< context */ parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA); -#if ENABLED (JERRY_ES2015) if (context_p->status_flags & PARSER_IS_ASYNC_FUNCTION) { parser_emit_cbc_ext (context_p, CBC_EXT_RETURN_PROMISE); } else { -#endif /* ENABLED (JERRY_ES2015) */ if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) { context_p->last_cbc_opcode = CBC_RETURN_WITH_LITERAL; @@ -2545,9 +2541,7 @@ parser_parse_arrow_function (parser_context_t *context_p, /**< context */ { parser_emit_cbc (context_p, CBC_RETURN); } -#if ENABLED (JERRY_ES2015) } -#endif /* ENABLED (JERRY_ES2015) */ parser_flush_cbc (context_p); } diff --git a/jerry-core/parser/js/js-scanner-util.c b/jerry-core/parser/js/js-scanner-util.c index d8cc36e0d..9b2e96c14 100644 --- a/jerry-core/parser/js/js-scanner-util.c +++ b/jerry-core/parser/js/js-scanner-util.c @@ -2291,6 +2291,40 @@ scanner_decode_map_to (parser_scope_stack_t *stack_item_p) /**< scope stack item #endif /* ENABLED (JERRY_ES2015) */ } /* scanner_decode_map_to */ +#if ENABLED (JERRY_ES2015) + +/** + * Checks whether the literal is a const in the current scope. + * + * @return true if the literal is a const + */ +bool +scanner_literal_is_const_reg (parser_context_t *context_p, /**< context */ + uint16_t literal_index) /**< literal index */ +{ + if (literal_index < PARSER_REGISTER_START) + { + /* Re-assignment of non-register const bindings are detected elsewhere. */ + return false; + } + + parser_scope_stack_t *scope_stack_p = context_p->scope_stack_p + context_p->scope_stack_top; + + literal_index = (uint16_t) (literal_index - (PARSER_REGISTER_START - 1)); + + do + { + /* Registers must be found in the scope stack. */ + JERRY_ASSERT (scope_stack_p > context_p->scope_stack_p); + scope_stack_p--; + } + while (literal_index != (scope_stack_p->map_to & PARSER_SCOPE_STACK_REGISTER_MASK)); + + return (scope_stack_p->map_to & PARSER_SCOPE_STACK_IS_CONST) != 0; +} /* scanner_literal_is_const_reg */ + +#endif /* ENABLED (JERRY_ES2015) */ + /** * @} * @} diff --git a/jerry-core/vm/vm.c b/jerry-core/vm/vm.c index bd37eeec0..86ef37877 100644 --- a/jerry-core/vm/vm.c +++ b/jerry-core/vm/vm.c @@ -1506,6 +1506,11 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ } continue; } + case VM_OC_THROW_CONST_ERROR: + { + result = ecma_raise_type_error (ECMA_ERR_MSG ("Constant bindings cannot be reassigned.")); + goto error; + } case VM_OC_COPY_TO_GLOBAL: { uint32_t literal_index; diff --git a/jerry-core/vm/vm.h b/jerry-core/vm/vm.h index 30c4c1db6..22d794aee 100644 --- a/jerry-core/vm/vm.h +++ b/jerry-core/vm/vm.h @@ -235,6 +235,7 @@ typedef enum VM_OC_CHECK_VAR, /**< check redeclared vars in the global scope */ VM_OC_CHECK_LET, /**< check redeclared lets in the global scope */ VM_OC_ASSIGN_LET_CONST, /**< assign values to let/const declarations */ + VM_OC_THROW_CONST_ERROR, /**< throw invalid assignment to const variable error */ VM_OC_COPY_TO_GLOBAL, /**< copy value to global lex env */ VM_OC_CLONE_CONTEXT, /**< clone lexical environment with let/const declarations */ VM_OC_SET_COMPUTED_PROPERTY, /**< set computed property */ @@ -296,6 +297,7 @@ typedef enum VM_OC_CHECK_VAR = VM_OC_NONE, /**< check redeclared vars in the global scope */ VM_OC_CHECK_LET = VM_OC_NONE, /**< check redeclared lets in the global scope */ VM_OC_ASSIGN_LET_CONST = VM_OC_NONE, /**< assign values to let/const declarations */ + VM_OC_THROW_CONST_ERROR = VM_OC_NONE, /**< throw invalid assignment to const variable error */ VM_OC_COPY_TO_GLOBAL = VM_OC_NONE, /**< copy value to global lex env */ VM_OC_CLONE_CONTEXT = VM_OC_NONE, /**< clone lexical environment with let/const declarations */ VM_OC_SET_COMPUTED_PROPERTY = VM_OC_NONE, /**< set computed property is unused */ diff --git a/tests/jerry/es2015/const1.js b/tests/jerry/es2015/const1.js new file mode 100644 index 000000000..6ba7322b7 --- /dev/null +++ b/tests/jerry/es2015/const1.js @@ -0,0 +1,74 @@ +/* Copyright JS Foundation and other contributors, http://js.foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const a = 6 +assert(!delete a) +assert(a === 6) + +try { + eval("a = 7") + assert(false) +} catch (e) { + assert(e instanceof TypeError) +} + +function check_type_error(code) { + try { + eval(code) + assert(false) + } catch (e) { + assert(e instanceof TypeError) + } +} + +// Register cases +check_type_error("{ const a = 0; a = 1 }"); +check_type_error("{ const a = 0; a += 1 }"); +check_type_error("{ const a = 0; a -= 1 }"); +check_type_error("{ const a = 0; a *= 1 }"); +check_type_error("{ const a = 0; a %= 1 }"); +check_type_error("{ const a = 0; a /= 1 }"); +check_type_error("{ const a = 0; a <<= 1 }"); +check_type_error("{ const a = 0; a >>= 1 }"); +check_type_error("{ const a = 0; a >>>= 1 }"); +check_type_error("{ const a = 0; a &= 1 }"); +check_type_error("{ const a = 0; a |= 1 }"); +check_type_error("{ const a = 0; a ^= 1 }"); +check_type_error("{ const a = 0; a++ }"); +check_type_error("{ const a = 0; a-- }"); +check_type_error("{ const a = 0; ++a }"); +check_type_error("{ const a = 0; --a }"); +check_type_error("{ const a = 0; [a] = [1] }"); +check_type_error("{ const a = 0; ({a} = { a:1 }) }"); + +// Non-register cases +check_type_error("{ const a = 0; (function (){ a = 1 })() }"); +check_type_error("{ const a = 0; (function (){ a += 1 })() }"); +check_type_error("{ const a = 0; (function (){ a -= 1 })() }"); +check_type_error("{ const a = 0; (function (){ a *= 1 })() }"); +check_type_error("{ const a = 0; (function (){ a %= 1 })() }"); +check_type_error("{ const a = 0; (function (){ a /= 1 })() }"); +check_type_error("{ const a = 0; (function (){ a <<= 1 })() }"); +check_type_error("{ const a = 0; (function (){ a >>= 1 })() }"); +check_type_error("{ const a = 0; (function (){ a >>>= 1 })() }"); +check_type_error("{ const a = 0; (function (){ a &= 1 })() }"); +check_type_error("{ const a = 0; (function (){ a |= 1 })() }"); +check_type_error("{ const a = 0; (function (){ a ^= 1 })() }"); +check_type_error("{ const a = 0; (function (){ a++ })() }"); +check_type_error("{ const a = 0; (function (){ a-- })() }"); +check_type_error("{ const a = 0; (function (){ ++a })() }"); +check_type_error("{ const a = 0; (function (){ --a })() }"); +check_type_error("{ const a = 0; (function (){ [a] = [1] })() }"); +check_type_error("{ const a = 0; (function (){ ({a} = { a:1 }) })() }"); diff --git a/tests/unit-core/test-snapshot.c b/tests/unit-core/test-snapshot.c index fe674b565..96817f97b 100644 --- a/tests/unit-core/test-snapshot.c +++ b/tests/unit-core/test-snapshot.c @@ -223,7 +223,7 @@ main (void) /* Check the snapshot data. Unused bytes should be filled with zeroes */ const uint8_t expected_data[] = { - 0x4A, 0x52, 0x52, 0x59, 0x29, 0x00, 0x00, 0x00, + 0x4A, 0x52, 0x52, 0x59, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0x41, 0x00, 0x01, 0x00,