From e29e29bcb33692974a18b68f332a59374638c8f1 Mon Sep 17 00:00:00 2001 From: Hisham Muhammad Date: Tue, 5 Sep 2023 12:16:04 -0300 Subject: [PATCH] fix: populate `...` for required modules (#693) * fix: populate `...` for required modules Fixes #689. * adjust code to more closely match documented semantics Instead of hardcoding the arguments within the loader, pass them through the searcher, as specified by the documented behavior: https://github.com/teal-language/tl/issues/689#issuecomment-1705914900 > quoted from https://www.lua.org/manual/5.4/manual.html#pdf-require > >> Once a loader is found, require calls the loader with two arguments: >> modname and an extra value, a loader data, also returned by the >> searcher. The loader data can be any value useful to the module; >> for the default searchers, it indicates where the loader was found. >> (For instance, if the loader came from a file, this extra value is >> the file path.) Thanks @fperrad for the research! * loader args: provide consistent behavior across Lua versions This implements a compromise solution that allows Teal modules to always get both arguments, even in Lua 5.1 based systems (this should be harmless for modules which don't expect a second argument there), without hardcoding `loader_data`, just in case the user is on an environment that runs a patched `require` for debug reasons or something. --- spec/cli/run_spec.lua | 22 ++++++++++++++++++++++ tl.lua | 10 +++++----- tl.tl | 15 +++++++++------ 3 files changed, 36 insertions(+), 11 deletions(-) diff --git a/spec/cli/run_spec.lua b/spec/cli/run_spec.lua index e89c37b1a..26f9d7335 100644 --- a/spec/cli/run_spec.lua +++ b/spec/cli/run_spec.lua @@ -93,6 +93,28 @@ describe("tl run", function() ]], output) end) + it("passes standard arguments to required chunks", function() + local dir_name = util.write_tmp_dir(finally, { + ["ld.tl"] = [[ + require("foo") + print("Done") + ]], + ["foo.tl"] = [[ + print(...) + ]] + }) + local pd, output + util.do_in(dir_name, function() + pd = io.popen(util.tl_cmd("run", "ld.tl"), "r") + output = pd:read("*a") + end) + util.assert_popen_close(0, pd:close()) + util.assert_line_by_line(util.os_path([[ + foo ./foo.tl + Done + ]]), output) + end) + describe("-l --require", function() it("can require a module from the CLI like Lua", function() local dir_name = util.write_tmp_dir(finally, { diff --git a/tl.lua b/tl.lua index d9ad5d808..3db88c27b 100644 --- a/tl.lua +++ b/tl.lua @@ -5487,14 +5487,14 @@ local function init_globals(lax) }), ["loaders"] = a_type({ typename = "array", - elements = a_type({ typename = "function", args = TUPLE({ STRING }), rets = TUPLE({ ANY }) }), + elements = a_type({ typename = "function", args = TUPLE({ STRING }), rets = TUPLE({ ANY, ANY }) }), }), ["loadlib"] = a_type({ typename = "function", args = TUPLE({ STRING, STRING }), rets = TUPLE({ FUNCTION }) }), ["path"] = STRING, ["preload"] = TABLE, ["searchers"] = a_type({ typename = "array", - elements = a_type({ typename = "function", args = TUPLE({ STRING }), rets = TUPLE({ ANY }) }), + elements = a_type({ typename = "function", args = TUPLE({ STRING }), rets = TUPLE({ ANY, ANY }) }), }), ["searchpath"] = a_type({ typename = "function", args = TUPLE({ STRING, STRING, OPT(STRING), OPT(STRING) }), rets = TUPLE({ STRING, STRING }) }), }, @@ -10824,11 +10824,11 @@ local function tl_package_loader(module_name) local code = assert(tl.pretty_print_ast(program, env.gen_target, true)) local chunk, err = load(code, "@" .. found_filename, "t") if chunk then - return function() - local ret = chunk() + return function(modname, loader_data) + local ret = chunk(modname, loader_data) package.loaded[module_name] = ret return ret - end + end, found_filename else error("Internal Compiler Error: Teal generator produced invalid Lua. Please report a bug at https://github.com/teal-language/tl\n\n" .. err) end diff --git a/tl.tl b/tl.tl index 8b6b43a99..2e79e1fd8 100644 --- a/tl.tl +++ b/tl.tl @@ -5487,14 +5487,14 @@ local function init_globals(lax: boolean): {string:Variable}, {string:Type} }, ["loaders"] = a_type { typename = "array", - elements = a_type { typename = "function", args = TUPLE { STRING }, rets = TUPLE { ANY } } + elements = a_type { typename = "function", args = TUPLE { STRING }, rets = TUPLE { ANY, ANY } } }, ["loadlib"] = a_type { typename = "function", args = TUPLE { STRING, STRING }, rets = TUPLE { FUNCTION } }, ["path"] = STRING, ["preload"] = TABLE, ["searchers"] = a_type { typename = "array", - elements = a_type { typename = "function", args = TUPLE { STRING }, rets = TUPLE { ANY } } + elements = a_type { typename = "function", args = TUPLE { STRING }, rets = TUPLE { ANY, ANY } } }, ["searchpath"] = a_type { typename = "function", args = TUPLE { STRING, STRING, OPT(STRING), OPT(STRING) }, rets = TUPLE { STRING, STRING } }, }, @@ -10791,7 +10791,7 @@ tl.gen = function(input: string, env: Env): string, Result return code, result end -local function tl_package_loader(module_name: string): any +local function tl_package_loader(module_name: string): any, any local found_filename, fd, tried = tl.search_module(module_name, false) if found_filename then local input = read_full_file(fd) @@ -10824,11 +10824,14 @@ local function tl_package_loader(module_name: string): any local code = assert(tl.pretty_print_ast(program, env.gen_target, true)) local chunk, err = load(code, "@" .. found_filename, "t") if chunk then - return function(): any - local ret = chunk() + return function(modname: string, loader_data: string): any + if loader_data == nil then + loader_data = found_filename + end + local ret = chunk(modname, loader_data) package.loaded[module_name] = ret return ret - end + end, found_filename else error("Internal Compiler Error: Teal generator produced invalid Lua. Please report a bug at https://github.com/teal-language/tl\n\n" .. err) end