Visitar URL original
fix(runtime): generate named Java proxies at runtime when not precompiled by NathanWalker · Pull Request #2016 · NativeScript/android · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
fix(runtime): resolve entry-module paths in Java before crossing JNI
runModule now sends every entry through Module.resolveEntryPath: absolute
paths and URLs pass through, and a scheme-less relative name - the
@JavaScriptImplementation convention, ./bundle.mjs by SBG's stamp - goes
through the same resolution require uses, extension and directory probing
included, against the app root. A missing entry throws require's
"Failed to find module" from Java instead of a cwd-relative native stat
failure.

This replaces AnchorToAppRoot. Anchoring inside ModuleInternal::Load
resolved the load but not the identity: Runtime::RunModule probes the
boot backstop with the path it was handed ("Java resolves package.json's
main before handing the path over, so the entry the backstop probes is
the very one that was just evaluated"), so an anchored relative entry
evaluated under the app-root key while the backstop probed the unanchored
one - a registry miss that silently disarmed the hold and the rejection
fatal for exactly the dev-served entries this branch exists to support.
Resolving before the JNI crossing keeps one resolution seam and makes the
evaluated entry and the probed entry the same string by construction.
  • Loading branch information
edusperoni committed Aug 20, 2026
commit d3bbd46b300eeecdb1dde5efa66b943f83b786d5
22 changes: 3 additions & 19 deletions test-app/runtime/src/main/cpp/ModuleInternal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -593,15 +593,6 @@ void ModuleInternal::RequireNativeCallback(const v8::FunctionCallbackInfo<v8::Va
funcPtr(args);
}

static std::string AnchorToAppRoot(const std::string& path, bool isHttpModule) {
if (isHttpModule || path.empty() || path[0] == '/' || path.rfind("file://", 0) == 0 ||
Constants::APP_ROOT_FOLDER_PATH.empty()) {
return path;
}
const std::string relative = path.rfind("./", 0) == 0 ? path.substr(2) : path;
return Constants::APP_ROOT_FOLDER_PATH + relative;
}

void ModuleInternal::Load(Local<Context> context, const string& path) {
TNSPERF();
auto isolate = m_isolate;
Expand All @@ -624,16 +615,9 @@ void ModuleInternal::Load(Local<Context> context, const string& path) {
if (isHttpModule) {
TNS_DEBUG(Esm, "run-module http-esm begin %s", NormalizeHttpModuleUrl(path).c_str());
}
// Callers reach here with a project-relative name as readily as an
// absolute one: `Runtime.createJSInstance` hands over whatever a
// generated binding's @JavaScriptImplementation carries, and that is
// `./bundle.mjs` by SBG convention — a module name in the app-root
// namespace, not a filesystem path. The require branch below resolves
// such names against the app root; the ES module branch stats the
// string as-is (after CanonicalizeRegistryKey folds `./` away), so
// anchor it here or it resolves against the process cwd and fails as
// "Cannot find module bundle.mjs".
LoadESModule(isolate, AnchorToAppRoot(path, isHttpModule), BootEntryEvaluationOptions(isHttpModule));
// The entry runs before this thread's event loop does, so its graph can
// only make progress from the pump inside LoadESModule.
LoadESModule(isolate, path, BootEntryEvaluationOptions(isHttpModule));
if (isHttpModule) {
TNS_DEBUG(Esm, "run-module http-esm ok %s", NormalizeHttpModuleUrl(path).c_str());
}
Expand Down
18 changes: 18 additions & 0 deletions test-app/runtime/src/main/java/com/tns/Module.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,24 @@ static String getApplicationFilesPath() {
return ApplicationFilesPath;
}

/**
* Resolves an entry-module path for runModule. A non-absolute, scheme-less
* name is an app-root-relative module name (the @JavaScriptImplementation
* convention) and goes through the same resolution require uses - extension
* and directory probing included - so the native side only ever receives a
* loadable identity: an absolute path or a URL. Missing entries throw here,
* with require's wording, instead of failing against the process cwd.
*/
static String resolveEntryPath(String path) {
if (path.isEmpty() || path.startsWith("/") || path.contains("://")) {
return path;
}
String relative = (path.startsWith("./") || path.startsWith("../") || path.startsWith("~/"))
? path
: "./" + path;
return resolvePath(relative, ApplicationFilesPath + ModulesFilesPath);
}

@RuntimeCallable
private static String resolvePath(String path, String baseDir) {
// The baseDir is the directory path of the calling module.
Expand Down
3 changes: 1 addition & 2 deletions test-app/runtime/src/main/java/com/tns/Runtime.java
Original file line number Diff line number Diff line change
Expand Up @@ -696,8 +696,7 @@ public void run() throws NativeScriptException {
}

public void runModule(File jsFile) throws NativeScriptException {
String filePath = jsFile.getPath();
runModule(getRuntimeId(), filePath);
runModule(getRuntimeId(), Module.resolveEntryPath(jsFile.getPath()));
}

public Object runScript(File jsFile) throws NativeScriptException {
Expand Down