re-add some support for persistent/overridable flags + fix unknown

master
Daniel Kolesa 2021-04-04 01:08:30 +02:00
parent f0bb6b1410
commit 6777eb73d5
7 changed files with 172 additions and 54 deletions

View File

@ -230,6 +230,9 @@ struct LIBCUBESCRIPT_EXPORT ident {
string_var *get_svar(); string_var *get_svar();
string_var const *get_svar() const; string_var const *get_svar() const;
bool is_overridden(state &cs) const;
bool is_persistent(state &cs) const;
protected: protected:
ident() = default; ident() = default;
@ -238,8 +241,17 @@ protected:
struct ident_impl *p_impl{}; struct ident_impl *p_impl{};
}; };
enum class var_type {
DEFAULT = 0,
PERSISTENT,
OVERRIDABLE
};
struct LIBCUBESCRIPT_EXPORT global_var: ident { struct LIBCUBESCRIPT_EXPORT global_var: ident {
bool is_read_only() const; bool is_read_only() const;
bool is_overridable() const;
var_type get_variable_type() const;
protected: protected:
global_var() = default; global_var() = default;
@ -375,6 +387,12 @@ struct LIBCUBESCRIPT_EXPORT state {
bool is_in_loop() const; bool is_in_loop() const;
bool get_override_mode() const;
bool set_override_mode(bool v);
bool get_persist_mode() const;
bool set_persist_mode(bool v);
void set_alias(std::string_view name, any_value v); void set_alias(std::string_view name, any_value v);
std::optional<string_ref> get_alias_val(std::string_view name); std::optional<string_ref> get_alias_val(std::string_view name);

View File

@ -24,15 +24,15 @@ var_impl::var_impl(
{} {}
ivar_impl::ivar_impl(string_ref name, integer_type v, int fl): ivar_impl::ivar_impl(string_ref name, integer_type v, int fl):
var_impl{ident_type::IVAR, name, fl}, p_storage{v} var_impl{ident_type::IVAR, name, fl}, p_storage{v}, p_override{v}
{} {}
fvar_impl::fvar_impl(string_ref name, float_type v, int fl): fvar_impl::fvar_impl(string_ref name, float_type v, int fl):
var_impl{ident_type::FVAR, name, fl}, p_storage{v} var_impl{ident_type::FVAR, name, fl}, p_storage{v}, p_override{v}
{} {}
svar_impl::svar_impl(string_ref name, string_ref v, int fl): svar_impl::svar_impl(string_ref name, string_ref v, int fl):
var_impl{ident_type::SVAR, name, fl}, p_storage{v} var_impl{ident_type::SVAR, name, fl}, p_storage{v}, p_override{v}
{} {}
alias_impl::alias_impl( alias_impl::alias_impl(
@ -120,9 +120,10 @@ void alias_stack::set_arg(alias *a, thread_state &ts, any_value &v) {
node->val_s = std::move(v); node->val_s = std::move(v);
} }
void alias_stack::set_alias(alias *, thread_state &, any_value &v) { void alias_stack::set_alias(alias *, thread_state &ts, any_value &v) {
node->val_s = std::move(v); node->val_s = std::move(v);
node->code = bcode_ref{}; node->code = bcode_ref{};
flags = ts.ident_flags;
} }
/* public interface */ /* public interface */
@ -266,10 +267,56 @@ LIBCUBESCRIPT_EXPORT string_var const *ident::get_svar() const {
return static_cast<string_var const *>(this); return static_cast<string_var const *>(this);
} }
LIBCUBESCRIPT_EXPORT bool ident::is_overridden(state &cs) const {
switch (get_type()) {
case ident_type::IVAR:
case ident_type::FVAR:
case ident_type::SVAR:
return (p_impl->p_flags & IDENT_FLAG_OVERRIDDEN);
case ident_type::ALIAS:
return (cs.thread_pointer()->get_astack(
static_cast<alias const *>(this)
).flags & IDENT_FLAG_OVERRIDDEN);
default:
break;
}
return false;
}
LIBCUBESCRIPT_EXPORT bool ident::is_persistent(state &cs) const {
switch (get_type()) {
case ident_type::IVAR:
case ident_type::FVAR:
case ident_type::SVAR:
return (p_impl->p_flags & IDENT_FLAG_PERSIST);
case ident_type::ALIAS:
return (cs.thread_pointer()->get_astack(
static_cast<alias const *>(this)
).flags & IDENT_FLAG_PERSIST);
default:
break;
}
return false;
}
LIBCUBESCRIPT_EXPORT bool global_var::is_read_only() const { LIBCUBESCRIPT_EXPORT bool global_var::is_read_only() const {
return (p_impl->p_flags & IDENT_FLAG_READONLY); return (p_impl->p_flags & IDENT_FLAG_READONLY);
} }
LIBCUBESCRIPT_EXPORT bool global_var::is_overridable() const {
return (p_impl->p_flags & IDENT_FLAG_OVERRIDE);
}
LIBCUBESCRIPT_EXPORT var_type global_var::get_variable_type() const {
if (p_impl->p_flags & IDENT_FLAG_OVERRIDE) {
return var_type::OVERRIDABLE;
} else if (p_impl->p_flags & IDENT_FLAG_PERSIST) {
return var_type::PERSISTENT;
} else {
return var_type::DEFAULT;
}
}
LIBCUBESCRIPT_EXPORT integer_type integer_var::get_value() const { LIBCUBESCRIPT_EXPORT integer_type integer_var::get_value() const {
return static_cast<ivar_impl const *>(this)->p_storage; return static_cast<ivar_impl const *>(this)->p_storage;
} }
@ -318,7 +365,7 @@ LIBCUBESCRIPT_EXPORT alias_local::alias_local(state &cs, ident *a) {
auto &ast = ts.get_astack(p_alias); auto &ast = ts.get_astack(p_alias);
ast.push(ts.idstack.emplace_back(cs)); ast.push(ts.idstack.emplace_back(cs));
p_sp = &ast; p_sp = &ast;
static_cast<alias_impl *>(p_alias)->p_flags &= ~IDENT_FLAG_UNKNOWN; ast.flags &= ~IDENT_FLAG_UNKNOWN;
} }
LIBCUBESCRIPT_EXPORT alias_local::~alias_local() { LIBCUBESCRIPT_EXPORT alias_local::~alias_local() {

View File

@ -17,9 +17,12 @@ enum {
}; };
enum { enum {
IDENT_FLAG_UNKNOWN = 1 << 0, IDENT_FLAG_UNKNOWN = 1 << 0,
IDENT_FLAG_ARG = 1 << 1, IDENT_FLAG_ARG = 1 << 1,
IDENT_FLAG_READONLY = 1 << 2 IDENT_FLAG_READONLY = 1 << 2,
IDENT_FLAG_OVERRIDE = 1 << 3,
IDENT_FLAG_OVERRIDDEN = 1 << 4,
IDENT_FLAG_PERSIST = 1 << 5
}; };
struct ident_stack { struct ident_stack {
@ -30,7 +33,8 @@ struct ident_stack {
}; };
struct alias_stack { struct alias_stack {
ident_stack *node; ident_stack *node = nullptr;
int flags = 0;
void push(ident_stack &st); void push(ident_stack &st);
void pop(); void pop();
@ -77,18 +81,21 @@ struct ivar_impl: var_impl, integer_var {
ivar_impl(string_ref n, integer_type v, int flags); ivar_impl(string_ref n, integer_type v, int flags);
integer_type p_storage; integer_type p_storage;
integer_type p_override;
}; };
struct fvar_impl: var_impl, float_var { struct fvar_impl: var_impl, float_var {
fvar_impl(string_ref n, float_type v, int flags); fvar_impl(string_ref n, float_type v, int flags);
float_type p_storage; float_type p_storage;
float_type p_override;
}; };
struct svar_impl: var_impl, string_var { struct svar_impl: var_impl, string_var {
svar_impl(string_ref n, string_ref v, int flags); svar_impl(string_ref n, string_ref v, int flags);
string_ref p_storage; string_ref p_storage;
string_ref p_override;
}; };
struct alias_impl: ident_impl, alias { struct alias_impl: ident_impl, alias {

View File

@ -354,7 +354,8 @@ LIBCUBESCRIPT_EXPORT void state::set_alias(
throw error{*this, "cannot alias invalid name '%s'", name.data()}; throw error{*this, "cannot alias invalid name '%s'", name.data()};
} else { } else {
auto *a = p_tstate->istate->create<alias_impl>( auto *a = p_tstate->istate->create<alias_impl>(
*this, string_ref{p_tstate->istate, name}, std::move(v), 0 *this, string_ref{p_tstate->istate, name}, std::move(v),
p_tstate->ident_flags
); );
p_tstate->istate->add_ident(a, a); p_tstate->istate->add_ident(a, a);
} }
@ -635,4 +636,32 @@ LIBCUBESCRIPT_EXPORT bool state::is_in_loop() const {
return !!p_tstate->loop_level; return !!p_tstate->loop_level;
} }
LIBCUBESCRIPT_EXPORT bool state::get_override_mode() const {
return (p_tstate->ident_flags & IDENT_FLAG_OVERRIDDEN);
}
LIBCUBESCRIPT_EXPORT bool state::set_override_mode(bool v) {
bool was = get_override_mode();
if (v) {
p_tstate->ident_flags |= IDENT_FLAG_OVERRIDDEN;
} else {
p_tstate->ident_flags &= ~IDENT_FLAG_OVERRIDDEN;
}
return was;
}
LIBCUBESCRIPT_EXPORT bool state::get_persist_mode() const {
return (p_tstate->ident_flags & IDENT_FLAG_PERSIST);
}
LIBCUBESCRIPT_EXPORT bool state::set_persist_mode(bool v) {
bool was = get_persist_mode();
if (v) {
p_tstate->ident_flags |= IDENT_FLAG_PERSIST;
} else {
p_tstate->ident_flags &= ~IDENT_FLAG_PERSIST;
}
return was;
}
} /* namespace cubescript */ } /* namespace cubescript */

View File

@ -21,8 +21,13 @@ alias_stack &thread_state::get_astack(alias *a) {
auto it = astacks.try_emplace(a->get_index()); auto it = astacks.try_emplace(a->get_index());
if (it.second) { if (it.second) {
it.first->second.node = &static_cast<alias_impl *>(a)->p_initial; it.first->second.node = &static_cast<alias_impl *>(a)->p_initial;
it.first->second.flags = static_cast<alias_impl *>(a)->p_flags;
} }
return it.first->second; return it.first->second;
} }
alias_stack const &thread_state::get_astack(alias const *a) {
return get_astack(const_cast<alias *>(a));
}
} /* namespace cubescript */ } /* namespace cubescript */

View File

@ -37,6 +37,8 @@ struct thread_state {
hook_func call_hook{}; hook_func call_hook{};
/* loop nesting level */ /* loop nesting level */
int loop_level = 0; int loop_level = 0;
/* thread ident flags */
int ident_flags = 0;
/* whether we own the internal state (i.e. not a side thread */ /* whether we own the internal state (i.e. not a side thread */
bool owner = false; bool owner = false;
@ -48,6 +50,7 @@ struct thread_state {
hook_func const &get_hook() const { return call_hook; } hook_func const &get_hook() const { return call_hook; }
alias_stack &get_astack(alias *a); alias_stack &get_astack(alias *a);
alias_stack const &get_astack(alias const *a);
}; };
} /* namespace cubescript */ } /* namespace cubescript */

