From d588fe77224ec1525e6dee1ea45134921787663e Mon Sep 17 00:00:00 2001 From: Yonggang Luo Date: Tue, 10 Dec 2024 09:00:05 +0000 Subject: [PATCH] Implement jerry_port_path_normalize in a more reliable way Replace jerry_port_path_normalize,jerry_port_path_free,jerry_port_path_base with jerry_port_path_style,jerry_port_get_cwd Partially fixes https://github.com/jerryscript-project/jerryscript/issues/4979 Closes: https://github.com/jerryscript-project/jerryscript/issues/4983 JerryScript-DCO-1.0-Signed-off-by: Yonggang Luo luoyonggang@gmail.com --- docs/05.PORT-API.md | 59 +- jerry-core/api/jerry-module.c | 651 +++++++++++++++++++- jerry-core/include/jerryscript-port.h | 56 +- jerry-core/include/jerryscript-types.h | 18 +- jerry-ext/include/jerryscript-ext/sources.h | 8 +- jerry-ext/util/sources.c | 22 +- jerry-main/main-desktop.c | 9 +- jerry-port/common/jerry-port-fs.c | 52 +- jerry-port/unix/jerry-port-unix-fs.c | 57 +- jerry-port/win/jerry-port-win-fs.c | 54 +- 10 files changed, 821 insertions(+), 165 deletions(-) diff --git a/docs/05.PORT-API.md b/docs/05.PORT-API.md index e84b0b4816..746cd9ccb9 100644 --- a/docs/05.PORT-API.md +++ b/docs/05.PORT-API.md @@ -26,6 +26,16 @@ void jerry_port_init (void); void jerry_port_fatal (jerry_fatal_code_t code); ``` +The path style of the OS + +```c +typedef enum +{ + JERRY_STYLE_WINDOWS, + JERRY_STYLE_UNIX, +} jerry_path_style_t; +``` + Error codes ```c @@ -172,52 +182,33 @@ void jerry_port_line_free (jerry_char_t *buffer_p); ## Filesystem -``` -/** - * Canonicalize a file path. - * - * If possible, the implementation should resolve symbolic links and other directory references found in the input path, - * and create a fully canonicalized file path as the result. - * - * The function may return with NULL in case an error is encountered, in which case the calling operation will not - * proceed. - * - * The implementation should allocate storage for the result path as necessary. Non-NULL return values will be passed - * to `jerry_port_path_free` when the result is no longer needed by the caller, which can be used to finalize - * dynamically allocated buffers. - * - * NOTE: The implementation must not return directly with the input, as the input buffer is released after the call. - * - * @param path_p: zero-terminated string containing the input path - * @param path_size: size of the input path string in bytes, excluding terminating zero - * - * @return buffer with the normalized path if the operation is successful, - * NULL otherwise - */ -jerry_char_t *jerry_port_path_normalize (const jerry_char_t *path_p, jerry_size_t path_size); -``` - ```c /** - * Free a path buffer returned by jerry_port_path_normalize. + * Get the path style of the current OS * - * @param path_p: the path buffer to free + * @return path style */ -void jerry_port_path_free (jerry_char_t *path_p); +jerry_path_style_t jerry_port_path_style (void); ``` ```c /** - * Get the offset of the basename component in the input path. + * Get the cwd, the output string will be zero-terminated * - * The implementation should return the offset of the first character after the last path separator found in the path. - * This is used by the caller to split the path into a directory name and a file name. + * @param buffer_p: the buffer to storage the cwd + * @param buffer_size: the `buffer_p` buffer size, including '\0` terminator * - * @param path_p: input zero-terminated path string + * @note + * - cwd: current working directory * - * @return offset of the basename component in the input path + * @return The length of cwd, excluding '\0' terminator + * - When buffer_p is `NULL` and get cwd succeed return length of cwd + * - When buffer_p is `NULL` and get cwd failed return 0 + * - When buffer_p is not `NULL` and the `buffer_size - 1` just equal to + * length of cwd; and get cwd succeed return `buffer_size - 1`. + * - Otherwise means get cwd failed and return 0 */ -jerry_size_t jerry_port_path_base (const jerry_char_t *path_p); +jerry_size_t jerry_port_get_cwd (jerry_char_t *buffer_p, jerry_size_t buffer_size); ``` ```c diff --git a/jerry-core/api/jerry-module.c b/jerry-core/api/jerry-module.c index 0e5f6eae02..872303b2c0 100644 --- a/jerry-core/api/jerry-module.c +++ b/jerry-core/api/jerry-module.c @@ -30,6 +30,7 @@ typedef struct jerry_module_t { struct jerry_module_t *next_p; /**< next_module */ jerry_char_t *path_p; /**< path to the module */ + jerry_size_t path_size; /**< size of path to the module, excluding '\0' terminator */ jerry_size_t basename_offset; /**< offset of the basename in the module path*/ jerry_value_t realm; /**< the realm of the module */ jerry_value_t module; /**< the module itself */ @@ -50,6 +51,628 @@ typedef struct jerry_module_t *module_head_p; /**< first module */ } jerry_module_manager_t; +typedef struct +{ + const jerry_string_t *path_list_p; + jerry_size_t path_list_count; + jerry_size_t root_length; + bool root_is_absolute; + + bool end_with_separator; + jerry_size_t list_pos; + jerry_size_t pos; + jerry_size_t length; + jerry_size_t segment_eat_count; + jerry_size_t segment_count; +} jerry_segment_iterator_t; + +static bool +jerry_path_is_separator (jerry_path_style_t style, const jerry_char_t ch) +{ + if (style == JERRY_STYLE_WINDOWS) + { + return ch == '/' || ch == '\\'; + } + else + { + return ch == '/'; + } +} /* jerry_path_is_separator */ + +static jerry_size_t +jerry_path_get_root_windows (const jerry_char_t *path) +{ + const jerry_char_t *c; + jerry_size_t length = 0; + // We can not determine the root if this is an empty string. So we set the + // root to NULL and the length to zero and cancel the whole thing. + c = path; + if (!*c) + { + return length; + } + + // Now we have to verify whether this is a windows network path (UNC), which + // we will consider our root. + if (jerry_path_is_separator (JERRY_STYLE_WINDOWS, *c)) + { + bool is_device_path; + ++c; + + // Check whether the path starts with a single backslash, which means this + // is not a network path - just a normal path starting with a backslash. + if (!jerry_path_is_separator (JERRY_STYLE_WINDOWS, *c)) + { + // Okay, this is not a network path but we still use the backslash as a + // root. + ++length; + return length; + } + + // A device path is a path which starts with "\\." or "\\?". A device path + // can be a UNC path as well, in which case it will take up one more + // segment. So, this is a network or device path. Skip the previous + // separator. Now we need to determine whether this is a device path. We + // might advance one character here if the server name starts with a '?' or + // a '.', but that's fine since we will search for a separator afterwards + // anyway. + ++c; + is_device_path = (*c == '?' || *c == '.') && jerry_path_is_separator (JERRY_STYLE_WINDOWS, *(++c)); + if (is_device_path) + { + // That's a device path, and the root must be either "\\.\" or "\\?\" + // which is 4 characters long. (at least that's how Windows + // GetFullPathName behaves.) + length = 4; + return length; + } + + // We will grab anything up to the next stop. The next stop might be a '\0' + // or another separator. That will be the server name. + while (*c != '\0' && !jerry_path_is_separator (JERRY_STYLE_WINDOWS, *c)) + { + ++c; + } + + // If this is a separator and not the end of a string we wil have to include + // it. However, if this is a '\0' we must not skip it. + while (jerry_path_is_separator (JERRY_STYLE_WINDOWS, *c)) + { + ++c; + } + + // We are now skipping the shared folder name, which will end after the + // next stop. + while (*c != '\0' && !jerry_path_is_separator (JERRY_STYLE_WINDOWS, *c)) + { + ++c; + } + // Then there might be a separator at the end. We will include that as well, + // it will mark the path as absolute. + if (jerry_path_is_separator (JERRY_STYLE_WINDOWS, *c)) + { + ++c; + } + + // Finally, calculate the size of the root. + length = (jerry_size_t) (c - path); + return length; + } + + // Move to the next and check whether this is a colon. + if (*++c == ':') + { + length = 2; + + // Now check whether this is a backslash (or slash). If it is not, we could + // assume that the next character is a '\0' if it is a valid path. However, + // we will not assume that - since ':' is not valid in a path it must be a + // mistake by the caller than. We will try to understand it anyway. + if (jerry_path_is_separator (JERRY_STYLE_WINDOWS, *(++c))) + { + length = 3; + } + } + return length; +} /* jerry_path_get_root_windows */ + +static jerry_size_t +jerry_path_get_root_unix (const jerry_char_t *path) +{ + // The slash of the unix path represents the root. There is no root if there + // is no slash. + return jerry_path_is_separator (JERRY_STYLE_UNIX, *path) ? 1 : 0; +} /* jerry_path_get_root_unix */ + +static jerry_size_t +jerry_path_get_root (jerry_path_style_t path_style, const jerry_char_t *path) +{ + if (!path) + { + return 0; + } + // We use a different implementation here based on the configuration of the + // library. + return path_style == JERRY_STYLE_WINDOWS ? jerry_path_get_root_windows (path) : jerry_path_get_root_unix (path); +} /* jerry_path_get_root */ + +static bool +jerry_path_iterator_before_root (jerry_segment_iterator_t *it) +{ + return it->list_pos == 0 && ((it->pos + 1) <= it->root_length); +} /* jerry_path_iterator_before_root */ + +static jerry_char_t +jerry_path_char_get (jerry_segment_iterator_t *it) +{ + if (jerry_path_iterator_before_root (it)) + { + return 0; + } + if (it->pos == JERRY_SIZE_MAX) + { + /** + * The tail '\0' treat as '/' that is a path separator for both + * POSIX/WINDOWS + */ + return '/'; + } + return it->path_list_p[it->list_pos].ptr[it->pos]; +} /* jerry_path_char_get */ + +static void +jerry_path_char_iter_prev (jerry_segment_iterator_t *it) +{ + if (jerry_path_iterator_before_root (it)) + { + return; + } + if (it->pos == JERRY_SIZE_MAX) + { + it->list_pos -= 1; + it->pos = it->path_list_p[it->list_pos].size - 1; + } + else + { + --it->pos; + } +} /* jerry_path_char_iter_prev */ + +static void +jerry_path_get_prev_segment_detail (jerry_path_style_t path_style, jerry_segment_iterator_t *it) +{ + if (it->segment_eat_count > 0) + { + --it->segment_eat_count; + if (it->segment_eat_count > 0) + { + return; + } + } + if (it->list_pos == 0 && it->pos == JERRY_SIZE_MAX && it->length == it->root_length) + { + /* To the head */ + it->length = 0; + return; + } + for (;;) + { + jerry_char_t ch; + jerry_size_t segment_length = 0; + for (;;) + { + ch = jerry_path_char_get (it); + if (jerry_path_is_separator (path_style, ch)) + { + jerry_path_char_iter_prev (it); + continue; + } + if (ch != 0) + { + segment_length += 1; + } + break; + } + for (;;) + { + jerry_path_char_iter_prev (it); + ch = jerry_path_char_get (it); + if (ch == 0 || jerry_path_is_separator (path_style, ch)) + { + break; + } + segment_length += 1; + } + const jerry_string_t *path_current = it->path_list_p + it->list_pos; + if (segment_length == 1) + { + if (path_current->ptr[it->pos + 1] == '.') + { + continue; + } + } + else if (segment_length == 2) + { + if (path_current->ptr[it->pos + 1] == '.' || path_current->ptr[it->pos + 2] == '.') + { + it->segment_eat_count += 1; + continue; + } + } + if (it->segment_eat_count > 0 && segment_length > 0) + { + it->segment_eat_count -= 1; + continue; + } + if (segment_length > 0) + { + /* segment_eat_count must be zero, this is a normal segment */ + it->length = segment_length; + return; + } + it->length = 0; + if (ch == 0 && it->root_is_absolute) + { + /* Dropping segment eat when the root segment is absolute */ + it->segment_eat_count = 0; + } + if (it->segment_eat_count > 0) + { + /* Genearting .. segment for relative path */ + return; + } + + if (ch == 0) + { + if (it->segment_count == 0 && !it->root_is_absolute) + { + /* Path like `C:` `C:abc\..` `` `abc\..` `.` should place a . as the + * path component */ + return; + } + + /* Return the root segment or head depends on `root_length` */ + it->pos = JERRY_SIZE_MAX; + it->length = it->root_length; + } + return; + } /* for (;;) */ +} /* jerry_path_get_prev_segment_detail */ + +static bool +jerry_path_get_prev_segment (jerry_path_style_t path_style, jerry_segment_iterator_t *it) +{ + jerry_path_get_prev_segment_detail (path_style, it); + if (it->segment_eat_count == 0 && it->list_pos == 0 && it->pos == JERRY_SIZE_MAX && it->length == 0 + && it->segment_count > 0) + { + /* no more segments */ + return false; + } + if (it->list_pos == 0 && it->pos == JERRY_SIZE_MAX && it->root_length > 0) + { + /* Root have no trailing separator */ + it->end_with_separator = false; + } + else if (it->segment_count > 0) + { + it->end_with_separator = true; + } + else + { + /* The last segment preserve the original separator. */ + } + + it->segment_count += 1; + return true; +} /* jerry_path_get_prev_segment */ + +static jerry_string_t +jerry_path_get_segment (jerry_segment_iterator_t *it) +{ + jerry_string_t segment; + const jerry_string_t *path_current = it->path_list_p + it->list_pos; + jerry_size_t pos = it->pos + 1; + if (it->length > 0) + { + segment.size = it->length; + segment.ptr = path_current->ptr + pos; + return segment; + } + if (it->segment_eat_count > 0) + { + segment.size = 2; + } + else + { + segment.size = 1; + } + if ((it->list_pos + 1 == it->path_list_count) && pos + segment.size == path_current->size) + { + /* The latest segment with '.' or '..' */ + segment.ptr = path_current->ptr + pos; + } + else if (segment.size == 2) + { + segment.ptr = JERRY_ZSTR_LITERAL (".."); + } + else + { + segment.ptr = JERRY_ZSTR_LITERAL ("."); + } + return segment; +} /* jerry_path_get_segment */ + +static void +jerry_path_push_front_char (jerry_path_style_t path_style, + jerry_char_t *buffer_p, + jerry_size_t buffer_size, + jerry_size_t *buffer_index, + jerry_char_t ch) +{ + *buffer_index -= 1; + jerry_size_t buffer_index_current = *buffer_index; + if (buffer_index_current < buffer_size) + { + if (buffer_index_current == (buffer_size - 1)) + { + buffer_p[buffer_index_current] = '\0'; + } + else if (jerry_path_is_separator (path_style, ch)) + { + buffer_p[buffer_index_current] = path_style == JERRY_STYLE_UNIX ? '/' : '\\'; + } + else + { + buffer_p[buffer_index_current] = ch; + } + } +} /* jerry_path_push_front_char */ + +static void +jerry_path_push_front_string (jerry_path_style_t path_style, + jerry_char_t *buffer_p, + jerry_size_t buffer_size, + jerry_size_t *buffer_index, + const jerry_string_t *str) +{ + jerry_size_t k = str->size; + for (; k > 0;) + { + jerry_path_push_front_char (path_style, buffer_p, buffer_size, buffer_index, str->ptr[--k]); + } +} /* jerry_path_push_front_string */ + +static const jerry_string_t path_list_empty = { JERRY_ZSTR_ARG ("") }; + +/** + * Init the path segment interator + */ +static jerry_segment_iterator_t +jerry_path_interator_init (jerry_path_style_t path_style, /**< The style of the path list */ + bool is_resolve, /**< If do path resolve */ + bool remove_trailing_slash, /**< If remove the trailing slash symbol */ + const jerry_string_t *path_list_p, /**< Path list */ + jerry_size_t path_list_count) +{ + jerry_segment_iterator_t it = { 0 }; + jerry_size_t path_list_i; + jerry_size_t end_with_separator = JERRY_SIZE_MAX; + if (path_list_count == 0) + { + path_list_count = 1; + path_list_p = &path_list_empty; + } + path_list_i = path_list_count; + it.path_list_p = path_list_p; + it.path_list_count = path_list_count; + for (; path_list_i > 0;) + { + const jerry_string_t *path_list_current = path_list_p + (--path_list_i); + if (end_with_separator == JERRY_SIZE_MAX && path_list_current->size > 0) + { + end_with_separator = + jerry_path_is_separator (path_style, path_list_current->ptr[path_list_current->size - 1]) ? 1 : 0; + } + if (it.root_length == 0 && (is_resolve || path_list_i == 0)) + { + /* Find the first root path from right to left when `is_resolve` are + * `true` */ + it.root_length = jerry_path_get_root (path_style, path_list_current->ptr); + if (it.root_length > 0) + { + it.path_list_p += path_list_i; + it.path_list_count -= path_list_i; + } + } + } + it.root_is_absolute = + it.root_length > 0 && jerry_path_is_separator (path_style, it.path_list_p[0].ptr[it.root_length - 1]); + it.list_pos = it.path_list_count; + it.pos = JERRY_SIZE_MAX; + if (remove_trailing_slash) + { + it.end_with_separator = false; + } + else + { + it.end_with_separator = end_with_separator == 1; + } + return it; +} /* jerry_path_interator_init */ + +/** + * @brief Joins multiple paths together. + * + * @note + * - If is_resolve is true. The given sequence of paths is processed from right + * to left, with each subsequent path prepended until an absolute path is + * constructed. For instance, given the sequence of path segments: /foo, /bar, + * baz, calling path.resolve('/foo', '/bar', 'baz') would return /bar/baz + * because 'baz' is not an absolute path but + * '/bar' + '/' + 'baz' is. + * - If is_resolve is false. All paths are joined + * + * @return The size of the joined path, excluding the '\0' teminiator + */ +static jerry_size_t +jerry_path_join_and_normalize (jerry_path_style_t path_style, /**< The style of the path list */ + bool is_resolve, /**< Join path in resolve mode */ + bool remove_trailing_slash, /**< If remove the trailing slash symbol */ + const jerry_string_t *path_list_p, /**< Path list */ + jerry_size_t path_list_count, /**< Path list count */ + jerry_char_t *buffer_p, /**< The buffer to storaged the joined path */ + jerry_size_t buffer_size /**< The size of the buffer_p */ +) +{ + jerry_size_t buffer_size_calculated = 0; + for (;;) + { + jerry_segment_iterator_t it = + jerry_path_interator_init (path_style, is_resolve, remove_trailing_slash, path_list_p, path_list_count); + jerry_size_t buffer_index = JERRY_SIZE_MAX; + jerry_char_t *buffer_p_used; + + if (buffer_size_calculated == 0) + { + buffer_p_used = NULL; + /* For calculating the path size */ + buffer_index = JERRY_SIZE_MAX; + } + else + { + buffer_p_used = buffer_p; + if (buffer_size_calculated > buffer_size) + { + /** + * When `buffer_p` exist and `buffer_size_calculated > buffer_size`, + * that means there is not enough buffer to storage the final generated + * path, so that set buffer_index_init to `buffer_size_calculated` to + * ensure only the head part of the generated path are stored into + * `buffer_p` + */ + buffer_index = buffer_size_calculated; + } + else + { + if (buffer_p == it.path_list_p[0].ptr && it.path_list_count == 1) + { + /** + * The input path and output buffer are the same, storing the + * generated path at the tail of `buffer_p`; so that when normalizing + * the path inplace, the path won't be corrupted. + */ + buffer_index = buffer_size; + } + else + { + buffer_index = buffer_size_calculated; + } + } + } + jerry_path_push_front_char (path_style, buffer_p_used, buffer_size, &buffer_index, '\0'); + for (; jerry_path_get_prev_segment (path_style, &it);) + { + if (it.end_with_separator) + { + jerry_path_push_front_char (path_style, buffer_p_used, buffer_size, &buffer_index, '/'); + } + jerry_string_t segment = jerry_path_get_segment (&it); + jerry_path_push_front_string (path_style, buffer_p_used, buffer_size, &buffer_index, &segment); + } + if (buffer_size_calculated == 0) + { + buffer_size_calculated = JERRY_SIZE_MAX - buffer_index; + if (!buffer_p) + { + return buffer_size_calculated - 1; + } + continue; + } + if (buffer_p) + { + if (buffer_index > 0 && buffer_index < buffer_size) + { + memmove (buffer_p, buffer_p + buffer_index, buffer_size_calculated); + } + } + return buffer_size_calculated - 1; + } +} /* jerry_path_join_and_normalize */ + +/** + * Get the end of the directory part of the input path. + * + * @param path_p: input zero-terminated path string + * + * @return offset of the directory end in the input path string + */ +static jerry_size_t +jerry_module_path_base (jerry_path_style_t style, const jerry_char_t *path_p, jerry_size_t path_size) +{ + const jerry_char_t *end_p = path_p + path_size; + + while (end_p > path_p) + { + if (jerry_path_is_separator (style, *(end_p - 1))) + { + return (jerry_size_t) (end_p - path_p); + } + + end_p--; + } + + return 0; +} /* jerry_module_path_base */ + +static jerry_size_t +jerry_module_get_cwd (jerry_char_t **path_p) +{ + jerry_char_t *buffer_p = NULL; + jerry_size_t path_size = jerry_port_get_cwd (NULL, 0); + if (path_size > 0) + { + jerry_size_t buffer_size = path_size + 1; + buffer_p = jerry_heap_alloc (buffer_size); + if (buffer_p) + { + if (jerry_port_get_cwd (buffer_p, buffer_size) != path_size) + { + jerry_heap_free (buffer_p, buffer_size); + buffer_p = NULL; + } + } + } + *path_p = buffer_p; + return path_size; +} /* jerry_module_get_cwd */ + +static jerry_size_t +jerry_module_path_join_allocated (jerry_path_style_t style, + const jerry_string_t *path_list_p, + jerry_size_t path_list_count, + jerry_char_t **path_out_p) +{ + jerry_char_t *buffer_p = NULL; + jerry_size_t path_size = jerry_path_join_and_normalize (style, true, true, path_list_p, path_list_count, NULL, 0); + if (path_size > 0) + { + jerry_size_t buffer_size = path_size + 1; + buffer_p = jerry_heap_alloc (buffer_size); + if (buffer_p) + { + if (jerry_path_join_and_normalize (style, true, true, path_list_p, path_list_count, buffer_p, buffer_size) + != path_size) + { + jerry_heap_free (buffer_p, buffer_size); + buffer_p = NULL; + } + } + } + *path_out_p = buffer_p; + return path_size; +} /* jerry_module_path_join_allocated */ + /** * Release known modules. */ @@ -70,7 +693,7 @@ jerry_module_free (jerry_module_manager_t *manager_p, /**< module manager */ if (release_all || module_p->realm == realm) { - jerry_port_path_free (module_p->path_p); + jerry_heap_free (module_p->path_p, module_p->path_size + 1); jerry_value_free (module_p->realm); jerry_value_free (module_p->module); @@ -157,7 +780,19 @@ jerry_module_resolve (const jerry_value_t specifier, /**< module specifier strin jerry_string_to_buffer (specifier, JERRY_ENCODING_UTF8, reference_path_p + directory_size, specifier_size); reference_path_p[reference_size] = '\0'; - jerry_char_t *path_p = jerry_port_path_normalize (reference_path_p, reference_size); + jerry_char_t *path_p = NULL; + jerry_size_t path_size = 0; + jerry_path_style_t path_style = jerry_port_path_style (); + jerry_char_t *cwd_path_p = NULL; + jerry_size_t cwd_path_size = jerry_module_get_cwd (&cwd_path_p); + if (cwd_path_p) + { + jerry_string_t path_list[] = { { cwd_path_p, cwd_path_size }, { reference_path_p, reference_size } }; + path_size = + jerry_module_path_join_allocated (path_style, path_list, sizeof (path_list) / sizeof (path_list[0]), &path_p); + jerry_heap_free (cwd_path_p, cwd_path_size + 1); + } + jerry_heap_free (reference_path_p, reference_size + 1); if (path_p == NULL) @@ -174,10 +809,11 @@ jerry_module_resolve (const jerry_value_t specifier, /**< module specifier strin while (module_p != NULL) { - if (module_p->realm == realm && strcmp ((const char *) module_p->path_p, (const char *) path_p) == 0) + if (module_p->realm == realm && module_p->path_size == path_size + && memcmp (module_p->path_p, path_p, path_size) == 0) { jerry_value_free (realm); - jerry_port_path_free (path_p); + jerry_heap_free (path_p, path_size + 1); return jerry_value_copy (module_p->module); } @@ -190,7 +826,7 @@ jerry_module_resolve (const jerry_value_t specifier, /**< module specifier strin if (source_p == NULL) { jerry_value_free (realm); - jerry_port_path_free (path_p); + jerry_heap_free (path_p, path_size + 1); return jerry_throw_sz (JERRY_ERROR_SYNTAX, "Module file not found"); } @@ -206,7 +842,7 @@ jerry_module_resolve (const jerry_value_t specifier, /**< module specifier strin if (jerry_value_is_exception (ret_value)) { - jerry_port_path_free (path_p); + jerry_heap_free (path_p, path_size + 1); jerry_value_free (realm); return ret_value; } @@ -215,7 +851,8 @@ jerry_module_resolve (const jerry_value_t specifier, /**< module specifier strin module_p->next_p = manager_p->module_head_p; module_p->path_p = path_p; - module_p->basename_offset = jerry_port_path_base (module_p->path_p); + module_p->path_size = path_size; + module_p->basename_offset = jerry_module_path_base (path_style, module_p->path_p, module_p->path_size); module_p->realm = realm; module_p->module = jerry_value_copy (ret_value); diff --git a/jerry-core/include/jerryscript-port.h b/jerry-core/include/jerryscript-port.h index e3187371a6..ab182da062 100644 --- a/jerry-core/include/jerryscript-port.h +++ b/jerry-core/include/jerryscript-port.h @@ -37,6 +37,15 @@ JERRY_C_API_BEGIN * @{ */ +/** + * The path style of the OS + */ +typedef enum +{ + JERRY_STYLE_WINDOWS, + JERRY_STYLE_UNIX, +} jerry_path_style_t; + /** * Error codes that can be passed by the engine when calling jerry_port_fatal */ @@ -197,46 +206,29 @@ void jerry_port_line_free (jerry_char_t *buffer_p); */ /** - * Canonicalize a file path. - * - * If possible, the implementation should resolve symbolic links and other directory references found in the input path, - * and create a fully canonicalized file path as the result. - * - * The function may return with NULL in case an error is encountered, in which case the calling operation will not - * proceed. - * - * The implementation should allocate storage for the result path as necessary. Non-NULL return values will be passed - * to `jerry_port_path_free` when the result is no longer needed by the caller, which can be used to finalize - * dynamically allocated buffers. - * - * NOTE: The implementation must not return directly with the input, as the input buffer is released after the call. - * - * @param path_p: zero-terminated string containing the input path - * @param path_size: size of the input path string in bytes, excluding terminating zero - * - * @return buffer with the normalized path if the operation is successful, - * NULL otherwise - */ -jerry_char_t *jerry_port_path_normalize (const jerry_char_t *path_p, jerry_size_t path_size); - -/** - * Free a path buffer returned by jerry_port_path_normalize. + * Get the path style of the current OS * - * @param path_p: the path buffer to free + * @return path style */ -void jerry_port_path_free (jerry_char_t *path_p); +jerry_path_style_t jerry_port_path_style (void); /** - * Get the offset of the basename component in the input path. + * Get the cwd, the output string will be zero-terminated * - * The implementation should return the offset of the first character after the last path separator found in the path. - * This is used by the caller to split the path into a directory name and a file name. + * @param buffer_p: the buffer to storage the cwd + * @param buffer_size: the `buffer_p` buffer size, including '\0` terminator * - * @param path_p: input zero-terminated path string + * @note + * - cwd: current working directory * - * @return offset of the basename component in the input path + * @return The length of cwd, excluding '\0' terminator + * - When buffer_p is `NULL` and get cwd succeed return length of cwd + * - When buffer_p is `NULL` and get cwd failed return 0 + * - When buffer_p is not `NULL` and the `buffer_size - 1` just equal to + * length of cwd; and get cwd succeed return `buffer_size - 1`. + * - Otherwise means get cwd failed and return 0 */ -jerry_size_t jerry_port_path_base (const jerry_char_t *path_p); +jerry_size_t jerry_port_get_cwd (jerry_char_t *buffer_p, jerry_size_t buffer_size); /** * Open a source file and read its contents into a buffer. diff --git a/jerry-core/include/jerryscript-types.h b/jerry-core/include/jerryscript-types.h index 908382b237..ad79f6daa6 100644 --- a/jerry-core/include/jerryscript-types.h +++ b/jerry-core/include/jerryscript-types.h @@ -142,6 +142,8 @@ typedef uint8_t jerry_char_t; */ typedef uint32_t jerry_size_t; +#define JERRY_SIZE_MAX UINT32_MAX + /** * Length type of JerryScript. */ @@ -152,6 +154,15 @@ typedef uint32_t jerry_length_t; */ typedef uint32_t jerry_value_t; +/** + * Description of a JerryScript string for arguments passing + */ +typedef struct +{ + const jerry_char_t *ptr; /**< pointer to the zero-terminated ASCII/UTF-8/CESU-8 string */ + jerry_size_t size; /**< size of the string, excluding '\0' terminator */ +} jerry_string_t; + /** * Option bits for jerry_parse_options_t. */ @@ -863,10 +874,15 @@ typedef void (*jerry_arraybuffer_free_cb_t) (jerry_arraybuffer_type_t buffer_typ void *arraybuffer_user_p, void *user_p); +/** + * Helper to generate a string literal with type `const jerry_char_t *` + */ +#define JERRY_ZSTR_LITERAL(str) ((const jerry_char_t *) (str "")) + /** * Helper to expand string literal to [string pointer, string size] argument pair. */ -#define JERRY_ZSTR_ARG(str) ((const jerry_char_t *) (str)), ((jerry_size_t) (sizeof (str) - 1)) +#define JERRY_ZSTR_ARG(str) JERRY_ZSTR_LITERAL (str), ((jerry_size_t) (sizeof (str) - 1)) /** * @} diff --git a/jerry-ext/include/jerryscript-ext/sources.h b/jerry-ext/include/jerryscript-ext/sources.h index e33aa6b9a6..add84b5523 100644 --- a/jerry-ext/include/jerryscript-ext/sources.h +++ b/jerry-ext/include/jerryscript-ext/sources.h @@ -20,10 +20,10 @@ JERRY_C_API_BEGIN -jerry_value_t jerryx_source_parse_script (const char* path); -jerry_value_t jerryx_source_exec_script (const char* path); -jerry_value_t jerryx_source_exec_module (const char* path); -jerry_value_t jerryx_source_exec_snapshot (const char* path, size_t function_index); +jerry_value_t jerryx_source_parse_script (const jerry_string_t *path); +jerry_value_t jerryx_source_exec_script (const jerry_string_t *path); +jerry_value_t jerryx_source_exec_module (const jerry_string_t *path); +jerry_value_t jerryx_source_exec_snapshot (const jerry_string_t *path, size_t function_index); jerry_value_t jerryx_source_exec_stdin (void); JERRY_C_API_END diff --git a/jerry-ext/util/sources.c b/jerry-ext/util/sources.c index dbd629ee7a..fd56b5498d 100644 --- a/jerry-ext/util/sources.c +++ b/jerry-ext/util/sources.c @@ -27,14 +27,14 @@ #include "jerryscript-ext/print.h" jerry_value_t -jerryx_source_parse_script (const char *path_p) +jerryx_source_parse_script (const jerry_string_t *path_p) { jerry_size_t source_size; - jerry_char_t *source_p = jerry_port_source_read (path_p, &source_size); + jerry_char_t *source_p = jerry_port_source_read ((const char *) path_p->ptr, &source_size); if (source_p == NULL) { - jerry_log (JERRY_LOG_LEVEL_ERROR, "Failed to open file: %s\n", path_p); + jerry_log (JERRY_LOG_LEVEL_ERROR, "Failed to open file: %.*s\n", (int) path_p->size, path_p->ptr); return jerry_throw_sz (JERRY_ERROR_SYNTAX, "Source file not found"); } @@ -46,8 +46,7 @@ jerryx_source_parse_script (const char *path_p) jerry_parse_options_t parse_options; parse_options.options = JERRY_PARSE_HAS_SOURCE_NAME; - parse_options.source_name = - jerry_string ((const jerry_char_t *) path_p, (jerry_size_t) strlen (path_p), JERRY_ENCODING_UTF8); + parse_options.source_name = jerry_string (path_p->ptr, path_p->size, JERRY_ENCODING_UTF8); jerry_value_t result = jerry_parse (source_p, source_size, &parse_options); @@ -58,7 +57,7 @@ jerryx_source_parse_script (const char *path_p) } /* jerryx_source_parse_script */ jerry_value_t -jerryx_source_exec_script (const char *path_p) +jerryx_source_exec_script (const jerry_string_t *path_p) { jerry_value_t result = jerryx_source_parse_script (path_p); @@ -73,10 +72,9 @@ jerryx_source_exec_script (const char *path_p) } /* jerryx_source_exec_script */ jerry_value_t -jerryx_source_exec_module (const char *path_p) +jerryx_source_exec_module (const jerry_string_t *path_p) { - jerry_value_t specifier = - jerry_string ((const jerry_char_t *) path_p, (jerry_size_t) strlen (path_p), JERRY_ENCODING_UTF8); + jerry_value_t specifier = jerry_string (path_p->ptr, path_p->size, JERRY_ENCODING_UTF8); jerry_value_t referrer = jerry_undefined (); jerry_value_t module = jerry_module_resolve (specifier, referrer, NULL); @@ -110,14 +108,14 @@ jerryx_source_exec_module (const char *path_p) } /* jerryx_source_exec_module */ jerry_value_t -jerryx_source_exec_snapshot (const char *path_p, size_t function_index) +jerryx_source_exec_snapshot (const jerry_string_t *path_p, size_t function_index) { jerry_size_t source_size; - jerry_char_t *source_p = jerry_port_source_read (path_p, &source_size); + jerry_char_t *source_p = jerry_port_source_read ((const char *) path_p->ptr, &source_size); if (source_p == NULL) { - jerry_log (JERRY_LOG_LEVEL_ERROR, "Failed to open file: %s\n", path_p); + jerry_log (JERRY_LOG_LEVEL_ERROR, "Failed to open file: %.*s\n", (int) path_p->size, path_p->ptr); return jerry_throw_sz (JERRY_ERROR_SYNTAX, "Snapshot file not found"); } diff --git a/jerry-main/main-desktop.c b/jerry-main/main-desktop.c index 920ea9ca6f..4cfe5c2177 100644 --- a/jerry-main/main-desktop.c +++ b/jerry-main/main-desktop.c @@ -130,17 +130,18 @@ main (int argc, char **argv) { main_source_t *source_file_p = sources_p + source_index; const char *file_path_p = argv[source_file_p->path_index]; + jerry_string_t file_path = { (const jerry_char_t *) file_path_p, (jerry_size_t) strlen (file_path_p) }; switch (source_file_p->type) { case SOURCE_MODULE: { - result = jerryx_source_exec_module (file_path_p); + result = jerryx_source_exec_module (&file_path); break; } case SOURCE_SNAPSHOT: { - result = jerryx_source_exec_snapshot (file_path_p, source_file_p->snapshot_index); + result = jerryx_source_exec_snapshot (&file_path, source_file_p->snapshot_index); break; } default: @@ -149,11 +150,11 @@ main (int argc, char **argv) if ((arguments.option_flags & OPT_FLAG_PARSE_ONLY) != 0) { - result = jerryx_source_parse_script (file_path_p); + result = jerryx_source_parse_script (&file_path); } else { - result = jerryx_source_exec_script (file_path_p); + result = jerryx_source_exec_script (&file_path); } break; diff --git a/jerry-port/common/jerry-port-fs.c b/jerry-port/common/jerry-port-fs.c index d32264c47c..950f9a9ce5 100644 --- a/jerry-port/common/jerry-port-fs.c +++ b/jerry-port/common/jerry-port-fs.c @@ -25,7 +25,7 @@ #ifndef S_ISDIR #define S_ISDIR(mode) (((mode) &S_IFMT) == S_IFDIR) #endif /* !defined(S_ISDIR) */ -#endif /* __GLIBC__ */ +#endif /* defined(__GLIBC__) || defined(_WIN32) */ /** * Determines the size of the given file. @@ -54,7 +54,7 @@ jerry_port_source_read (const char *file_name_p, jerry_size_t *out_size_p) { return NULL; } -#endif /* __GLIBC__ */ +#endif /* defined(__GLIBC__) || defined(_WIN32) */ FILE *file_p = fopen (file_name_p, "rb"); @@ -93,45 +93,37 @@ jerry_port_source_free (uint8_t *buffer_p) free (buffer_p); } /* jerry_port_source_free */ +#if !defined(_WIN32) + +jerry_path_style_t +jerry_port_path_style (void) +{ + return JERRY_STYLE_UNIX; +} /* jerry_port_path_style */ + +#endif /* !defined(_WIN32) */ + /** * These functions provide generic implementation for paths and are only enabled when the compiler support weak symbols, * and we are not building for a platform that has platform specific versions. */ #if !(defined(__unix__) || defined(__APPLE__) || defined(_WIN32)) -jerry_char_t *JERRY_ATTR_WEAK -jerry_port_path_normalize (const jerry_char_t *path_p, jerry_size_t path_size) +jerry_size_t JERRY_ATTR_WEAK +jerry_port_get_cwd (jerry_char_t *buffer_p, jerry_size_t buffer_size) { - jerry_char_t *buffer_p = (jerry_char_t *) malloc (path_size + 1); - + /* Default to `/` */ if (buffer_p == NULL) { - return NULL; + return 1; } - - /* Also copy terminating zero byte. */ - memcpy (buffer_p, path_p, path_size + 1); - - return buffer_p; -} /* jerry_port_path_normalize */ - -void JERRY_ATTR_WEAK -jerry_port_path_free (jerry_char_t *path_p) -{ - free (path_p); -} /* jerry_port_path_free */ - -jerry_size_t JERRY_ATTR_WEAK -jerry_port_path_base (const jerry_char_t *path_p) -{ - const jerry_char_t *basename_p = (jerry_char_t *) strrchr ((char *) path_p, '/') + 1; - - if (basename_p == NULL) + if (buffer_size == 2) { - return 0; + buffer_p[0] = '/'; + buffer_p[1] = '\0'; + return 1; } - - return (jerry_size_t) (basename_p - path_p); -} /* jerry_port_path_base */ + return 0; +} /* jerry_port_get_cwd */ #endif /* !(defined(__unix__) || defined(__APPLE__) || defined(_WIN32)) */ diff --git a/jerry-port/unix/jerry-port-unix-fs.c b/jerry-port/unix/jerry-port-unix-fs.c index 278b34794f..354dd22ab9 100644 --- a/jerry-port/unix/jerry-port-unix-fs.c +++ b/jerry-port/unix/jerry-port-unix-fs.c @@ -17,29 +17,46 @@ #if defined(__unix__) || defined(__APPLE__) +#include #include #include +#include -jerry_char_t * -jerry_port_path_normalize (const jerry_char_t *path_p, jerry_size_t path_size) +jerry_size_t +jerry_port_get_cwd (jerry_char_t *buffer_p, jerry_size_t buffer_size) { - (void) path_size; - - return (jerry_char_t *) realpath ((char *) path_p, NULL); -} /* jerry_port_path_normalize */ - -void -jerry_port_path_free (jerry_char_t *path_p) -{ - free (path_p); -} /* jerry_port_path_free */ - -jerry_size_t JERRY_ATTR_WEAK -jerry_port_path_base (const jerry_char_t *path_p) -{ - const jerry_char_t *basename_p = (jerry_char_t *) strrchr ((char *) path_p, '/') + 1; - - return (jerry_size_t) (basename_p - path_p); -} /* jerry_port_path_base */ + if (buffer_p == NULL) + { + jerry_size_t size = 260; + char *buf = NULL; + char *ptr = NULL; + for (; ptr == NULL; size *= 2) + { + if ((ptr = realloc (buf, size)) == NULL) + { + break; + } + buf = ptr; + ptr = getcwd (buf, size); + if (ptr == NULL && errno != ERANGE) + { + break; + } + } + size = ptr ? (jerry_size_t) strlen (ptr) : 0; + if (buf) + free (buf); + return size; + } + + if (getcwd ((char *) buffer_p, buffer_size) != NULL) + { + if ((strlen ((char *) buffer_p) + 1) == buffer_size) + { + return buffer_size - 1; + } + } + return 0; +} /* jerry_port_get_cwd */ #endif /* defined(__unix__) || defined(__APPLE__) */ diff --git a/jerry-port/win/jerry-port-win-fs.c b/jerry-port/win/jerry-port-win-fs.c index a761d32cb6..f9fa6c8e81 100644 --- a/jerry-port/win/jerry-port-win-fs.c +++ b/jerry-port/win/jerry-port-win-fs.c @@ -17,39 +17,51 @@ #if defined(_WIN32) +#include #include #include -jerry_char_t * -jerry_port_path_normalize (const jerry_char_t *path_p, jerry_size_t path_size) +jerry_path_style_t +jerry_port_path_style (void) { - (void) path_size; - - return (jerry_char_t *) _fullpath (NULL, (const char *) path_p, _MAX_PATH); -} /* jerry_port_path_normalize */ - -void -jerry_port_path_free (jerry_char_t *path_p) -{ - free (path_p); -} /* jerry_port_path_free */ + return JERRY_STYLE_WINDOWS; +} /* jerry_port_path_style */ jerry_size_t -jerry_port_path_base (const jerry_char_t *path_p) +jerry_port_get_cwd (jerry_char_t *buffer_p, jerry_size_t buffer_size) { - const jerry_char_t *end_p = path_p + strlen ((const char *) path_p); - - while (end_p > path_p) + if (buffer_p == NULL) { - if (end_p[-1] == '/' || end_p[-1] == '\\') + jerry_size_t size = _MAX_PATH; + char *buf = NULL; + char *ptr = NULL; + for (; ptr == NULL; size *= 2) { - return (jerry_size_t) (end_p - path_p); + if ((ptr = realloc (buf, size)) == NULL) + { + break; + } + buf = ptr; + ptr = getcwd (buf, (int) size); + if (ptr == NULL && errno != ERANGE) + { + break; + } } - - end_p--; + size = ptr ? (jerry_size_t) strlen (ptr) : 0; + if (buf) + free (buf); + return size; } + if (getcwd ((char *) buffer_p, (int) buffer_size) != NULL) + { + if ((strlen ((char *) buffer_p) + 1) == buffer_size) + { + return buffer_size - 1; + } + } return 0; -} /* jerry_port_path_base */ +} /* jerry_port_get_cwd */ #endif /* defined(_WIN32) */