Repository navigation
fix(android): use $name when a Java package's global name is taken - #86
NathanWalker wants to merge 2 commits into
Conversation
A host can own a top-level package name as a read-only global; Expo defines `expo`. The jsi runtime already exposed such a package as `$expo`, but did so silently. It now logs a warning naming both globals. The napi runtime had no fallback: depending on the engine, the assignment either failed or was ignored, and the package was not reachable at all. It now gets the same `$name` fallback and warning.
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the
|
| @@ -1120,7 +1142,15 @@ void MetadataNode::CreateTopLevelNamespaces(napi_env env) { | |||
| if (IsJavascriptKeyword(nameSpace)) { | |||
| nameSpace = "$" + nameSpace; | |||
| } | |||
| NAPI_GUARD(napi_set_named_property(env, global, nameSpace.c_str(), packageObj)) {} | |||
| if (!SetGlobalIfWritable(env, global, nameSpace, packageObj)) { | |||
| // The host already owns the name as a read-only global (Expo's | |||
| // `expo`), so the package takes the same $ prefix as keywords. | |||
| string fallback = "$" + nameSpace; | |||
| __android_log_print(ANDROID_LOG_WARN, "TNS.Runtime", | |||
| "Java package '%s' is exposed as '%s': the global '%s' is read-only.", | |||
| nameSpace.c_str(), fallback.c_str(), nameSpace.c_str()); | |||
| NAPI_GUARD(napi_set_named_property(env, global, fallback.c_str(), packageObj)) {} | |||
| } | |||
There was a problem hiding this comment.
The SetGlobalIfWritable function is used only once hence move the function body inside CreateTopLevelNamespaces.
There was a problem hiding this comment.
Done. The helper is gone and the check is inline in CreateTopLevelNamespaces.
| } | ||
| NAPI_GUARD(napi_set_named_property(env, global, nameSpace.c_str(), packageObj)) {} | ||
| if (!SetGlobalIfWritable(env, global, nameSpace, packageObj)) { | ||
| // The host already owns the name as a read-only global (Expo's |
There was a problem hiding this comment.
Remove mention of expo or Expo from the comments.
There was a problem hiding this comment.
Done. No comment mentions Expo now.
| if (napi_set_named_property(env, global, name.c_str(), value) == napi_ok) { | ||
| napi_value assigned; | ||
| bool same = false; | ||
| if (napi_get_named_property(env, global, name.c_str(), &assigned) == napi_ok && | ||
| napi_strict_equals(env, assigned, value, &same) == napi_ok && same) { | ||
| return true; | ||
| } | ||
| } | ||
| bool pending = false; | ||
| napi_is_exception_pending(env, &pending); | ||
| if (pending) { | ||
| napi_value error; | ||
| napi_get_and_clear_last_exception(env, &error); | ||
| } | ||
| return false; | ||
| } |
There was a problem hiding this comment.
All of this is probably not needed because we do not want to silently overwrite a value set on a global object even if it was not read-only I think. So A simple napi_has_named_property is enough.
There was a problem hiding this comment.
Agreed. It now does a plain napi_has_named_property: if the global already has the name, the package goes on $name and a warning is logged, so nothing is overwritten. 0948f32
| try { | ||
| global.setProperty(rt, nameSpace.c_str(), packageObj); | ||
| } catch (JsError &) { | ||
| // A host can already own the name as a non-writable global (Expo | ||
| // defines `expo`), so fall back to the same $ prefix as keywords. | ||
| global.setProperty(rt, ("$" + nameSpace).c_str(), packageObj); | ||
| // The host already owns the name as a read-only global (Expo's | ||
| // `expo`), so the package takes the same $ prefix as keywords. | ||
| string fallback = "$" + nameSpace; | ||
| __android_log_print(ANDROID_LOG_WARN, "TNS.Runtime", | ||
| "Java package '%s' is exposed as '%s': the global '%s' is read-only.", | ||
| nameSpace.c_str(), fallback.c_str(), nameSpace.c_str()); | ||
| global.setProperty(rt, fallback.c_str(), packageObj); |
There was a problem hiding this comment.
Do the same here as comments for the napi runtime. Check if the property already exists, if it does prefix with $. also remove the comment.
There was a problem hiding this comment.
Done. The jsi tree checks global.hasProperty first, takes the same $name fallback, and the comment is removed.
Check whether the global already has the package's name and, if it does, expose the package as `$name`, logging a warning. Both the jsi and napi trees now do the same check, so an existing global is never overwritten and no engine-specific read-only handling is needed.
Important
Squash merge this PR. The first commit (f44d92e) describes a read-only-global fallback that review replaced with an existence check. Use this as the squash commit message (title + body), replacing GitHub's default list of commit messages:
Addresses the "Expo-specific special-casing in core runtime code" point in the #77 review.
Summary
Top-level Java package globals no longer overwrite, or depend on, a global the host already defined.
ffi/jni/jsi) used to try the assignment and fall back to$nameonly if it threw. It now checks for the name first.ffi/jni/napi) had no fallback, so a taken name made the package unreachable. It now runs the same check.TNS.Runtime).Evidence
./gradlew :runtime:externalNativeBuildDebug -PonlyArm64 -PbindingLayer=napi, then the same with-PbindingLayer=jsi.CreateTopLevelNamespacesmatches a Java package name. That set isglobal,self,Worker, and on workerspostMessage,close,terminateand__ns__worker. A standalone app therefore gets exactly the names it got before.expo.Merge Danger
Door: two-way
Blast Radius: top-level Java package globals
The only change is for a package whose name is already a global. Before, that package overwrote a writable global, or was unreachable on napi when the global was read-only. Now it appears as
$name.