View File

@ -12,8 +12,9 @@ namespace cubescript {
static inline void push_alias(thread_state &ts, ident *id, ident_stack &st) { static inline void push_alias(thread_state &ts, ident *id, ident_stack &st) {
if (id->is_alias() && !static_cast<alias *>(id)->is_arg()) { if (id->is_alias() && !static_cast<alias *>(id)->is_arg()) {
auto *aimp = static_cast<alias_impl *>(id); auto *aimp = static_cast<alias_impl *>(id);
ts.get_astack(aimp).push(st); auto ast = ts.get_astack(aimp);
aimp->p_flags &= ~IDENT_FLAG_UNKNOWN; ast.push(st);
ast.flags &= ~IDENT_FLAG_UNKNOWN;
} }
} }
@ -205,8 +206,14 @@ bool exec_alias(
std::size_t offset, std::size_t skip, std::uint32_t op, bool ncheck std::size_t offset, std::size_t skip, std::uint32_t op, bool ncheck
) { ) {
auto &aast = ts.get_astack(a); auto &aast = ts.get_astack(a);
if (ncheck && aast.node->val_s.get_type() == value_type::NONE) { if (ncheck) {
return false; if (aast.node->val_s.get_type() == value_type::NONE) {
return false;
}
} else if (aast.flags & IDENT_FLAG_UNKNOWN) {
throw error {
*ts.pstate, "unknown command: %s", a->get_name().data()
};
} }
/* excess arguments get ignored (make error maybe?) */ /* excess arguments get ignored (make error maybe?) */
callargs = std::min(callargs, MAX_ARGUMENTS); callargs = std::min(callargs, MAX_ARGUMENTS);
@ -225,6 +232,8 @@ bool exec_alias(
uargs[i] = true; uargs[i] = true;
} }
auto oldargs = anargs->get_value(); auto oldargs = anargs->get_value();
auto oldflags = ts.ident_flags;
ts.ident_flags = aast.flags;
anargs->set_value(integer_type(callargs)); anargs->set_value(integer_type(callargs));
ident_link aliaslink = {a, ts.callstack, uargs}; ident_link aliaslink = {a, ts.callstack, uargs};
ts.callstack = &aliaslink; ts.callstack = &aliaslink;
@ -240,6 +249,7 @@ bool exec_alias(
bcode_ref coderef = aast.node->code; bcode_ref coderef = aast.node->code;
auto cleanup = [&]() { auto cleanup = [&]() {
ts.callstack = aliaslink.next; ts.callstack = aliaslink.next;
ts.ident_flags = oldflags;
auto amask = aliaslink.usedargs; auto amask = aliaslink.usedargs;
for (std::size_t i = 0; i < callargs; i++) { for (std::size_t i = 0; i < callargs; i++) {
ts.get_astack( ts.get_astack(
@ -283,7 +293,9 @@ run_depth_guard::run_depth_guard(thread_state &ts) {
run_depth_guard::~run_depth_guard() { --rundepth; } run_depth_guard::~run_depth_guard() { --rundepth; }
static inline alias *get_lookup_id(thread_state &ts, std::uint32_t op) { static inline alias *get_lookup_id(
thread_state &ts, std::uint32_t op, alias_stack *&ast
) {
ident *id = ts.istate->identmap[op >> 8]; ident *id = ts.istate->identmap[op >> 8];
auto *a = static_cast<alias_impl *>(id); auto *a = static_cast<alias_impl *>(id);
@ -291,16 +303,21 @@ static inline alias *get_lookup_id(thread_state &ts, std::uint32_t op) {
if (!ident_is_used_arg(id, ts)) { if (!ident_is_used_arg(id, ts)) {
return nullptr; return nullptr;
} }
} else if (a->p_flags & IDENT_FLAG_UNKNOWN) { ast = &ts.get_astack(static_cast<alias *>(id));
throw error{ } else {
*ts.pstate, "unknown alias lookup: %s", id->get_name().data() ast = &ts.get_astack(static_cast<alias *>(id));
}; if (ast->flags & IDENT_FLAG_UNKNOWN) {
throw error{
*ts.pstate, "unknown alias lookup: %s", id->get_name().data()
};
}
} }
return static_cast<alias *>(id); return static_cast<alias *>(id);
} }
static inline int get_lookupu_type( static inline int get_lookupu_type(
thread_state &ts, any_value &arg, ident *&id, std::uint32_t op thread_state &ts, any_value &arg, ident *&id, std::uint32_t op,
alias_stack *&ast
) { ) {
if (arg.get_type() != value_type::STRING) { if (arg.get_type() != value_type::STRING) {
return -2; /* default case */ return -2; /* default case */
@ -310,7 +327,8 @@ static inline int get_lookupu_type(
switch(id->get_type()) { switch(id->get_type()) {
case ident_type::ALIAS: { case ident_type::ALIAS: {
auto *a = static_cast<alias_impl *>(id); auto *a = static_cast<alias_impl *>(id);
if (a->p_flags & IDENT_FLAG_UNKNOWN) { ast = &ts.get_astack(static_cast<alias *>(id));
if (ast->flags & IDENT_FLAG_UNKNOWN) {
break; break;
} }
if (a->is_arg() && !ident_is_used_arg(id, ts)) { if (a->is_arg() && !ident_is_used_arg(id, ts)) {
@ -770,12 +788,11 @@ std::uint32_t *vm_exec(
case BC_INST_LOOKUP_U | BC_RET_STRING: { case BC_INST_LOOKUP_U | BC_RET_STRING: {
ident *id = nullptr; ident *id = nullptr;
alias_stack *ast;
any_value &arg = args.back(); any_value &arg = args.back();
switch (get_lookupu_type(ts, arg, id, op)) { switch (get_lookupu_type(ts, arg, id, op, ast)) {
case ID_ALIAS: case ID_ALIAS:
arg = ts.get_astack( arg = ast->node->val_s;
static_cast<alias *>(id)
).node->val_s;
arg.force_str(); arg.force_str();
continue; continue;
case ID_SVAR: case ID_SVAR:
@ -798,12 +815,13 @@ std::uint32_t *vm_exec(
} }
case BC_INST_LOOKUP | BC_RET_STRING: { case BC_INST_LOOKUP | BC_RET_STRING: {
alias *a = get_lookup_id(ts, op); alias_stack *ast;
alias *a = get_lookup_id(ts, op, ast);
if (!a) { if (!a) {
args.emplace_back(cs).set_str(""); args.emplace_back(cs).set_str("");
} else { } else {
auto &v = args.emplace_back(cs); auto &v = args.emplace_back(cs);
v = ts.get_astack(a).node->val_s; v = ast->node->val_s;
v.force_str(); v.force_str();
} }
continue; continue;
@ -811,12 +829,11 @@ std::uint32_t *vm_exec(
case BC_INST_LOOKUP_U | BC_RET_INT: { case BC_INST_LOOKUP_U | BC_RET_INT: {
ident *id = nullptr; ident *id = nullptr;
alias_stack *ast;
any_value &arg = args.back(); any_value &arg = args.back();
switch (get_lookupu_type(ts, arg, id, op)) { switch (get_lookupu_type(ts, arg, id, op, ast)) {
case ID_ALIAS: case ID_ALIAS:
arg.set_int(ts.get_astack(static_cast<alias *>( arg.set_int(ast->node->val_s.get_int());
id
)).node->val_s.get_int());
continue; continue;
case ID_SVAR: case ID_SVAR:
arg.set_int(parse_int( arg.set_int(parse_int(
@ -839,24 +856,22 @@ std::uint32_t *vm_exec(
} }
} }
case BC_INST_LOOKUP | BC_RET_INT: { case BC_INST_LOOKUP | BC_RET_INT: {
alias *a = get_lookup_id(ts, op); alias_stack *ast;
alias *a = get_lookup_id(ts, op, ast);
if (!a) { if (!a) {
args.emplace_back(cs).set_int(0); args.emplace_back(cs).set_int(0);
} else { } else {
args.emplace_back(cs).set_int( args.emplace_back(cs).set_int(ast->node->val_s.get_int());
ts.get_astack(a).node->val_s.get_int()
);
} }
continue; continue;
} }
case BC_INST_LOOKUP_U | BC_RET_FLOAT: { case BC_INST_LOOKUP_U | BC_RET_FLOAT: {
ident *id = nullptr; ident *id = nullptr;
alias_stack *ast;
any_value &arg = args.back(); any_value &arg = args.back();
switch (get_lookupu_type(ts, arg, id, op)) { switch (get_lookupu_type(ts, arg, id, op, ast)) {
case ID_ALIAS: case ID_ALIAS:
arg.set_float(ts.get_astack(static_cast<alias *>( arg.set_float(ast->node->val_s.get_float());
id
)).node->val_s.get_float());
continue; continue;
case ID_SVAR: case ID_SVAR:
arg.set_float(parse_float( arg.set_float(parse_float(
@ -881,24 +896,24 @@ std::uint32_t *vm_exec(
} }
} }
case BC_INST_LOOKUP | BC_RET_FLOAT: { case BC_INST_LOOKUP | BC_RET_FLOAT: {
alias *a = get_lookup_id(ts, op); alias_stack *ast;
alias *a = get_lookup_id(ts, op, ast);
if (!a) { if (!a) {
args.emplace_back(cs).set_float(float_type(0)); args.emplace_back(cs).set_float(float_type(0));
} else { } else {
args.emplace_back(cs).set_float( args.emplace_back(cs).set_float(
ts.get_astack(a).node->val_s.get_float() ast->node->val_s.get_float()
); );
} }
continue; continue;
} }
case BC_INST_LOOKUP_U | BC_RET_NULL: { case BC_INST_LOOKUP_U | BC_RET_NULL: {
ident *id = nullptr; ident *id = nullptr;
alias_stack *ast;
any_value &arg = args.back(); any_value &arg = args.back();
switch (get_lookupu_type(ts, arg, id, op)) { switch (get_lookupu_type(ts, arg, id, op, ast)) {
case ID_ALIAS: case ID_ALIAS:
ts.get_astack( ast->node->val_s.get_val(arg);
static_cast<alias *>(id)
).node->val_s.get_val(arg);
continue; continue;
case ID_SVAR: case ID_SVAR:
arg.set_str(static_cast<string_var *>(id)->get_value()); arg.set_str(static_cast<string_var *>(id)->get_value());
@ -919,13 +934,12 @@ std::uint32_t *vm_exec(
} }
} }
case BC_INST_LOOKUP | BC_RET_NULL: { case BC_INST_LOOKUP | BC_RET_NULL: {
alias *a = get_lookup_id(ts, op); alias_stack *ast;
alias *a = get_lookup_id(ts, op, ast);
if (!a) { if (!a) {
args.emplace_back(cs).set_none(); args.emplace_back(cs).set_none();
} else { } else {
ts.get_astack(a).node->val_s.get_val( ast->node->val_s.get_val(args.emplace_back(cs));
args.emplace_back(cs)
);
} }
continue; continue;
} }
@ -1049,11 +1063,6 @@ std::uint32_t *vm_exec(
force_arg(result, op & BC_INST_RET_MASK); force_arg(result, op & BC_INST_RET_MASK);
continue; continue;
} }
} else if (imp->p_flags & IDENT_FLAG_UNKNOWN) {
force_arg(result, op & BC_INST_RET_MASK);
throw error{
cs, "unknown command: %s", id->get_name().data()
};
} }
exec_alias( exec_alias(
ts, imp, &args[0], result, callargs, ts, imp, &args[0], result, callargs,