guarantee valid result from new_command

master
Daniel Kolesa 2021-04-05 20:05:41 +02:00
parent 8c8aa26c20
commit 3d06157dbc
2 changed files with 111 additions and 103 deletions

View File

@ -89,7 +89,7 @@ struct LIBCUBESCRIPT_EXPORT state {
void touch_var(std::string_view name);
template<typename F>
command *new_command(
command &new_command(
std::string_view name, std::string_view args, F &&f
) {
return new_command(
@ -108,7 +108,7 @@ struct LIBCUBESCRIPT_EXPORT state {
any_value run(bcode_ref const &code);
any_value run(std::string_view code);
any_value run(std::string_view code, std::string_view source);
any_value run(ident *id, std::span<any_value> args);
any_value run(ident &id, std::span<any_value> args);
loop_state run_loop(bcode_ref const &code, any_value &ret);
loop_state run_loop(bcode_ref const &code);
@ -129,7 +129,7 @@ private:
hook_func set_call_hook(hook_func func);
command *new_command(
command &new_command(
std::string_view name, std::string_view args, command_func func
);

View File

@ -127,7 +127,7 @@ state::state(alloc_func func, void *data) {
/* default handlers for variables */
statep->cmd_ivar = new_command("//ivar_builtin", "$iN", [](
statep->cmd_ivar = &new_command("//ivar_builtin", "$iN", [](
auto &cs, auto args, auto &
) {
auto *iv = args[0].get_ident()->get_ivar();
@ -138,7 +138,7 @@ state::state(alloc_func func, void *data) {
}
});
statep->cmd_fvar = new_command("//fvar_builtin", "$fN", [](
statep->cmd_fvar = &new_command("//fvar_builtin", "$fN", [](
auto &cs, auto args, auto &
) {
auto *fv = args[0].get_ident()->get_fvar();
@ -154,7 +154,7 @@ state::state(alloc_func func, void *data) {
}
});
statep->cmd_svar = new_command("//svar_builtin", "$sN", [](
statep->cmd_svar = &new_command("//svar_builtin", "$sN", [](
auto &cs, auto args, auto &
) {
auto *sv = args[0].get_ident()->get_svar();
@ -174,34 +174,34 @@ state::state(alloc_func func, void *data) {
/* builtins */
p = new_command("do", "e", [](auto &cs, auto args, auto &res) {
p = &new_command("do", "e", [](auto &cs, auto args, auto &res) {
res = cs.run(args[0].get_code());
});
static_cast<command_impl *>(p)->p_type = ID_DO;
p = new_command("doargs", "e", [](auto &cs, auto args, auto &res) {
p = &new_command("doargs", "e", [](auto &cs, auto args, auto &res) {
call_with_args(*cs.p_tstate, [&cs, &res, &args]() {
res = cs.run(args[0].get_code());
});
});
static_cast<command_impl *>(p)->p_type = ID_DOARGS;
p = new_command("if", "tee", [](auto &cs, auto args, auto &res) {
p = &new_command("if", "tee", [](auto &cs, auto args, auto &res) {
res = cs.run((args[0].get_bool() ? args[1] : args[2]).get_code());
});
static_cast<command_impl *>(p)->p_type = ID_IF;
p = new_command("result", "t", [](auto &, auto args, auto &res) {
p = &new_command("result", "t", [](auto &, auto args, auto &res) {
res = std::move(args[0]);
});
static_cast<command_impl *>(p)->p_type = ID_RESULT;
p = new_command("!", "t", [](auto &, auto args, auto &res) {
p = &new_command("!", "t", [](auto &, auto args, auto &res) {
res.set_int(!args[0].get_bool());
});
static_cast<command_impl *>(p)->p_type = ID_NOT;
p = new_command("&&", "E1V", [](auto &cs, auto args, auto &res) {
p = &new_command("&&", "E1V", [](auto &cs, auto args, auto &res) {
if (args.empty()) {
res.set_int(1);
} else {
@ -220,7 +220,7 @@ state::state(alloc_func func, void *data) {
});
static_cast<command_impl *>(p)->p_type = ID_AND;
p = new_command("||", "E1V", [](auto &cs, auto args, auto &res) {
p = &new_command("||", "E1V", [](auto &cs, auto args, auto &res) {
if (args.empty()) {
res.set_int(0);
} else {
@ -239,10 +239,10 @@ state::state(alloc_func func, void *data) {
});
static_cast<command_impl *>(p)->p_type = ID_OR;
p = new_command("local", "", nullptr);
p = &new_command("local", "", nullptr);
static_cast<command_impl *>(p)->p_type = ID_LOCAL;
p = new_command("break", "", [](auto &cs, auto, auto &) {
p = &new_command("break", "", [](auto &cs, auto, auto &) {
if (cs.p_tstate->loop_level) {
throw break_exception{};
} else {
@ -251,7 +251,7 @@ state::state(alloc_func func, void *data) {
});
static_cast<command_impl *>(p)->p_type = ID_BREAK;
p = new_command("continue", "", [](auto &cs, auto, auto &) {
p = &new_command("continue", "", [](auto &cs, auto, auto &) {
if (cs.p_tstate->loop_level) {
throw continue_exception{};
} else {
@ -502,7 +502,7 @@ LIBCUBESCRIPT_EXPORT void state::set_alias(
case ident_type::IVAR:
case ident_type::FVAR:
case ident_type::SVAR:
run(id, std::span<any_value>{&v, 1});
run(*id, std::span<any_value>{&v, 1});
break;
default:
throw error{
@ -527,7 +527,7 @@ static char const *allowed_builtins[] = {
nullptr
};
LIBCUBESCRIPT_EXPORT command *state::new_command(
LIBCUBESCRIPT_EXPORT command &state::new_command(
std::string_view name, std::string_view args, command_func func
) {
int nargs = 0;
@ -552,13 +552,19 @@ LIBCUBESCRIPT_EXPORT command *state::new_command(
case '4': {
int nrep = (*fmt - '0');
if (nargs < nrep) {
return nullptr;
throw error{
*this, "not enough arguments to repeat"
};
}
if ((args.end() - fmt) != 2) {
return nullptr;
throw error{
*this, "malformed argument list"
};
}
if ((fmt[1] != 'C') && (fmt[1] != 'V')) {
return nullptr;
throw error{
*this, "repetition without variadic arguments"
};
}
nargs -= nrep;
break;
@ -566,11 +572,15 @@ LIBCUBESCRIPT_EXPORT command *state::new_command(
case 'C':
case 'V':
if ((fmt + 1) != args.end()) {
return nullptr;
throw error{
*this, "unterminated variadic argument list"
};
}
break;
default:
return nullptr;
throw error{
*this, "invalid argument type: %c", *fmt
};
}
}
auto &is = *p_tstate->istate;
@ -616,7 +626,7 @@ valid:
}
do_add:
is.add_ident(cmd, cmd);
return cmd;
return *cmd;
}
LIBCUBESCRIPT_EXPORT void state::init_libs(int libs) {
@ -666,101 +676,99 @@ LIBCUBESCRIPT_EXPORT any_value state::run(
}
LIBCUBESCRIPT_EXPORT any_value state::run(
ident *id, std::span<any_value> args
ident &id, std::span<any_value> args
) {
any_value ret{*this};
std::size_t nargs = args.size();
run_depth_guard level{*p_tstate}; /* incr and decr on scope exit */
if (id) {
switch (id->get_type()) {
default:
if (!ident_is_callable(id)) {
break;
}
/* fallthrough */
case ident_type::COMMAND: {
auto *cimpl = static_cast<command_impl *>(id);
if (nargs < std::size_t(cimpl->get_num_args())) {
stack_guard s{*p_tstate}; /* restore after call */
auto &targs = p_tstate->vmstack;
auto osz = targs.size();
targs.resize(osz + cimpl->get_num_args(), any_value{*this});
for (std::size_t i = 0; i < nargs; ++i) {
targs[osz + i] = args[i];
}
exec_command(
*p_tstate, cimpl, id, &targs[osz], ret, nargs, false
);
} else {
exec_command(
*p_tstate, cimpl, id, &args[0], ret, nargs, false
);
}
nargs = 0;
switch (id.get_type()) {
default:
if (!ident_is_callable(&id)) {
break;
}
case ident_type::IVAR: {
auto *hid = p_tstate->istate->cmd_ivar;
auto *cimp = static_cast<command_impl *>(hid);
/* fallthrough */
case ident_type::COMMAND: {
auto &cimpl = static_cast<command_impl &>(id);
if (nargs < std::size_t(cimpl.get_num_args())) {
stack_guard s{*p_tstate}; /* restore after call */
auto &targs = p_tstate->vmstack;
auto osz = targs.size();
auto anargs = std::size_t(cimp->get_num_args());
targs.resize(
osz + std::max(args.size(), anargs + 1), any_value{*this}
);
targs.resize(osz + cimpl.get_num_args(), any_value{*this});
for (std::size_t i = 0; i < nargs; ++i) {
targs[osz + i + 1] = args[i];
targs[osz + i] = args[i];
}
exec_command(
*p_tstate, cimp, id, &targs[osz], ret, nargs + 1, false
*p_tstate, &cimpl, &id, &targs[osz], ret, nargs, false
);
break;
}
case ident_type::FVAR: {
auto *hid = p_tstate->istate->cmd_fvar;
auto *cimp = static_cast<command_impl *>(hid);
auto &targs = p_tstate->vmstack;
auto osz = targs.size();
auto anargs = std::size_t(cimp->get_num_args());
targs.resize(
osz + std::max(args.size(), anargs + 1), any_value{*this}
);
for (std::size_t i = 0; i < nargs; ++i) {
targs[osz + i + 1] = args[i];
}
} else {
exec_command(
*p_tstate, cimp, id, &targs[osz], ret, nargs + 1, false
);
break;
}
case ident_type::SVAR: {
auto *hid = p_tstate->istate->cmd_svar;
auto *cimp = static_cast<command_impl *>(hid);
auto &targs = p_tstate->vmstack;
auto osz = targs.size();
auto anargs = std::size_t(cimp->get_num_args());
targs.resize(
osz + std::max(args.size(), anargs + 1), any_value{*this}
);
for (std::size_t i = 0; i < nargs; ++i) {
targs[osz + i + 1] = args[i];
}
exec_command(
*p_tstate, cimp, id, &targs[osz], ret, nargs + 1, false
);
break;
}
case ident_type::ALIAS: {
alias *a = static_cast<alias *>(id);
if (a->is_arg() && !ident_is_used_arg(a, *p_tstate)) {
break;
}
exec_alias(
*p_tstate, a, &args[0], ret, nargs, nargs, 0, 0,
BC_RET_NULL, true
*p_tstate, &cimpl, &id, &args[0], ret, nargs, false
);
}
nargs = 0;
break;
}
case ident_type::IVAR: {
auto *hid = p_tstate->istate->cmd_ivar;
auto *cimp = static_cast<command_impl *>(hid);
auto &targs = p_tstate->vmstack;
auto osz = targs.size();
auto anargs = std::size_t(cimp->get_num_args());
targs.resize(
osz + std::max(args.size(), anargs + 1), any_value{*this}
);
for (std::size_t i = 0; i < nargs; ++i) {
targs[osz + i + 1] = args[i];
}
exec_command(
*p_tstate, cimp, &id, &targs[osz], ret, nargs + 1, false
);
break;
}
case ident_type::FVAR: {
auto *hid = p_tstate->istate->cmd_fvar;
auto *cimp = static_cast<command_impl *>(hid);
auto &targs = p_tstate->vmstack;
auto osz = targs.size();
auto anargs = std::size_t(cimp->get_num_args());
targs.resize(
osz + std::max(args.size(), anargs + 1), any_value{*this}
);
for (std::size_t i = 0; i < nargs; ++i) {
targs[osz + i + 1] = args[i];
}
exec_command(
*p_tstate, cimp, &id, &targs[osz], ret, nargs + 1, false
);
break;
}
case ident_type::SVAR: {
auto *hid = p_tstate->istate->cmd_svar;
auto *cimp = static_cast<command_impl *>(hid);
auto &targs = p_tstate->vmstack;
auto osz = targs.size();
auto anargs = std::size_t(cimp->get_num_args());
targs.resize(
osz + std::max(args.size(), anargs + 1), any_value{*this}
);
for (std::size_t i = 0; i < nargs; ++i) {
targs[osz + i + 1] = args[i];
}
exec_command(
*p_tstate, cimp, &id, &targs[osz], ret, nargs + 1, false
);
break;
}
case ident_type::ALIAS: {
alias &a = static_cast<alias &>(id);
if (a.is_arg() && !ident_is_used_arg(&a, *p_tstate)) {
break;
}
exec_alias(
*p_tstate, &a, &args[0], ret, nargs, nargs, 0, 0,
BC_RET_NULL, true
);
break;
}
}
return ret;