Hide shell functions from _G using upvalues on the main chunk
Store __command, __interactive, __getenv, __setenv as upvalues populated by lua_load() from the registry, keeping them invisible to user code while accessible to the parser's codegen.
This commit is contained in:
22
lapi.c
22
lapi.c
@@ -29,6 +29,7 @@
|
|||||||
#include "ltm.h"
|
#include "ltm.h"
|
||||||
#include "lundump.h"
|
#include "lundump.h"
|
||||||
#include "lvm.h"
|
#include "lvm.h"
|
||||||
|
#include "lcmd.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1135,6 +1136,27 @@ LUA_API int lua_load (lua_State *L, lua_Reader reader, void *data,
|
|||||||
setobj(L, f->upvals[0]->v.p, >);
|
setobj(L, f->upvals[0]->v.p, >);
|
||||||
luaC_barrier(L, f->upvals[0], >);
|
luaC_barrier(L, f->upvals[0], >);
|
||||||
}
|
}
|
||||||
|
if (f->nupvalues >= LUSH_NUM_UPVALS) { /* has lush upvalues? */
|
||||||
|
/* populate shell function upvalues from registry */
|
||||||
|
Table *regt = hvalue(&G(L)->l_registry);
|
||||||
|
TValue lushtv;
|
||||||
|
lu_byte tag = luaH_getint(regt, LUA_RIDX_LUSH, &lushtv);
|
||||||
|
if (novariant(tag) == LUA_TTABLE) {
|
||||||
|
static const char *const fields[] = {
|
||||||
|
"command", "interactive", "getenv", "setenv"
|
||||||
|
};
|
||||||
|
Table *lusht = hvalue(&lushtv);
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < 4; i++) {
|
||||||
|
TValue val;
|
||||||
|
TString *key = luaS_new(L, fields[i]);
|
||||||
|
if (luaH_getstr(lusht, key, &val) != LUA_TNIL) {
|
||||||
|
setobj(L, f->upvals[i + 1]->v.p, &val);
|
||||||
|
luaC_barrier(L, f->upvals[i + 1], &val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
lua_unlock(L);
|
lua_unlock(L);
|
||||||
return APIstatus(status);
|
return APIstatus(status);
|
||||||
|
|||||||
@@ -155,5 +155,9 @@ void luaopen_builtins (lua_State *L) {
|
|||||||
lua_setfield(L, -2, "exec");
|
lua_setfield(L, -2, "exec");
|
||||||
lua_pushcfunction(L, builtin_umask);
|
lua_pushcfunction(L, builtin_umask);
|
||||||
lua_setfield(L, -2, "umask");
|
lua_setfield(L, -2, "umask");
|
||||||
lua_setglobal(L, "__builtins");
|
/* Store as __lush.builtins in registry */
|
||||||
|
lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_LUSH);
|
||||||
|
lua_insert(L, -2);
|
||||||
|
lua_setfield(L, -2, "builtins");
|
||||||
|
lua_pop(L, 1);
|
||||||
}
|
}
|
||||||
|
|||||||
47
lcmd.c
47
lcmd.c
@@ -874,15 +874,21 @@ static int exec_pipeline (lua_State *L, char **stages, int nstages,
|
|||||||
*/
|
*/
|
||||||
static int try_builtin (lua_State *L, ParsedArgs *pa) {
|
static int try_builtin (lua_State *L, ParsedArgs *pa) {
|
||||||
int i;
|
int i;
|
||||||
if (lua_getglobal(L, "__builtins") != LUA_TTABLE) {
|
lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_LUSH);
|
||||||
|
if (!lua_istable(L, -1)) {
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (lua_getfield(L, -1, pa->argv[0]) != LUA_TFUNCTION) {
|
if (lua_getfield(L, -1, "builtins") != LUA_TTABLE) {
|
||||||
lua_pop(L, 2);
|
lua_pop(L, 2);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
lua_remove(L, -2); /* remove __builtins, keep function */
|
if (lua_getfield(L, -1, pa->argv[0]) != LUA_TFUNCTION) {
|
||||||
|
lua_pop(L, 3);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
lua_remove(L, -2); /* remove builtins table */
|
||||||
|
lua_remove(L, -2); /* remove lush table */
|
||||||
for (i = 0; i < pa->argc; i++)
|
for (i = 0; i < pa->argc; i++)
|
||||||
lua_pushstring(L, pa->argv[i]);
|
lua_pushstring(L, pa->argv[i]);
|
||||||
lua_call(L, pa->argc, 1);
|
lua_call(L, pa->argc, 1);
|
||||||
@@ -890,9 +896,9 @@ static int try_builtin (lua_State *L, ParsedArgs *pa) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ===== luaB_command ===== */
|
/* ===== lushCmd_command ===== */
|
||||||
|
|
||||||
int luaB_command (lua_State *L) {
|
int lushCmd_command (lua_State *L) {
|
||||||
const char *cmd = luaL_checkstring(L, 1);
|
const char *cmd = luaL_checkstring(L, 1);
|
||||||
char *stages[MAX_PIPELINE_STAGES];
|
char *stages[MAX_PIPELINE_STAGES];
|
||||||
int nstages;
|
int nstages;
|
||||||
@@ -1033,9 +1039,9 @@ int luaB_command (lua_State *L) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ===== luaB_interactive ===== */
|
/* ===== lushCmd_interactive ===== */
|
||||||
|
|
||||||
int luaB_interactive (lua_State *L) {
|
int lushCmd_interactive (lua_State *L) {
|
||||||
const char *cmd = luaL_checkstring(L, 1);
|
const char *cmd = luaL_checkstring(L, 1);
|
||||||
char *stages[MAX_PIPELINE_STAGES];
|
char *stages[MAX_PIPELINE_STAGES];
|
||||||
int nstages;
|
int nstages;
|
||||||
@@ -1142,3 +1148,30 @@ int luaB_interactive (lua_State *L) {
|
|||||||
|
|
||||||
return 0; /* void — no Lua return values */
|
return 0; /* void — no Lua return values */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ===== lushCmd_getenv ===== */
|
||||||
|
|
||||||
|
int lushCmd_getenv (lua_State *L) {
|
||||||
|
const char *name = luaL_checkstring(L, 1);
|
||||||
|
const char *val = getenv(name);
|
||||||
|
if (val == NULL)
|
||||||
|
lua_pushnil(L);
|
||||||
|
else
|
||||||
|
lua_pushstring(L, val);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ===== lushCmd_setenv ===== */
|
||||||
|
|
||||||
|
int lushCmd_setenv (lua_State *L) {
|
||||||
|
const char *name = luaL_checkstring(L, 1);
|
||||||
|
if (lua_isnoneornil(L, 2))
|
||||||
|
unsetenv(name);
|
||||||
|
else {
|
||||||
|
const char *val = luaL_tolstring(L, 2, NULL);
|
||||||
|
setenv(name, val, 1);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|||||||
14
lcmd.h
14
lcmd.h
@@ -9,7 +9,17 @@
|
|||||||
|
|
||||||
#include "lua.h"
|
#include "lua.h"
|
||||||
|
|
||||||
int luaB_command (lua_State *L);
|
/* Upvalue indices for shell functions on the main chunk.
|
||||||
int luaB_interactive (lua_State *L);
|
** Index 0 is _ENV (standard). Indices 1-4 are lush shell functions. */
|
||||||
|
#define LUSH_UPVAL_COMMAND 1
|
||||||
|
#define LUSH_UPVAL_INTERACTIVE 2
|
||||||
|
#define LUSH_UPVAL_GETENV 3
|
||||||
|
#define LUSH_UPVAL_SETENV 4
|
||||||
|
#define LUSH_NUM_UPVALS 5 /* total: _ENV(0) + 4 shell functions */
|
||||||
|
|
||||||
|
int lushCmd_command (lua_State *L);
|
||||||
|
int lushCmd_interactive (lua_State *L);
|
||||||
|
int lushCmd_getenv (lua_State *L);
|
||||||
|
int lushCmd_setenv (lua_State *L);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
44
linit.c
44
linit.c
@@ -43,41 +43,21 @@ static const luaL_Reg stdlibs[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
static int luaB_getenv (lua_State *L) {
|
|
||||||
const char *name = luaL_checkstring(L, 1);
|
|
||||||
const char *val = getenv(name);
|
|
||||||
if (val == NULL)
|
|
||||||
lua_pushnil(L);
|
|
||||||
else
|
|
||||||
lua_pushstring(L, val);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int luaB_setenv (lua_State *L) {
|
|
||||||
const char *name = luaL_checkstring(L, 1);
|
|
||||||
if (lua_isnoneornil(L, 2))
|
|
||||||
unsetenv(name);
|
|
||||||
else {
|
|
||||||
const char *val = luaL_tolstring(L, 2, NULL);
|
|
||||||
setenv(name, val, 1);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Register shell built-in globals (called after standard libraries).
|
** Store shell functions in a registry table at LUA_RIDX_LUSH.
|
||||||
|
** These are populated as upvalues on the main chunk by lua_load().
|
||||||
*/
|
*/
|
||||||
static void opencommand (lua_State *L) {
|
static void opencommand (lua_State *L) {
|
||||||
lua_pushcfunction(L, luaB_command);
|
lua_createtable(L, 0, 5);
|
||||||
lua_setglobal(L, "__command");
|
lua_pushcfunction(L, lushCmd_command);
|
||||||
lua_pushcfunction(L, luaB_interactive);
|
lua_setfield(L, -2, "command");
|
||||||
lua_setglobal(L, "__interactive");
|
lua_pushcfunction(L, lushCmd_interactive);
|
||||||
lua_pushcfunction(L, luaB_getenv);
|
lua_setfield(L, -2, "interactive");
|
||||||
lua_setglobal(L, "__getenv");
|
lua_pushcfunction(L, lushCmd_getenv);
|
||||||
lua_pushcfunction(L, luaB_setenv);
|
lua_setfield(L, -2, "getenv");
|
||||||
lua_setglobal(L, "__setenv");
|
lua_pushcfunction(L, lushCmd_setenv);
|
||||||
|
lua_setfield(L, -2, "setenv");
|
||||||
|
lua_rawseti(L, LUA_REGISTRYINDEX, LUA_RIDX_LUSH);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
45
lparser.c
45
lparser.c
@@ -27,6 +27,7 @@
|
|||||||
#include "lstate.h"
|
#include "lstate.h"
|
||||||
#include "lstring.h"
|
#include "lstring.h"
|
||||||
#include "ltable.h"
|
#include "ltable.h"
|
||||||
|
#include "lcmd.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -499,6 +500,19 @@ static void singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Look up a lush shell function (e.g. __command) that is registered
|
||||||
|
** as an upvalue on the main chunk. singlevaraux will find it directly
|
||||||
|
** as VUPVAL, emitting OP_GETUPVAL instead of OP_GETTABUP.
|
||||||
|
*/
|
||||||
|
static void buildlushvar (LexState *ls, TString *varname, expdesc *var) {
|
||||||
|
FuncState *fs = ls->fs;
|
||||||
|
init_exp(var, VGLOBAL, -1);
|
||||||
|
singlevaraux(fs, varname, var, 1);
|
||||||
|
lua_assert(var->k == VUPVAL); /* must be found as upvalue */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void buildglobal (LexState *ls, TString *varname, expdesc *var) {
|
static void buildglobal (LexState *ls, TString *varname, expdesc *var) {
|
||||||
FuncState *fs = ls->fs;
|
FuncState *fs = ls->fs;
|
||||||
expdesc key;
|
expdesc key;
|
||||||
@@ -552,8 +566,8 @@ static void codecommand (LexState *ls, expdesc *v, expdesc *cmdstr) {
|
|||||||
expdesc func;
|
expdesc func;
|
||||||
TString *cmdname = luaX_newstring(ls, "__command", 9);
|
TString *cmdname = luaX_newstring(ls, "__command", 9);
|
||||||
line = ls->linenumber;
|
line = ls->linenumber;
|
||||||
/* look up __command as _ENV["__command"] */
|
/* look up __command as upvalue */
|
||||||
buildglobal(ls, cmdname, &func);
|
buildlushvar(ls, cmdname, &func);
|
||||||
luaK_exp2nextreg(fs, &func);
|
luaK_exp2nextreg(fs, &func);
|
||||||
base = func.u.info;
|
base = func.u.info;
|
||||||
/* push the command string argument */
|
/* push the command string argument */
|
||||||
@@ -1243,7 +1257,7 @@ static void commandexp (LexState *ls, expdesc *v) {
|
|||||||
TString *cmdname = luaX_newstring(ls, "__command", 9);
|
TString *cmdname = luaX_newstring(ls, "__command", 9);
|
||||||
TString *tsname = luaX_newstring(ls, "tostring", 8);
|
TString *tsname = luaX_newstring(ls, "tostring", 8);
|
||||||
/* load __command function first so it occupies the base register */
|
/* load __command function first so it occupies the base register */
|
||||||
buildglobal(ls, cmdname, &func);
|
buildlushvar(ls, cmdname, &func);
|
||||||
luaK_exp2nextreg(fs, &func);
|
luaK_exp2nextreg(fs, &func);
|
||||||
base = func.u.info;
|
base = func.u.info;
|
||||||
/* load the first fragment */
|
/* load the first fragment */
|
||||||
@@ -1308,7 +1322,7 @@ static void codeenvget (LexState *ls, expdesc *v, TString *name) {
|
|||||||
expdesc func, arg;
|
expdesc func, arg;
|
||||||
TString *fname = luaX_newstring(ls, "__getenv", 8);
|
TString *fname = luaX_newstring(ls, "__getenv", 8);
|
||||||
line = ls->linenumber;
|
line = ls->linenumber;
|
||||||
buildglobal(ls, fname, &func);
|
buildlushvar(ls, fname, &func);
|
||||||
luaK_exp2nextreg(fs, &func);
|
luaK_exp2nextreg(fs, &func);
|
||||||
base = func.u.info;
|
base = func.u.info;
|
||||||
codestring(&arg, name);
|
codestring(&arg, name);
|
||||||
@@ -2193,7 +2207,7 @@ static void envstat (LexState *ls) {
|
|||||||
luaX_next(ls); /* skip TK_ENVVAR */
|
luaX_next(ls); /* skip TK_ENVVAR */
|
||||||
checknext(ls, '=');
|
checknext(ls, '=');
|
||||||
fname = luaX_newstring(ls, "__setenv", 8);
|
fname = luaX_newstring(ls, "__setenv", 8);
|
||||||
buildglobal(ls, fname, &func);
|
buildlushvar(ls, fname, &func);
|
||||||
luaK_exp2nextreg(fs, &func);
|
luaK_exp2nextreg(fs, &func);
|
||||||
base = func.u.info;
|
base = func.u.info;
|
||||||
codestring(&arg, name);
|
codestring(&arg, name);
|
||||||
@@ -2219,7 +2233,7 @@ static void interactivestat (LexState *ls) {
|
|||||||
expdesc func, cmdstr, v;
|
expdesc func, cmdstr, v;
|
||||||
TString *fname = luaX_newstring(ls, "__interactive", 13);
|
TString *fname = luaX_newstring(ls, "__interactive", 13);
|
||||||
line = ls->linenumber;
|
line = ls->linenumber;
|
||||||
buildglobal(ls, fname, &func);
|
buildlushvar(ls, fname, &func);
|
||||||
luaK_exp2nextreg(fs, &func);
|
luaK_exp2nextreg(fs, &func);
|
||||||
base = func.u.info;
|
base = func.u.info;
|
||||||
codestring(&cmdstr, ls->t.seminfo.ts);
|
codestring(&cmdstr, ls->t.seminfo.ts);
|
||||||
@@ -2236,7 +2250,7 @@ static void interactivestat (LexState *ls) {
|
|||||||
int line = ls->linenumber;
|
int line = ls->linenumber;
|
||||||
TString *fname = luaX_newstring(ls, "__interactive", 13);
|
TString *fname = luaX_newstring(ls, "__interactive", 13);
|
||||||
TString *tsname = luaX_newstring(ls, "tostring", 8);
|
TString *tsname = luaX_newstring(ls, "tostring", 8);
|
||||||
buildglobal(ls, fname, &func);
|
buildlushvar(ls, fname, &func);
|
||||||
luaK_exp2nextreg(fs, &func);
|
luaK_exp2nextreg(fs, &func);
|
||||||
base = func.u.info;
|
base = func.u.info;
|
||||||
/* load the first fragment */
|
/* load the first fragment */
|
||||||
@@ -2396,8 +2410,12 @@ static void statement (LexState *ls) {
|
|||||||
** upvalue named LUA_ENV
|
** upvalue named LUA_ENV
|
||||||
*/
|
*/
|
||||||
static void mainfunc (LexState *ls, FuncState *fs) {
|
static void mainfunc (LexState *ls, FuncState *fs) {
|
||||||
|
static const char *const lush_upval_names[] = {
|
||||||
|
"__command", "__interactive", "__getenv", "__setenv"
|
||||||
|
};
|
||||||
BlockCnt bl;
|
BlockCnt bl;
|
||||||
Upvaldesc *env;
|
Upvaldesc *env;
|
||||||
|
int i;
|
||||||
open_func(ls, fs, &bl);
|
open_func(ls, fs, &bl);
|
||||||
setvararg(fs, PF_ISVARARG); /* main function is always vararg */
|
setvararg(fs, PF_ISVARARG); /* main function is always vararg */
|
||||||
env = allocupvalue(fs); /* ...set environment upvalue */
|
env = allocupvalue(fs); /* ...set environment upvalue */
|
||||||
@@ -2406,6 +2424,15 @@ static void mainfunc (LexState *ls, FuncState *fs) {
|
|||||||
env->kind = VDKREG;
|
env->kind = VDKREG;
|
||||||
env->name = ls->envn;
|
env->name = ls->envn;
|
||||||
luaC_objbarrier(ls->L, fs->f, env->name);
|
luaC_objbarrier(ls->L, fs->f, env->name);
|
||||||
|
/* allocate upvalues for lush shell functions */
|
||||||
|
for (i = 0; i < 4; i++) {
|
||||||
|
Upvaldesc *uv = allocupvalue(fs);
|
||||||
|
uv->instack = 1;
|
||||||
|
uv->idx = 0;
|
||||||
|
uv->kind = VDKREG;
|
||||||
|
uv->name = luaS_new(ls->L, lush_upval_names[i]);
|
||||||
|
luaC_objbarrier(ls->L, fs->f, uv->name);
|
||||||
|
}
|
||||||
luaX_next(ls); /* read first token */
|
luaX_next(ls); /* read first token */
|
||||||
statlist(ls); /* parse main body */
|
statlist(ls); /* parse main body */
|
||||||
check(ls, TK_EOS);
|
check(ls, TK_EOS);
|
||||||
@@ -2417,7 +2444,7 @@ LClosure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff,
|
|||||||
Dyndata *dyd, const char *name, int firstchar) {
|
Dyndata *dyd, const char *name, int firstchar) {
|
||||||
LexState lexstate;
|
LexState lexstate;
|
||||||
FuncState funcstate;
|
FuncState funcstate;
|
||||||
LClosure *cl = luaF_newLclosure(L, 1); /* create main closure */
|
LClosure *cl = luaF_newLclosure(L, LUSH_NUM_UPVALS); /* create main closure */
|
||||||
setclLvalue2s(L, L->top.p, cl); /* anchor it (to avoid being collected) */
|
setclLvalue2s(L, L->top.p, cl); /* anchor it (to avoid being collected) */
|
||||||
luaD_inctop(L);
|
luaD_inctop(L);
|
||||||
lexstate.h = luaH_new(L); /* create table for scanner */
|
lexstate.h = luaH_new(L); /* create table for scanner */
|
||||||
@@ -2432,7 +2459,7 @@ LClosure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff,
|
|||||||
dyd->actvar.n = dyd->gt.n = dyd->label.n = 0;
|
dyd->actvar.n = dyd->gt.n = dyd->label.n = 0;
|
||||||
luaX_setinput(L, &lexstate, z, funcstate.f->source, firstchar);
|
luaX_setinput(L, &lexstate, z, funcstate.f->source, firstchar);
|
||||||
mainfunc(&lexstate, &funcstate);
|
mainfunc(&lexstate, &funcstate);
|
||||||
lua_assert(!funcstate.prev && funcstate.nups == 1 && !lexstate.fs);
|
lua_assert(!funcstate.prev && funcstate.nups == LUSH_NUM_UPVALS && !lexstate.fs);
|
||||||
/* all scopes should be correctly finished */
|
/* all scopes should be correctly finished */
|
||||||
lua_assert(dyd->actvar.n == 0 && dyd->gt.n == 0 && dyd->label.n == 0);
|
lua_assert(dyd->actvar.n == 0 && dyd->gt.n == 0 && dyd->label.n == 0);
|
||||||
L->top.p--; /* remove scanner's table */
|
L->top.p--; /* remove scanner's table */
|
||||||
|
|||||||
3
lua.h
3
lua.h
@@ -83,7 +83,8 @@ typedef struct lua_State lua_State;
|
|||||||
/* index 1 is reserved for the reference mechanism */
|
/* index 1 is reserved for the reference mechanism */
|
||||||
#define LUA_RIDX_GLOBALS 2
|
#define LUA_RIDX_GLOBALS 2
|
||||||
#define LUA_RIDX_MAINTHREAD 3
|
#define LUA_RIDX_MAINTHREAD 3
|
||||||
#define LUA_RIDX_LAST 3
|
#define LUA_RIDX_LUSH 4
|
||||||
|
#define LUA_RIDX_LAST LUA_RIDX_LUSH
|
||||||
|
|
||||||
|
|
||||||
/* type of numbers in Lua */
|
/* type of numbers in Lua */
|
||||||
|
|||||||
@@ -3,13 +3,10 @@
|
|||||||
|
|
||||||
print "testing shell builtins"
|
print "testing shell builtins"
|
||||||
|
|
||||||
-- === __builtins table ===
|
-- === __builtins is NOT in _G (internal, stored in registry) ===
|
||||||
|
|
||||||
do
|
do
|
||||||
assert(type(__builtins) == "table")
|
assert(__builtins == nil, "__builtins should not be in _G")
|
||||||
assert(type(__builtins.cd) == "function")
|
|
||||||
assert(type(__builtins.exec) == "function")
|
|
||||||
assert(type(__builtins.umask) == "function")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -117,9 +114,9 @@ end
|
|||||||
|
|
||||||
-- === exec ===
|
-- === exec ===
|
||||||
|
|
||||||
-- exec with no command returns error
|
-- exec with no command returns error (tested via backtick)
|
||||||
do
|
do
|
||||||
local r = __builtins.exec("exec")
|
local r = `exec`
|
||||||
assert(r.code == 1)
|
assert(r.code == 1)
|
||||||
assert(r.stderr:find("command required"),
|
assert(r.stderr:find("command required"),
|
||||||
"expected 'command required', got: " .. r.stderr)
|
"expected 'command required', got: " .. r.stderr)
|
||||||
@@ -127,7 +124,7 @@ end
|
|||||||
|
|
||||||
-- exec nonexistent command returns error
|
-- exec nonexistent command returns error
|
||||||
do
|
do
|
||||||
local r = __builtins.exec("exec", "nonexistent_cmd_xyz_999")
|
local r = `exec nonexistent_cmd_xyz_999`
|
||||||
assert(r.code == 1)
|
assert(r.code == 1)
|
||||||
assert(r.stderr:find("exec:"),
|
assert(r.stderr:find("exec:"),
|
||||||
"expected exec error, got: " .. r.stderr)
|
"expected exec error, got: " .. r.stderr)
|
||||||
@@ -140,21 +137,4 @@ do
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
-- === builtins are overridable ===
|
|
||||||
|
|
||||||
do
|
|
||||||
local old_cd = __builtins.cd
|
|
||||||
local called = false
|
|
||||||
__builtins.cd = function(...)
|
|
||||||
called = true
|
|
||||||
return old_cd(...)
|
|
||||||
end
|
|
||||||
local before = `pwd`.stdout:gsub("\n$", "")
|
|
||||||
`cd /tmp`
|
|
||||||
assert(called, "custom cd was not called")
|
|
||||||
__builtins.cd = old_cd
|
|
||||||
`cd ${before}`
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
print "OK"
|
print "OK"
|
||||||
|
|||||||
Reference in New Issue
Block a user