@@ -77,9 +77,9 @@ invokeHelper(Value const& fn, dom::Array const& args)
7777 // of symbol contexts which contain circular references.
7878 std::vector<dom::Value> callArgs;
7979 callArgs.reserve (args.size () - 1 );
80- for (auto it = args. begin (); it != std::prev ( args.end ()) ; ++it )
80+ for (std:: size_t i = 0 ; i < args.size () - 1 ; ++i )
8181 {
82- callArgs.push_back (*it );
82+ callArgs.push_back (args. get (i) );
8383 }
8484
8585 auto ret = fn.apply (callArgs);
@@ -128,6 +128,12 @@ toString(jerry_value_t v)
128128// - Runtime errors (thrown exceptions) are prefixed with "Unexpected: " if they
129129// don't already contain that marker, helping distinguish them from parse errors
130130// - This prefix is intentionally consistent to aid debugging and testing
131+ //
132+ // LIMITATION: The "Unexpected" heuristic isn't perfect - some runtime errors
133+ // may contain "Unexpected" in their message and won't get the prefix, while
134+ // some custom syntax-like errors might get prefixed incorrectly. This is
135+ // acceptable because the prefix is for debugging convenience, not semantic
136+ // correctness.
131137static Error
132138makeError (jerry_value_t exc)
133139{
@@ -138,8 +144,12 @@ makeError(jerry_value_t exc)
138144 std::string msg;
139145 if (jerry_value_is_object (obj))
140146 {
141- jerry_value_t msg_prop
142- = jerry_object_get (obj, jerry_string_sz (" message" ));
147+ // Note: jerry_string_sz is used here instead of makeString because
148+ // makeString is defined later in this file and we need to extract
149+ // error messages early in the error handling path.
150+ jerry_value_t msg_key = jerry_string_sz (" message" );
151+ jerry_value_t msg_prop = jerry_object_get (obj, msg_key);
152+ jerry_value_free (msg_key);
143153 if (!jerry_value_is_exception (msg_prop))
144154 {
145155 msg = toString (msg_prop);
@@ -245,12 +255,23 @@ struct Context::Impl {
245255std::mutex Context::Impl::init_mtx;
246256unsigned Context::Impl::jerry_refcount = 0 ;
247257
258+ // Acquire the context lock for thread-safe JerryScript operations.
259+ //
260+ // Thread Safety Model:
261+ // - JerryScript is single-threaded; all engine calls must be serialized.
262+ // - Each Context::Impl has a recursive_mutex protecting its JerryScript state.
263+ // - Values hold shared_ptr<Impl> to keep the context alive and use this
264+ // function before any jerry_* calls.
265+ // - The recursive_mutex allows nested locking (e.g., a trap callback that
266+ // needs to call toJsValue which also locks).
267+ //
268+ // @param impl Shared context state, or nullptr for moved-from Values.
269+ // @return RAII lock guard; empty if impl is null.
248270static std::unique_lock<std::recursive_mutex>
249271lockContext (std::shared_ptr<Context::Impl> const & impl)
250272{
251- // Prevent concurrent use of a JerryScript context without requiring callers
252- // to remember which locks to take. Accepts null shared_ptr so callers can
253- // use it uniformly in move/copy paths.
273+ // Accepts null shared_ptr so callers can use it uniformly in move/copy
274+ // paths where the source Value may have been moved-from (val_ == 0).
254275 if (impl)
255276 {
256277 return std::unique_lock<std::recursive_mutex>(impl->mtx );
@@ -985,8 +1006,9 @@ Value::size() const
9851006 if (isArray ())
9861007 {
9871008 auto lock = lockContext (impl_);
988- jerry_value_t lenVal
989- = jerry_object_get (to_js (val_), jerry_string_sz (" length" ));
1009+ jerry_value_t lenKey = makeString (" length" );
1010+ jerry_value_t lenVal = jerry_object_get (to_js (val_), lenKey);
1011+ jerry_value_free (lenKey);
9901012 if (jerry_value_is_exception (lenVal))
9911013 {
9921014 jerry_value_free (lenVal);
@@ -1172,7 +1194,7 @@ makeObjectProxy(dom::Object obj, std::shared_ptr<Context::Impl> impl)
11721194 return toJsValue (val, h->impl );
11731195 });
11741196
1175- jerry_value_t get_key = jerry_string_sz (" get" );
1197+ jerry_value_t get_key = makeString (" get" );
11761198 jerry_value_t sr = jerry_object_set (handler, get_key, get_fn);
11771199 jerry_value_free (sr);
11781200 jerry_value_free (get_key);
@@ -1195,7 +1217,7 @@ makeObjectProxy(dom::Object obj, std::shared_ptr<Context::Impl> impl)
11951217 return jerry_boolean (h->value .getObject ().exists (propName));
11961218 });
11971219
1198- jerry_value_t has_key = jerry_string_sz (" has" );
1220+ jerry_value_t has_key = makeString (" has" );
11991221 sr = jerry_object_set (handler, has_key, has_fn);
12001222 jerry_value_free (sr);
12011223 jerry_value_free (has_key);
@@ -1221,15 +1243,15 @@ makeObjectProxy(dom::Object obj, std::shared_ptr<Context::Impl> impl)
12211243 jerry_value_t arr = jerry_array (keys.size ());
12221244 for (uint32_t i = 0 ; i < keys.size (); ++i)
12231245 {
1224- jerry_value_t keyVal = jerry_string_sz (keys[i]. c_str () );
1246+ jerry_value_t keyVal = makeString (keys[i]);
12251247 jerry_value_t setRes = jerry_object_set_index (arr, i, keyVal);
12261248 jerry_value_free (setRes);
12271249 jerry_value_free (keyVal);
12281250 }
12291251 return arr;
12301252 });
12311253
1232- jerry_value_t ownKeys_key = jerry_string_sz (" ownKeys" );
1254+ jerry_value_t ownKeys_key = makeString (" ownKeys" );
12331255 sr = jerry_object_set (handler, ownKeys_key, ownKeys_fn);
12341256 jerry_value_free (sr);
12351257 jerry_value_free (ownKeys_key);
@@ -1257,53 +1279,58 @@ makeObjectProxy(dom::Object obj, std::shared_ptr<Context::Impl> impl)
12571279 jerry_value_t val = toJsValue (h->value .getObject ().get (propName), h->impl );
12581280 jerry_value_t setRes;
12591281
1260- jerry_value_t valueKey = jerry_string_sz (" value" );
1282+ jerry_value_t valueKey = makeString (" value" );
12611283 setRes = jerry_object_set (desc, valueKey, val);
12621284 jerry_value_free (setRes);
12631285 jerry_value_free (valueKey);
12641286 jerry_value_free (val);
12651287
1266- jerry_value_t writableKey = jerry_string_sz (" writable" );
1288+ jerry_value_t writableKey = makeString (" writable" );
12671289 setRes = jerry_object_set (desc, writableKey, jerry_boolean (true ));
12681290 jerry_value_free (setRes);
12691291 jerry_value_free (writableKey);
12701292
1271- jerry_value_t enumKey = jerry_string_sz (" enumerable" );
1293+ jerry_value_t enumKey = makeString (" enumerable" );
12721294 setRes = jerry_object_set (desc, enumKey, jerry_boolean (true ));
12731295 jerry_value_free (setRes);
12741296 jerry_value_free (enumKey);
12751297
1276- jerry_value_t configKey = jerry_string_sz (" configurable" );
1298+ jerry_value_t configKey = makeString (" configurable" );
12771299 setRes = jerry_object_set (desc, configKey, jerry_boolean (true ));
12781300 jerry_value_free (setRes);
12791301 jerry_value_free (configKey);
12801302
12811303 return desc;
12821304 });
12831305
1284- jerry_value_t getOwnPropDesc_key = jerry_string_sz (" getOwnPropertyDescriptor" );
1306+ jerry_value_t getOwnPropDesc_key = makeString (" getOwnPropertyDescriptor" );
12851307 sr = jerry_object_set (handler, getOwnPropDesc_key, getOwnPropDesc_fn);
12861308 jerry_value_free (sr);
12871309 jerry_value_free (getOwnPropDesc_key);
12881310 jerry_value_free (getOwnPropDesc_fn);
12891311
1290- // Store a reference to the holder on the handler so traps can access it
1312+ // Store a reference to the holder on the handler so traps can access it.
1313+ //
1314+ // Ownership chain: proxy → handler → holderRef → native_ptr(holder)
1315+ //
1316+ // When the proxy is garbage collected, handler is released, which releases
1317+ // holderRef, which triggers DomValueHolder::free_cb to delete the holder.
1318+ // This happens synchronously when refcounts reach zero (not deferred to GC).
12911319 jerry_value_t holderRef = jerry_object ();
12921320 jerry_object_set_native_ptr (holderRef, &kDomProxyInfo , holder);
1293- jerry_value_t holderKey = jerry_string_sz (" __holder__" );
1321+ jerry_value_t holderKey = makeString (" __holder__" );
12941322 sr = jerry_object_set (handler, holderKey, holderRef);
12951323 jerry_value_free (sr);
12961324 jerry_value_free (holderKey);
1297- jerry_value_free (holderRef);
1325+ jerry_value_free (holderRef); // handler now owns the reference
12981326
12991327 // Create the proxy
13001328 jerry_value_t proxy = jerry_proxy (target, handler);
13011329 jerry_value_free (target);
1302- jerry_value_free (handler);
1330+ jerry_value_free (handler); // proxy now owns handler (and transitively holder)
13031331
1304- // If proxy creation fails, return an empty object rather than an exception.
1305- // The holder is cleaned up via the free_cb when holderRef (attached to
1306- // handler) is garbage collected.
1332+ // If proxy creation fails, handler was still freed above, which releases
1333+ // holderRef, which deletes holder via free_cb. Return empty object.
13071334 if (jerry_value_is_exception (proxy))
13081335 {
13091336 jerry_value_free (proxy);
0 commit comments