sanitize var names, provide cached builtins for var handlers
parent
a789659387
commit
e65e141741
|
@ -1204,10 +1204,7 @@ static void compilestatements(codegen_state &gs, int rettype, int brak) {
|
||||||
);
|
);
|
||||||
goto endstatement;
|
goto endstatement;
|
||||||
case ident_type::IVAR: {
|
case ident_type::IVAR: {
|
||||||
auto *hid = gs.ts.pstate->get_ident("//ivar");
|
auto *hid = gs.ts.istate->cmd_ivar;
|
||||||
if (!hid || !hid->is_command()) {
|
|
||||||
throw error{gs.ts, "invalid ivar handler"};
|
|
||||||
}
|
|
||||||
compile_cmd(
|
compile_cmd(
|
||||||
gs, static_cast<command_impl *>(hid),
|
gs, static_cast<command_impl *>(hid),
|
||||||
id, more, rettype, 1
|
id, more, rettype, 1
|
||||||
|
@ -1215,10 +1212,7 @@ static void compilestatements(codegen_state &gs, int rettype, int brak) {
|
||||||
goto endstatement;
|
goto endstatement;
|
||||||
}
|
}
|
||||||
case ident_type::FVAR: {
|
case ident_type::FVAR: {
|
||||||
auto *hid = gs.ts.pstate->get_ident("//fvar");
|
auto *hid = gs.ts.istate->cmd_fvar;
|
||||||
if (!hid || !hid->is_command()) {
|
|
||||||
throw error{gs.ts, "invalid fvar handler"};
|
|
||||||
}
|
|
||||||
compile_cmd(
|
compile_cmd(
|
||||||
gs, static_cast<command_impl *>(hid),
|
gs, static_cast<command_impl *>(hid),
|
||||||
id, more, rettype, 1
|
id, more, rettype, 1
|
||||||
|
@ -1226,10 +1220,7 @@ static void compilestatements(codegen_state &gs, int rettype, int brak) {
|
||||||
goto endstatement;
|
goto endstatement;
|
||||||
}
|
}
|
||||||
case ident_type::SVAR: {
|
case ident_type::SVAR: {
|
||||||
auto *hid = gs.ts.pstate->get_ident("//svar");
|
auto *hid = gs.ts.istate->cmd_svar;
|
||||||
if (!hid || !hid->is_command()) {
|
|
||||||
throw error{gs.ts, "invalid svar handler"};
|
|
||||||
}
|
|
||||||
compile_cmd(
|
compile_cmd(
|
||||||
gs, static_cast<command_impl *>(hid),
|
gs, static_cast<command_impl *>(hid),
|
||||||
id, more, rettype, 1
|
id, more, rettype, 1
|
||||||
|
@ -1338,10 +1329,7 @@ noid:
|
||||||
compile_and_or(gs, id, more, rettype);
|
compile_and_or(gs, id, more, rettype);
|
||||||
break;
|
break;
|
||||||
case ID_IVAR: {
|
case ID_IVAR: {
|
||||||
auto *hid = gs.ts.pstate->get_ident("//ivar");
|
auto *hid = gs.ts.istate->cmd_ivar;
|
||||||
if (!hid || !hid->is_command()) {
|
|
||||||
throw error{gs.ts, "invalid ivar handler"};
|
|
||||||
}
|
|
||||||
compile_cmd(
|
compile_cmd(
|
||||||
gs, static_cast<command_impl *>(hid),
|
gs, static_cast<command_impl *>(hid),
|
||||||
id, more, rettype
|
id, more, rettype
|
||||||
|
@ -1349,10 +1337,7 @@ noid:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ID_FVAR: {
|
case ID_FVAR: {
|
||||||
auto *hid = gs.ts.pstate->get_ident("//fvar");
|
auto *hid = gs.ts.istate->cmd_fvar;
|
||||||
if (!hid || !hid->is_command()) {
|
|
||||||
throw error{gs.ts, "invalid fvar handler"};
|
|
||||||
}
|
|
||||||
compile_cmd(
|
compile_cmd(
|
||||||
gs, static_cast<command_impl *>(hid),
|
gs, static_cast<command_impl *>(hid),
|
||||||
id, more, rettype
|
id, more, rettype
|
||||||
|
@ -1360,10 +1345,7 @@ noid:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ID_SVAR: {
|
case ID_SVAR: {
|
||||||
auto *hid = gs.ts.pstate->get_ident("//svar");
|
auto *hid = gs.ts.istate->cmd_svar;
|
||||||
if (!hid || !hid->is_command()) {
|
|
||||||
throw error{gs.ts, "invalid svar handler"};
|
|
||||||
}
|
|
||||||
compile_cmd(
|
compile_cmd(
|
||||||
gs, static_cast<command_impl *>(hid),
|
gs, static_cast<command_impl *>(hid),
|
||||||
id, more, rettype
|
id, more, rettype
|
||||||
|
|
|
@ -83,8 +83,8 @@ command_impl::command_impl(
|
||||||
{}
|
{}
|
||||||
|
|
||||||
void var_changed(thread_state &ts, ident *id) {
|
void var_changed(thread_state &ts, ident *id) {
|
||||||
auto *cid = ts.pstate->get_ident("//var_changed");
|
auto *cid = ts.istate->cmd_var_changed;
|
||||||
if (!cid || !cid->is_command()) {
|
if (!cid) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto *cimp = static_cast<command_impl *>(cid);
|
auto *cimp = static_cast<command_impl *>(cid);
|
||||||
|
|
154
src/cs_state.cc
154
src/cs_state.cc
|
@ -1,4 +1,6 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
#include "cs_bcode.hh"
|
#include "cs_bcode.hh"
|
||||||
#include "cs_state.hh"
|
#include "cs_state.hh"
|
||||||
|
@ -58,7 +60,7 @@ ident *internal_state::new_ident(state &cs, std::string_view name, int flags) {
|
||||||
if (!id) {
|
if (!id) {
|
||||||
if (!is_valid_name(name)) {
|
if (!is_valid_name(name)) {
|
||||||
throw error{
|
throw error{
|
||||||
cs, "number %s is not a valid identifier name", name.data()
|
cs, "'%s' is not a valid identifier name", name.data()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
auto *inst = create<alias_impl>(
|
auto *inst = create<alias_impl>(
|
||||||
|
@ -133,6 +135,55 @@ state::state(alloc_func func, void *data) {
|
||||||
throw internal_error{"invalid dbgalias index"};
|
throw internal_error{"invalid dbgalias index"};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* default handlers for variables */
|
||||||
|
|
||||||
|
statep->cmd_ivar = new_command("//ivar_builtin", "$iN", [](
|
||||||
|
auto &cs, auto args, auto &
|
||||||
|
) {
|
||||||
|
auto *iv = args[0].get_ident()->get_ivar();
|
||||||
|
if (args[2].get_int() <= 1) {
|
||||||
|
std::printf("%s = %d\n", iv->get_name().data(), iv->get_value());
|
||||||
|
} else {
|
||||||
|
iv->set_value(cs, args[1].get_int());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
statep->cmd_fvar = new_command("//fvar_builtin", "$fN", [](
|
||||||
|
auto &cs, auto args, auto &
|
||||||
|
) {
|
||||||
|
auto *fv = args[0].get_ident()->get_fvar();
|
||||||
|
if (args[2].get_int() <= 1) {
|
||||||
|
auto val = fv->get_value();
|
||||||
|
if (std::floor(val) == val) {
|
||||||
|
std::printf("%s = %.1f\n", fv->get_name().data(), val);
|
||||||
|
} else {
|
||||||
|
std::printf("%s = %.7g\n", fv->get_name().data(), val);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fv->set_value(cs, args[1].get_float());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
statep->cmd_svar = new_command("//svar_builtin", "$sN", [](
|
||||||
|
auto &cs, auto args, auto &
|
||||||
|
) {
|
||||||
|
auto *sv = args[0].get_ident()->get_svar();
|
||||||
|
if (args[2].get_int() <= 1) {
|
||||||
|
auto val = std::string_view{sv->get_value()};
|
||||||
|
if (val.find('"') == val.npos) {
|
||||||
|
std::printf("%s = \"%s\"\n", sv->get_name().data(), val.data());
|
||||||
|
} else {
|
||||||
|
std::printf("%s = [%s]\n", sv->get_name().data(), val.data());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sv->set_value(cs, args[1].get_str());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
statep->cmd_var_changed = nullptr;
|
||||||
|
|
||||||
|
/* builtins */
|
||||||
|
|
||||||
p = new_command("do", "e", [](auto &cs, auto args, auto &res) {
|
p = new_command("do", "e", [](auto &cs, auto args, auto &res) {
|
||||||
cs.run(args[0].get_code(), res);
|
cs.run(args[0].get_code(), res);
|
||||||
});
|
});
|
||||||
|
@ -356,6 +407,21 @@ inline int var_flags(bool read_only, var_type vtp) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void var_name_check(
|
||||||
|
state &cs, ident *id, std::string_view n
|
||||||
|
) {
|
||||||
|
if (id) {
|
||||||
|
throw error{
|
||||||
|
cs, "redefinition of ident '%.*s'", int(n.size()), n.data()
|
||||||
|
};
|
||||||
|
} else if (!is_valid_name(n)) {
|
||||||
|
throw error{
|
||||||
|
cs, "'%.*s' is not a valid variable name",
|
||||||
|
int(n.size()), n.data()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
LIBCUBESCRIPT_EXPORT integer_var *state::new_ivar(
|
LIBCUBESCRIPT_EXPORT integer_var *state::new_ivar(
|
||||||
std::string_view n, integer_type v, bool read_only, var_type vtp
|
std::string_view n, integer_type v, bool read_only, var_type vtp
|
||||||
) {
|
) {
|
||||||
|
@ -363,6 +429,12 @@ LIBCUBESCRIPT_EXPORT integer_var *state::new_ivar(
|
||||||
string_ref{p_tstate->istate, n}, v,
|
string_ref{p_tstate->istate, n}, v,
|
||||||
var_flags(read_only, vtp)
|
var_flags(read_only, vtp)
|
||||||
);
|
);
|
||||||
|
try {
|
||||||
|
var_name_check(*this, p_tstate->istate->get_ident(n), n);
|
||||||
|
} catch (...) {
|
||||||
|
p_tstate->istate->destroy(iv);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
p_tstate->istate->add_ident(iv, iv);
|
p_tstate->istate->add_ident(iv, iv);
|
||||||
return iv;
|
return iv;
|
||||||
}
|
}
|
||||||
|
@ -374,6 +446,12 @@ LIBCUBESCRIPT_EXPORT float_var *state::new_fvar(
|
||||||
string_ref{p_tstate->istate, n}, v,
|
string_ref{p_tstate->istate, n}, v,
|
||||||
var_flags(read_only, vtp)
|
var_flags(read_only, vtp)
|
||||||
);
|
);
|
||||||
|
try {
|
||||||
|
var_name_check(*this, p_tstate->istate->get_ident(n), n);
|
||||||
|
} catch (...) {
|
||||||
|
p_tstate->istate->destroy(fv);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
p_tstate->istate->add_ident(fv, fv);
|
p_tstate->istate->add_ident(fv, fv);
|
||||||
return fv;
|
return fv;
|
||||||
}
|
}
|
||||||
|
@ -385,6 +463,12 @@ LIBCUBESCRIPT_EXPORT string_var *state::new_svar(
|
||||||
string_ref{p_tstate->istate, n}, string_ref{p_tstate->istate, v},
|
string_ref{p_tstate->istate, n}, string_ref{p_tstate->istate, v},
|
||||||
var_flags(read_only, vtp)
|
var_flags(read_only, vtp)
|
||||||
);
|
);
|
||||||
|
try {
|
||||||
|
var_name_check(*this, p_tstate->istate->get_ident(n), n);
|
||||||
|
} catch (...) {
|
||||||
|
p_tstate->istate->destroy(sv);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
p_tstate->istate->add_ident(sv, sv);
|
p_tstate->istate->add_ident(sv, sv);
|
||||||
return sv;
|
return sv;
|
||||||
}
|
}
|
||||||
|
@ -448,6 +532,12 @@ LIBCUBESCRIPT_EXPORT void state::set_alias(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char const *allowed_builtins[] = {
|
||||||
|
"//ivar", "//fvar", "//svar", "//var_changed",
|
||||||
|
"//ivar_builtin", "//fvar_builtin", "//svar_builtin",
|
||||||
|
nullptr
|
||||||
|
};
|
||||||
|
|
||||||
LIBCUBESCRIPT_EXPORT command *state::new_command(
|
LIBCUBESCRIPT_EXPORT command *state::new_command(
|
||||||
std::string_view name, std::string_view args, command_func func
|
std::string_view name, std::string_view args, command_func func
|
||||||
) {
|
) {
|
||||||
|
@ -494,12 +584,49 @@ LIBCUBESCRIPT_EXPORT command *state::new_command(
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
auto *cmd = p_tstate->istate->create<command_impl>(
|
auto &is = *p_tstate->istate;
|
||||||
string_ref{p_tstate->istate, name},
|
auto *cmd = is.create<command_impl>(
|
||||||
string_ref{p_tstate->istate, args},
|
string_ref{&is, name}, string_ref{&is, args}, nargs, std::move(func)
|
||||||
nargs, std::move(func)
|
|
||||||
);
|
);
|
||||||
p_tstate->istate->add_ident(cmd, cmd);
|
/* we can set these builtins */
|
||||||
|
command **bptrs[] = {
|
||||||
|
&is.cmd_ivar, &is.cmd_fvar, &is.cmd_svar, &is.cmd_var_changed
|
||||||
|
};
|
||||||
|
auto nbptrs = sizeof(bptrs) / sizeof(*bptrs);
|
||||||
|
/* provided a builtin */
|
||||||
|
if ((name.size() >= 2) && (name[0] == '/') && (name[1] == '/')) {
|
||||||
|
/* sanitize */
|
||||||
|
for (auto **p = allowed_builtins; *p; ++p) {
|
||||||
|
if (!name.compare(*p)) {
|
||||||
|
/* if it's one of the settable ones, maybe set it */
|
||||||
|
if (std::size_t(p - allowed_builtins) < nbptrs) {
|
||||||
|
if (!is.get_ident(name)) {
|
||||||
|
/* only set if it does not exist already */
|
||||||
|
*bptrs[p - allowed_builtins] = cmd;
|
||||||
|
goto do_add;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* this will ensure we're not redefining them */
|
||||||
|
goto valid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* we haven't found one matching the list, so error */
|
||||||
|
is.destroy(cmd);
|
||||||
|
throw error{
|
||||||
|
*this, "forbidden builtin command: %.*s",
|
||||||
|
int(name.size()), name.data()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
valid:
|
||||||
|
if (is.get_ident(name)) {
|
||||||
|
is.destroy(cmd);
|
||||||
|
throw error{
|
||||||
|
*this, "redefinition of ident '%.*s'",
|
||||||
|
int(name.size()), name.data()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
do_add:
|
||||||
|
is.add_ident(cmd, cmd);
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -593,10 +720,7 @@ LIBCUBESCRIPT_EXPORT void state::run(
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ident_type::IVAR: {
|
case ident_type::IVAR: {
|
||||||
auto *hid = get_ident("//ivar");
|
auto *hid = p_tstate->istate->cmd_ivar;
|
||||||
if (!hid || !hid->is_command()) {
|
|
||||||
throw error{*p_tstate, "invalid ivar handler"};
|
|
||||||
}
|
|
||||||
auto *cimp = static_cast<command_impl *>(hid);
|
auto *cimp = static_cast<command_impl *>(hid);
|
||||||
auto &targs = p_tstate->vmstack;
|
auto &targs = p_tstate->vmstack;
|
||||||
auto osz = targs.size();
|
auto osz = targs.size();
|
||||||
|
@ -613,10 +737,7 @@ LIBCUBESCRIPT_EXPORT void state::run(
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ident_type::FVAR: {
|
case ident_type::FVAR: {
|
||||||
auto *hid = get_ident("//fvar");
|
auto *hid = p_tstate->istate->cmd_fvar;
|
||||||
if (!hid || !hid->is_command()) {
|
|
||||||
throw error{*p_tstate, "invalid fvar handler"};
|
|
||||||
}
|
|
||||||
auto *cimp = static_cast<command_impl *>(hid);
|
auto *cimp = static_cast<command_impl *>(hid);
|
||||||
auto &targs = p_tstate->vmstack;
|
auto &targs = p_tstate->vmstack;
|
||||||
auto osz = targs.size();
|
auto osz = targs.size();
|
||||||
|
@ -633,10 +754,7 @@ LIBCUBESCRIPT_EXPORT void state::run(
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ident_type::SVAR: {
|
case ident_type::SVAR: {
|
||||||
auto *hid = get_ident("//svar");
|
auto *hid = p_tstate->istate->cmd_svar;
|
||||||
if (!hid || !hid->is_command()) {
|
|
||||||
throw error{*p_tstate, "invalid svar handler"};
|
|
||||||
}
|
|
||||||
auto *cimp = static_cast<command_impl *>(hid);
|
auto *cimp = static_cast<command_impl *>(hid);
|
||||||
auto &targs = p_tstate->vmstack;
|
auto &targs = p_tstate->vmstack;
|
||||||
auto osz = targs.size();
|
auto osz = targs.size();
|
||||||
|
|
|
@ -52,6 +52,11 @@ struct internal_state {
|
||||||
string_pool *strman;
|
string_pool *strman;
|
||||||
empty_block *empty;
|
empty_block *empty;
|
||||||
|
|
||||||
|
command *cmd_ivar;
|
||||||
|
command *cmd_fvar;
|
||||||
|
command *cmd_svar;
|
||||||
|
command *cmd_var_changed;
|
||||||
|
|
||||||
internal_state() = delete;
|
internal_state() = delete;
|
||||||
|
|
||||||
internal_state(alloc_func af, void *data);
|
internal_state(alloc_func af, void *data);
|
||||||
|
|
15
src/cs_vm.cc
15
src/cs_vm.cc
|
@ -1146,10 +1146,7 @@ noid:
|
||||||
return code;
|
return code;
|
||||||
}
|
}
|
||||||
case ID_IVAR: {
|
case ID_IVAR: {
|
||||||
auto *hid = cs.get_ident("//ivar");
|
auto *hid = ts.istate->cmd_ivar;
|
||||||
if (!hid || !hid->is_command()) {
|
|
||||||
throw error{ts, "invalid ivar handler"};
|
|
||||||
}
|
|
||||||
auto *cimp = static_cast<command_impl *>(hid);
|
auto *cimp = static_cast<command_impl *>(hid);
|
||||||
/* the $ argument */
|
/* the $ argument */
|
||||||
args.insert(offset, any_value{cs});
|
args.insert(offset, any_value{cs});
|
||||||
|
@ -1164,10 +1161,7 @@ noid:
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
case ID_FVAR: {
|
case ID_FVAR: {
|
||||||
auto *hid = cs.get_ident("//fvar");
|
auto *hid = ts.istate->cmd_fvar;
|
||||||
if (!hid || !hid->is_command()) {
|
|
||||||
throw error{ts, "invalid fvar handler"};
|
|
||||||
}
|
|
||||||
auto *cimp = static_cast<command_impl *>(hid);
|
auto *cimp = static_cast<command_impl *>(hid);
|
||||||
/* the $ argument */
|
/* the $ argument */
|
||||||
args.insert(offset, any_value{cs});
|
args.insert(offset, any_value{cs});
|
||||||
|
@ -1182,10 +1176,7 @@ noid:
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
case ID_SVAR: {
|
case ID_SVAR: {
|
||||||
auto *hid = cs.get_ident("//svar");
|
auto *hid = ts.istate->cmd_svar;
|
||||||
if (!hid || !hid->is_command()) {
|
|
||||||
throw error{ts, "invalid svar handler"};
|
|
||||||
}
|
|
||||||
auto *cimp = static_cast<command_impl *>(hid);
|
auto *cimp = static_cast<command_impl *>(hid);
|
||||||
/* the $ argument */
|
/* the $ argument */
|
||||||
args.insert(offset, any_value{cs});
|
args.insert(offset, any_value{cs});
|
||||||
|
|
Loading…
Reference in New Issue