如何在 Duktape 中使用多个 js 文件?
How to use multiple js files with Duktape?
我在嵌入式 MCU 中使用 Duktape。对于测试用例,我有:
main.js 文件:
(function(){
test();
})();
test.js 文件:
(function test(){
print("func");
})
都编译为全局默认代码,main.js 执行时使用 duk_call(ctx, 0);
问题是它在调用 test() 函数时抛出错误。
我也试过只用
function test() {
print("test");
}
在 test.js 代码中,但它也不起作用。
据我了解,这两个文件都有单独的执行上下文。这就是功能无法访问的原因。
但是将代码拆分为多个 Duktape 文件的正确方法是什么?
P.S。我的目标是避免使用全局上下文,因为在文档中据说以这种方式访问变量很慢,这就是为什么 main.js 看起来那样。
P.P.S。我确定 test() 函数无法访问,但我不知道如何编写 js 代码才能使一切正常。
P.P.P.S print() 是一个 C 函数,它输出到 esp32 的串行端口并且它可以工作。即使 main.js 也可以在没有 test() 函数调用的情况下工作。
基本上,您需要的是文件导入功能。您可以通过两种方式实现它:
- 在后端提供一个函数并将其导出到 JS,以允许在运行时动态加载文件。
- 像 Node.js 一样实现模块处理(本质上也归结为一个导入函数)。
第二个想法是最常用的,它实现了一种定义明确的方法来将其他文件包含在您的 JS 应用程序中。 Duktape 带有一个额外的文件,它实现了 require
命令,就像在 Node.js 中一样。您只需提供自己的函数来解析模块并从磁盘加载它(因为 duktape 没有文件 I/O 支持)。
我在 MGA tool in MySQL Workbench. The duktape file for implementing node module handling is here. The function to resolve modules (which includes handling of nested node_modules
folders etc.) is implemented in the ScriptingContext class 中实现了这种方法。它的相关部分是这样的:
/**
* Part of the module loading machinery. JS interfacing is done by the duk_module_node code.
* But we have to do the file work here. On the stack we get the value passed to `require()` as a "module ID" and
* the ID of the calling script (which is empty for the main script).
*/
duk_ret_t ScriptingContext::resolveModule(duk_context *ctx) {
// stack: [ requested_id parent_id ]
std::string requestedID = duk_get_string(ctx, 0);
std::string callingID = duk_get_string(ctx, 1);
std::string parentPath = FS::isDir(callingID) ? callingID : Path::dirname(callingID);
// Module resolution strategy in Node.js style: https://nodejs.org/api/modules.html#modules_all_together
auto modules = getInternalModules();
if (modules.find(requestedID) != modules.end()) {
duk_push_string(ctx, requestedID.c_str());
return 1;
}
ScriptingContext *context = ScriptingContext::fromDuktapeContext(ctx);
std::string resolvedID;
std::string cwd = Process::cwd();
try {
if (Path::isAbsolute(requestedID) || Utilities::hasPrefix(requestedID, ".")) {
std::string temp;
if (Path::isAbsolute(requestedID)) {
temp = Path::relative(cwd, requestedID);
} else
temp = Path::join({ parentPath, requestedID });
resolvedID = resolveFile(temp);
if (resolvedID.empty())
resolvedID = resolveFolder(context, temp);
}
} catch (std::runtime_error &e) {
// Triggered for parse errors in package.json.
context->throwScriptingError(ScriptingError::Syntax, e.what());
return 0;
}
// No files found so far. Check node modules.
if (resolvedID.empty()) {
for (auto &folder : moduleFolders(parentPath)) {
std::string path = Path::join({ folder, requestedID });
std::string temp = resolveFile(path);
if (!temp.empty()) {
resolvedID = temp;
break;
}
temp = resolveFolder(context, path);
if (!temp.empty()) {
resolvedID = temp;
break;
}
}
}
if (resolvedID.empty()) {
context->throwScriptingError(ScriptingError::Error, Utilities::format("Cannot resolve module %s", requestedID.c_str()));
return 0;
}
duk_push_string(ctx, resolvedID.c_str());
return 1; // Use result on stack.
}
我在嵌入式 MCU 中使用 Duktape。对于测试用例,我有: main.js 文件:
(function(){
test();
})();
test.js 文件:
(function test(){
print("func");
})
都编译为全局默认代码,main.js 执行时使用 duk_call(ctx, 0);
问题是它在调用 test() 函数时抛出错误。
我也试过只用
function test() {
print("test");
}
在 test.js 代码中,但它也不起作用。
据我了解,这两个文件都有单独的执行上下文。这就是功能无法访问的原因。
但是将代码拆分为多个 Duktape 文件的正确方法是什么?
P.S。我的目标是避免使用全局上下文,因为在文档中据说以这种方式访问变量很慢,这就是为什么 main.js 看起来那样。
P.P.S。我确定 test() 函数无法访问,但我不知道如何编写 js 代码才能使一切正常。
P.P.P.S print() 是一个 C 函数,它输出到 esp32 的串行端口并且它可以工作。即使 main.js 也可以在没有 test() 函数调用的情况下工作。
基本上,您需要的是文件导入功能。您可以通过两种方式实现它:
- 在后端提供一个函数并将其导出到 JS,以允许在运行时动态加载文件。
- 像 Node.js 一样实现模块处理(本质上也归结为一个导入函数)。
第二个想法是最常用的,它实现了一种定义明确的方法来将其他文件包含在您的 JS 应用程序中。 Duktape 带有一个额外的文件,它实现了 require
命令,就像在 Node.js 中一样。您只需提供自己的函数来解析模块并从磁盘加载它(因为 duktape 没有文件 I/O 支持)。
我在 MGA tool in MySQL Workbench. The duktape file for implementing node module handling is here. The function to resolve modules (which includes handling of nested node_modules
folders etc.) is implemented in the ScriptingContext class 中实现了这种方法。它的相关部分是这样的:
/**
* Part of the module loading machinery. JS interfacing is done by the duk_module_node code.
* But we have to do the file work here. On the stack we get the value passed to `require()` as a "module ID" and
* the ID of the calling script (which is empty for the main script).
*/
duk_ret_t ScriptingContext::resolveModule(duk_context *ctx) {
// stack: [ requested_id parent_id ]
std::string requestedID = duk_get_string(ctx, 0);
std::string callingID = duk_get_string(ctx, 1);
std::string parentPath = FS::isDir(callingID) ? callingID : Path::dirname(callingID);
// Module resolution strategy in Node.js style: https://nodejs.org/api/modules.html#modules_all_together
auto modules = getInternalModules();
if (modules.find(requestedID) != modules.end()) {
duk_push_string(ctx, requestedID.c_str());
return 1;
}
ScriptingContext *context = ScriptingContext::fromDuktapeContext(ctx);
std::string resolvedID;
std::string cwd = Process::cwd();
try {
if (Path::isAbsolute(requestedID) || Utilities::hasPrefix(requestedID, ".")) {
std::string temp;
if (Path::isAbsolute(requestedID)) {
temp = Path::relative(cwd, requestedID);
} else
temp = Path::join({ parentPath, requestedID });
resolvedID = resolveFile(temp);
if (resolvedID.empty())
resolvedID = resolveFolder(context, temp);
}
} catch (std::runtime_error &e) {
// Triggered for parse errors in package.json.
context->throwScriptingError(ScriptingError::Syntax, e.what());
return 0;
}
// No files found so far. Check node modules.
if (resolvedID.empty()) {
for (auto &folder : moduleFolders(parentPath)) {
std::string path = Path::join({ folder, requestedID });
std::string temp = resolveFile(path);
if (!temp.empty()) {
resolvedID = temp;
break;
}
temp = resolveFolder(context, path);
if (!temp.empty()) {
resolvedID = temp;
break;
}
}
}
if (resolvedID.empty()) {
context->throwScriptingError(ScriptingError::Error, Utilities::format("Cannot resolve module %s", requestedID.c_str()));
return 0;
}
duk_push_string(ctx, resolvedID.c_str());
return 1; // Use result on stack.
}