22#include " encode.h"
33
44#include < cstdio>
5- #include < iostream>
65#include < js/CompilationAndEvaluation.h>
76#include < js/MapAndSet.h>
87#include < js/Value.h>
98#include < jsfriendapi.h>
109#include < sys/stat.h>
1110
11+ namespace {
12+
1213static api::Engine* ENGINE;
1314static ScriptLoader* SCRIPT_LOADER;
15+ static bool MODULE_MODE = true ;
16+ static std::string BASE_PATH;
17+
1418JS::PersistentRootedObject moduleRegistry;
1519JS::PersistentRootedObject builtinModules;
16- static bool MODULE_MODE = true ;
17- static char * BASE_PATH = nullptr ;
18- mozilla::Maybe<std::string> PATH_PREFIX = mozilla::Nothing();
1920JS::CompileOptions *COMPILE_OPTS;
2021
22+ mozilla::Maybe<std::string> PATH_PREFIX = mozilla::Nothing();
23+
24+ } // namespace
25+
2126using host_api::HostString;
2227
2328namespace ScriptLoaderErrors {
@@ -40,107 +45,92 @@ class AutoCloseFile {
4045 success = !fclose (file);
4146 }
4247 file = nullptr ;
48+ // Clang analyzer complains about the stream leaking when stream is stdin, stdout, or stderr.
49+ // NOLINTNEXTLINE(clang-analyzer-unix.Stream)
4350 return success;
4451 }
4552};
4653
4754// strip off the given prefix when possible for nicer debugging stacks
48- static const char * strip_prefix (const char * resolved_path,
55+ static std::string strip_prefix (std::string_view resolved_path,
4956 mozilla::Maybe<std::string> path_prefix) {
5057 if (!path_prefix) {
51- return strdup (resolved_path);
58+ return std::string (resolved_path);
5259 }
53- auto & base = *path_prefix;
54- if (strncmp (resolved_path, base.data (), base.length ()) != 0 ) {
55- return strdup (resolved_path);
60+
61+ const auto & base = *path_prefix;
62+ if (resolved_path.rfind (base, 0 ) != 0 ) {
63+ return std::string (resolved_path);
5664 }
57- size_t path_len = strlen (resolved_path);
58- char * buf (js_pod_malloc<char >(path_len - base.length () + 1 ));
59- strncpy (buf, &resolved_path[base.length ()], path_len - base.length () + 1 );
60- return buf;
65+
66+ return std::string (resolved_path.substr (base.size ()));
6167}
6268
6369struct stat s;
6470
65- static const char * resolve_extension (const char * resolved_path) {
66- if (stat (resolved_path, &s) == 0 ) {
71+
72+ static std::string resolve_extension (std::string resolved_path) {
73+ if (stat (resolved_path.c_str (), &s) == 0 ) {
6774 return resolved_path;
6875 }
69- size_t len = strlen (resolved_path);
70- if (strncmp (resolved_path + len - 3 , " .js" , 3 ) == 0 ) {
76+
77+ if (resolved_path.size () >= 3 &&
78+ resolved_path.compare (resolved_path.size () - 3 , 3 , " .js" ) == 0 ) {
7179 return resolved_path;
7280 }
73- char * resolved_path_ext = new char [len + 4 ];
74- strncpy (resolved_path_ext, resolved_path, len);
75- strncpy (resolved_path_ext + len, " .js" , 4 );
76- MOZ_ASSERT (strlen (resolved_path_ext) == len + 3 );
77- if (stat (resolved_path_ext, &s) == 0 ) {
78- delete[] resolved_path;
79- return resolved_path_ext;
81+
82+ std::string with_ext = resolved_path + " .js" ;
83+ if (stat (with_ext.c_str (), &s) == 0 ) {
84+ return with_ext;
8085 }
81- delete[] resolved_path_ext;
8286 return resolved_path;
8387}
8488
85- static const char * resolve_path (const char * path, const char * base, size_t base_len) {
86- MOZ_ASSERT (base);
87- while (base_len > 0 && base[base_len - 1 ] != ' /' ) {
88- base_len--;
89+ static std::string resolve_path (std::string_view path,
90+ std::string_view base) {
91+ // Prepare initial resolved (with trailing '/')
92+ std::string resolved;
93+ if (!path.empty () && path[0 ] == ' /' ) {
94+ // absolute path
95+ } else {
96+ resolved.assign (base);
97+ auto slash = resolved.find_last_of (' /' );
98+ if (slash != std::string::npos) {
99+ resolved.resize (slash + 1 );
100+ }
89101 }
90- size_t path_len = strlen (path);
91-
92- // create the maximum buffer size as a working buffer
93- char * resolved_path = new char [base_len + path_len + 1 ];
94102
95- // copy the base in if used
96- size_t resolved_len = base_len;
97- if (path[0 ] == ' /' ) {
98- resolved_len = 0 ;
99- } else {
100- strncpy (resolved_path, base, base_len);
103+ // Split into segments
104+ std::vector<std::string_view> parts;
105+ size_t start = 0 ;
106+ while (start <= path.size ()) {
107+ auto end = path.find (' /' , start);
108+ if (end == std::string_view::npos) end = path.size ();
109+ parts.emplace_back (path.substr (start, end - start));
110+ start = end + 1 ;
101111 }
102112
103113 // Iterate through each segment of the path, copying each segment into the resolved path,
104114 // while handling backtracking for .. segments and skipping . segments appropriately.
105- size_t path_from_idx = 0 ;
106- size_t path_cur_idx = 0 ;
107- while (path_cur_idx < path_len) {
108- // read until the end or the next / to get the segment position
109- // as the substring between path_from_idx and path_cur_idx
110- while (path_cur_idx < path_len && path[path_cur_idx] != ' /' )
111- path_cur_idx++;
112- if (path_cur_idx == path_from_idx)
113- break ;
114- // . segment to skip
115- if (path_cur_idx - path_from_idx == 1 && path[path_from_idx] == ' .' ) {
116- path_cur_idx++;
117- path_from_idx = path_cur_idx;
115+ std::string result = resolved;
116+ for (auto seg : parts) {
117+ if (seg.empty () || seg == " ." ) {
118118 continue ;
119119 }
120- // .. segment backtracking
121- if (path_cur_idx - path_from_idx == 2 && path[path_from_idx] == ' .' && path[path_from_idx + 1 ] == ' .' ) {
122- path_cur_idx++;
123- path_from_idx = path_cur_idx;
124- if (resolved_len > 0 && resolved_path[resolved_len - 1 ] == ' /' ) {
125- resolved_len --;
120+ if (seg == " .." ) {
121+ auto pos = result.find_last_of (' /' );
122+ if (pos != std::string::npos) {
123+ result.resize (pos);
126124 }
127- while (resolved_len > 0 && resolved_path[resolved_len - 1 ] != ' /' ) {
128- resolved_len--;
125+ } else {
126+ if (!result.empty () && result.back () != ' /' ) {
127+ result.push_back (' /' );
129128 }
130- continue ;
129+ result. append (seg) ;
131130 }
132- // normal segment to copy (with the trailing / if not the last segment)
133- if (path[path_cur_idx] == ' /' )
134- path_cur_idx++;
135- strncpy (resolved_path + resolved_len, path + path_from_idx, path_cur_idx - path_from_idx);
136- resolved_len += path_cur_idx - path_from_idx;
137- path_from_idx = path_cur_idx;
138- }
139-
140- // finalize the buffer
141- resolved_path[resolved_len] = ' \0 ' ;
142- MOZ_ASSERT (strlen (resolved_path) == resolved_len);
143- return resolve_extension (resolved_path);
131+ }
132+
133+ return resolve_extension (result);
144134}
145135
146136static JSObject* get_module (JSContext* cx, JS::SourceText<mozilla::Utf8Unit> &source,
@@ -178,11 +168,12 @@ static JSObject* get_module(JSContext* cx, JS::SourceText<mozilla::Utf8Unit> &so
178168static JSObject* get_module (JSContext* cx, const char * specifier, const char * resolved_path,
179169 const JS::CompileOptions &opts) {
180170 RootedString resolved_path_str (cx, JS_NewStringCopyZ (cx, resolved_path));
171+
181172 if (!resolved_path_str) {
182173 return nullptr ;
183174 }
184- RootedValue resolved_path_val (cx, StringValue (resolved_path_str));
185175
176+ RootedValue resolved_path_val (cx, StringValue (resolved_path_str));
186177 RootedValue module_val (cx);
187178 if (!JS::MapGet (cx, moduleRegistry, resolved_path_val, &module_val)) {
188179 return nullptr ;
@@ -327,11 +318,12 @@ JSObject* module_resolve_hook(JSContext* cx, HandleValue referencingPrivate,
327318 }
328319
329320 HostString str = core::encode (cx, parent_path_val);
330- const char * resolved_path = resolve_path (path.get (), str.ptr .get (), str. len );
321+ auto resolved_path = resolve_path (path.get (), str.ptr .get ());
331322
332323 JS::CompileOptions opts (cx, *COMPILE_OPTS);
333- opts.setFileAndLine (strip_prefix (resolved_path, PATH_PREFIX), 1 );
334- return get_module (cx, path.get (), resolved_path, opts);
324+ auto stripped = strip_prefix (resolved_path, PATH_PREFIX);
325+ opts.setFileAndLine (stripped.c_str (), 1 );
326+ return get_module (cx, path.get (), resolved_path.c_str (), opts);
335327}
336328
337329bool module_metadata_hook (JSContext* cx, HandleValue referencingPrivate, HandleObject metaObject) {
@@ -432,34 +424,31 @@ bool ScriptLoader::load_resolved_script(JSContext *cx, const char *specifier,
432424
433425bool ScriptLoader::load_script (JSContext *cx, const char *script_path,
434426 JS::SourceText<mozilla::Utf8Unit> &script) {
435- const char *resolved_path;
436- if (!BASE_PATH) {
437- auto last_slash = strrchr (script_path, ' /' );
438- size_t base_len;
439- if (last_slash) {
440- last_slash++;
441- base_len = last_slash - script_path;
442- BASE_PATH = new char [base_len + 1 ];
443- MOZ_ASSERT (BASE_PATH);
444- strncpy (BASE_PATH, script_path, base_len);
445- BASE_PATH[base_len] = ' \0 ' ;
427+
428+ std::string path (script_path);
429+ std::string resolved;
430+ if (BASE_PATH.empty ()) {
431+ auto pos = path.find_last_of (' /' );
432+ if (pos != std::string::npos) {
433+ BASE_PATH = path.substr (0 , pos + 1 );
446434 } else {
447- BASE_PATH = strdup ( " ./" ) ;
435+ BASE_PATH = " ./" ;
448436 }
449- resolved_path = script_path ;
437+ resolved = path ;
450438 } else {
451- resolved_path = resolve_path (script_path , BASE_PATH, strlen (BASE_PATH) );
439+ resolved = resolve_path (path , BASE_PATH);
452440 }
453-
454- return load_resolved_script (cx, script_path, resolved_path, script);
441+ return load_resolved_script (cx, script_path, resolved.c_str (), script);
455442}
456443
457444bool ScriptLoader::eval_top_level_script (const char *path, JS::SourceText<mozilla::Utf8Unit> &source,
458445 MutableHandleValue result, MutableHandleValue tla_promise) {
459446 JSContext *cx = ENGINE->cx ();
460447
461448 JS::CompileOptions opts (cx, *COMPILE_OPTS);
462- opts.setFileAndLine (strip_prefix (path, PATH_PREFIX), 1 );
449+ auto stripped = strip_prefix (path, PATH_PREFIX);
450+ opts.setFileAndLine (stripped.c_str (), 1 );
451+
463452 JS::RootedScript script (cx);
464453 RootedObject module (cx);
465454 if (MODULE_MODE) {
0 commit comments