1

我在嵌入式 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 的多个文件的正确方法是什么?

PS 我的目标是避免使用全局上下文,因为在文档中据说这样访问变量很慢,这就是 main.js 看起来那样的原因。

PPS 我确定 test() 函数无法访问,但我不知道如何编写 js 代码以便一切正常。

PPPS print() 是一个输出到 esp32 串口的 C 函数,它可以工作。甚至 main.js 也可以在没有 test() 函数调用的情况下工作。

4

1 回答 1

1

基本上,您想要的是文件导入功能。您可以通过两种方式实现它:

  1. 在后端提供一个函数并将其导出到 JS,以允许在运行时动态加载文件。
  2. 像在 Node.js 中一样实现模块处理(本质上也可以归结为导入函数)。

第二个想法是最常用的,它实现了一个定义明确的方法来在你的 JS 应用程序中包含其他文件。Duktape 附带了一个实现该require命令的额外文件,就像在 Node.js 中一样。您只需提供自己的函数来解析模块并从磁盘加载它(因为 duktape 不支持文件 I/O)。

我在 MySQL Workbench的 MGA 工具中实现了这种方法。用于实现节点模块处理的 duktape 文件在此处。解析模块的功能(包括处理嵌套node_modules文件夹等)在ScriptingContext类中实现。它的相关部分是这样的:

/**
 * 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.
}
于 2020-05-27T07:16:15.050 回答