Skip to content
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

Improve location info for syntax errors. #4816

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 89 additions & 2 deletions docs/02.API-REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -1462,6 +1462,30 @@ typedef struct
- [jerry_source_info_enabled_fields_t](#jerry_source_info_enabled_fields_t)
- [jerry_get_source_info](#jerry_get_source_info)

## jerry_syntax_error_location_t

**Summary**

Detailed location info for SyntaxErrors. It contains the
start and end location of the token which caused the SyntaxError.

**Prototype**

```c
typedef struct
{
uint32_t line; /**< start line of the invalid token */
uint32_t column_start; /**< start column of the invalid token */
uint32_t column_end; /**< end column of the invalid token */
} jerry_syntax_error_location_t;
```

*New in version [[NEXT_RELEASE]]*.

**See also**

- [jerry_get_syntax_error_location](#jerry_get_syntax_error_location)

## jerry_arraybuffer_type_t

**Summary**
Expand Down Expand Up @@ -12236,8 +12260,8 @@ Returns a newly created source info structure corresponding to the passed script
The function is lower level than `toString()` operation, but provides more contextual information.

*Notes*:
- Returned value must be freed with [jerry_free_source_info](#jerry_free_source_info) when it
is no longer needed.
- Returned value must be freed with [jerry_free_source_info](#jerry_free_source_info)
when it is no longer needed.
- This API depends on a build option (`JERRY_FUNCTION_TO_STRING`) and can be checked
in runtime with the `JERRY_FEATURE_FUNCTION_TO_STRING` feature enum value,
see: [jerry_is_feature_enabled](#jerry_is_feature_enabled).
Expand Down Expand Up @@ -12320,6 +12344,69 @@ See [jerry_get_source_info](#jerry_get_source_info)
- [jerry_get_source_info](#jerry_get_source_info)
- [jerry_source_info_t](#jerry_source_info_t)

## jerry_get_syntax_error_location

**Summary**

Gets the resource name and location info assigned to a SyntaxError object generated by the parser.

*Notes*:
- Returned value must be freed with [jerry_release_value](#jerry_release_value)
when it is no longer needed.
- This API depends on a build option (`JERRY_ERROR_MESSAGES`) and can be checked
in runtime with the `JERRY_FEATURE_ERROR_MESSAGES` feature enum value,
see: [jerry_is_feature_enabled](#jerry_is_feature_enabled).

**Prototype**

```c
jerry_value_t jerry_get_syntax_error_location (jerry_value_t value,
jerry_syntax_error_location_t *error_location_p);
```
- `value` - SyntaxError object
- `error_location_p` - output location info
- return
- resource name - if the `value` object has a location info data
- error - otherwise

*New in version [[NEXT_RELEASE]]*.

**Example**

[doctest]: # ()

```c
#include "jerryscript.h"

int
main (void)
{
jerry_init (JERRY_INIT_EMPTY);

const jerry_char_t script[] = "aa bb";

jerry_value_t result_value = jerry_parse (script, sizeof (script) - 1, NULL);

jerry_syntax_error_location_t error_location;
jerry_value_t resource_value = jerry_get_syntax_error_location (result_value, &error_location);

if (jerry_value_is_string (resource_value))
{
/* Prints the location of the error. */
}

jerry_release_value (resource_value);
jerry_release_value (result_value);

jerry_cleanup ();
return 0;
}
```

**See also**

- [jerry_syntax_error_location_t](#jerry_syntax_error_location_t)


# Functions for realm objects

Expand Down
58 changes: 58 additions & 0 deletions jerry-core/api/jerryscript.c
Original file line number Diff line number Diff line change
Expand Up @@ -5645,6 +5645,64 @@ jerry_free_source_info (jerry_source_info_t *source_info_p) /**< source info blo
#endif /* JERRY_FUNCTION_TO_STRING */
} /* jerry_free_source_info */

/**
* Gets the resource name and location info assigned to a SyntaxError object generated by the parser.
*
* @return resource name, if a location info is available
* error, otherwise
*/
jerry_value_t
jerry_get_syntax_error_location (jerry_value_t value, /**< SyntaxError object */
Copy link
Member

Choose a reason for hiding this comment

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

How about not making this syntax error specific, and changing the name to be generic for errors. Functionally it already seems to be generic. Also in the future we could possibly include location info for other error types as well.

Same for the jerry_syntax_error_location_t type, I would just call it jerry_error_location_t.

jerry_syntax_error_location_t *error_location_p) /**< [out] location info */
{
jerry_assert_api_available ();

#if JERRY_ERROR_MESSAGES
if (ecma_is_value_error_reference (value))
{
value = ecma_get_extended_primitive_from_value (value)->u.value;
}

if (ecma_is_value_object (value))
{
ecma_object_t *object_p = ecma_get_object_from_value (value);

ecma_string_t *name_p = ecma_get_internal_string (LIT_INTERNAL_MAGIC_STRING_SYNTAX_ERROR_LOCATION);
ecma_property_t *property_p = ecma_find_named_property (object_p, name_p);

if (property_p != NULL)
{
ecma_value_t error_property_value = ECMA_PROPERTY_VALUE_PTR (property_p)->value;
uint8_t *location_p = ECMA_GET_NON_NULL_POINTER_FROM_POINTER_TAG (uint8_t, error_property_value);
ecma_value_t result = *(ecma_value_t *) location_p;

if (error_location_p != NULL)
{
size_t size_data = error_property_value & ECMA_SYNTAX_ERROR_ALLOCATION_SIZE_MASK;
location_p += ((size_data + 1) << ECMA_SYNTAX_ERROR_ALLOCATION_UNIT_SHIFT);

error_location_p->line = ecma_extended_info_decode_vlq (&location_p);
error_location_p->column_start = ecma_extended_info_decode_vlq (&location_p);

uint32_t difference = ecma_extended_info_decode_vlq (&location_p);

error_location_p->column_end = error_location_p->column_start + difference;
}

ecma_ref_ecma_string (ecma_get_string_from_value (result));
return result;
}
}

return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG ("Location is not available")));
Copy link
Member

Choose a reason for hiding this comment

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

I'm wondering wether it would be better to simply return with undefined if there is no location, or with a pre-defined magic string. And similarly when error messages are disabled.

It feels to me like the location info is an added bonus, it can be used when it's available, but if it's not, then that's not really a problem. Or at least not something that would warrant an error.

#else /* !JERRY_ERROR_MESSAGES */
JERRY_UNUSED (value);
JERRY_UNUSED (error_location_p);

return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG ("Location is not available")));
#endif /* JERRY_ERROR_MESSAGES */
} /* jerry_get_syntax_error_location */

/**
* Replaces the currently active realm with another realm.
*
Expand Down
6 changes: 5 additions & 1 deletion jerry-core/ecma/base/ecma-extended-info.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

#include "byte-code.h"

#if JERRY_ESNEXT || JERRY_FUNCTION_TO_STRING
#if JERRY_ESNEXT || JERRY_ERROR_MESSAGES || JERRY_FUNCTION_TO_STRING

/** \addtogroup ecma ECMA
* @{
Expand Down Expand Up @@ -105,6 +105,10 @@ ecma_extended_info_get_encoded_length (uint32_t value) /**< encoded value */
return length;
} /* ecma_extended_info_get_encoded_length */

#endif /* JERRY_ESNEXT || JERRY_ERROR_MESSAGES || JERRY_FUNCTION_TO_STRING */

#if JERRY_ESNEXT || JERRY_FUNCTION_TO_STRING

/**
* Get the extended info from a byte code
*
Expand Down
6 changes: 5 additions & 1 deletion jerry-core/ecma/base/ecma-extended-info.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

#include "ecma-globals.h"

#if JERRY_ESNEXT || JERRY_FUNCTION_TO_STRING
#if JERRY_ESNEXT || JERRY_ERROR_MESSAGES || JERRY_FUNCTION_TO_STRING

/**
* Vlq encoding: flag which is set for all bytes except the last one.
Expand All @@ -46,6 +46,10 @@ uint32_t ecma_extended_info_decode_vlq (uint8_t **buffer_p);
void ecma_extended_info_encode_vlq (uint8_t **buffer_p, uint32_t value);
uint32_t ecma_extended_info_get_encoded_length (uint32_t value);

#endif /* JERRY_ESNEXT || JERRY_ERROR_MESSAGES || JERRY_FUNCTION_TO_STRING */

#if JERRY_ESNEXT || JERRY_FUNCTION_TO_STRING

uint8_t *ecma_compiled_code_resolve_extended_info (const ecma_compiled_code_t *bytecode_header_p);

#endif /* JERRY_ESNEXT || JERRY_FUNCTION_TO_STRING */
Expand Down
12 changes: 12 additions & 0 deletions jerry-core/ecma/base/ecma-gc.c
Original file line number Diff line number Diff line change
Expand Up @@ -1618,6 +1618,18 @@ ecma_gc_free_property (ecma_object_t *object_p, /**< object */
break;
}
#endif /* JERRY_BUILTIN_CONTAINER */
#if JERRY_ERROR_MESSAGES
case LIT_INTERNAL_MAGIC_STRING_SYNTAX_ERROR_LOCATION:
{
uint8_t *location_p = ECMA_GET_NON_NULL_POINTER_FROM_POINTER_TAG (uint8_t, value);

ecma_deref_ecma_string (ecma_get_string_from_value (*(ecma_value_t *) location_p));

size_t size_data = value & ECMA_SYNTAX_ERROR_ALLOCATION_SIZE_MASK;
jmem_heap_free_block (location_p, (size_data + 1) << ECMA_SYNTAX_ERROR_ALLOCATION_UNIT_SHIFT);
break;
}
#endif /* JERRY_ERROR_MESSAGES */
default:
{
JERRY_ASSERT (name_cp == LIT_INTERNAL_MAGIC_STRING_NATIVE_POINTER
Expand Down
14 changes: 14 additions & 0 deletions jerry-core/ecma/base/ecma-globals.h
Original file line number Diff line number Diff line change
Expand Up @@ -1920,6 +1920,20 @@ typedef struct
ecma_stringbuilder_header_t *header_p; /**< pointer to header */
} ecma_stringbuilder_t;

#if JERRY_ERROR_MESSAGES

/**
* Allocation block size shift for SyntaxError line info data.
*/
#define ECMA_SYNTAX_ERROR_ALLOCATION_UNIT_SHIFT 3

/**
* Mask for extracting allocation size.
*/
#define ECMA_SYNTAX_ERROR_ALLOCATION_SIZE_MASK 0x3

#endif /* JERRY_ERROR_MESSAGES */

#ifndef JERRY_BUILTIN_BIGINT
/**
* BigInt type.
Expand Down
1 change: 1 addition & 0 deletions jerry-core/include/jerryscript-core.h
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,7 @@ jerry_value_t jerry_get_user_value (const jerry_value_t value);
bool jerry_is_eval_code (const jerry_value_t value);
jerry_source_info_t *jerry_get_source_info (const jerry_value_t value);
void jerry_free_source_info (jerry_source_info_t *source_info_p);
jerry_value_t jerry_get_syntax_error_location (jerry_value_t value, jerry_syntax_error_location_t *error_location_p);

/**
* Array buffer components.
Expand Down
10 changes: 10 additions & 0 deletions jerry-core/include/jerryscript-types.h
Original file line number Diff line number Diff line change
Expand Up @@ -817,6 +817,16 @@ typedef struct
uint32_t source_range_length; /**< source length of the function in the source code */
} jerry_source_info_t;

/**
* Detailed location info for SyntaxErrors.
*/
typedef struct
{
uint32_t line; /**< start line of the invalid token */
uint32_t column_start; /**< start column of the invalid token */
uint32_t column_end; /**< end column of the invalid token */
} jerry_syntax_error_location_t;

/**
* Array buffer types.
*/
Expand Down
12 changes: 6 additions & 6 deletions jerry-core/jmem/jmem.h
Original file line number Diff line number Diff line change
Expand Up @@ -262,12 +262,12 @@ void *JERRY_ATTR_PURE jmem_decompress_pointer (uintptr_t compressed_pointer);
* Set value of pointer-tag value so that it will correspond
* to specified non_compressed_pointer along with tag
*/
#define JMEM_CP_SET_NON_NULL_POINTER_TAG(cp_value, pointer, tag) \
do \
{ \
JERRY_ASSERT ((uintptr_t) tag < (uintptr_t) (JMEM_ALIGNMENT)); \
jmem_cpointer_tag_t compressed_ptr = jmem_compress_pointer (pointer); \
(cp_value) = (jmem_cpointer_tag_t) ((compressed_ptr << JMEM_TAG_SHIFT) | tag); \
#define JMEM_CP_SET_NON_NULL_POINTER_TAG(cp_value, pointer, tag) \
do \
{ \
JERRY_ASSERT ((uintptr_t) tag < (uintptr_t) (JMEM_ALIGNMENT)); \
jmem_cpointer_tag_t compressed_ptr = jmem_compress_pointer (pointer); \
(cp_value) = (jmem_cpointer_tag_t) ((compressed_ptr << JMEM_TAG_SHIFT) | (tag)); \
} while (false);

/**
Expand Down
1 change: 1 addition & 0 deletions jerry-core/lit/lit-magic-strings.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ typedef enum
* data properties */
LIT_INTERNAL_MAGIC_STRING_NATIVE_POINTER_WITH_REFERENCES, /**< native pointer info associated with an object
* which contains references to other values */
LIT_INTERNAL_MAGIC_STRING_SYNTAX_ERROR_LOCATION, /**< location info for syntax error */
LIT_INTERNAL_MAGIC_STRING_ENVIRONMENT_RECORD, /**< dynamic environment record needed by class constructors */
LIT_INTERNAL_MAGIC_STRING_CLASS_FIELD_COMPUTED, /**< computed class field name list */
LIT_INTERNAL_MAGIC_STRING_CONTAINER_WEAK_REFS, /**< Weak references to the current container object */
Expand Down
Loading