mirror of
https://github.com/zeek/zeek.git
synced 2025-10-12 03:28:19 +00:00

* origin/topic/vern/when-lambda: explicitly provide the frame for evaluating a "when" timeout expression attempt to make "when" btest deterministic tests for new "when" semantics/errors update existing test suite usage of "when" statements to include captures update uses of "when" in base scripts to include captures captures for "when" statements update Triggers to IntrusivePtr's and simpler AST traversal introduce IDSet type, migrate associated "ID*" types to "const ID*" logic (other than in profiling) for assignments that yield separate values option for internal use to mark a function type as allowing non-expression returns removed some now-obsolete profiling functionality minor commenting clarifications
5543 lines
120 KiB
C++
5543 lines
120 KiB
C++
// See the file "COPYING" in the main distribution directory for copyright.
|
|
|
|
#include "zeek/Expr.h"
|
|
|
|
#include "zeek/zeek-config.h"
|
|
|
|
#include "zeek/DebugLogger.h"
|
|
#include "zeek/Desc.h"
|
|
#include "zeek/Event.h"
|
|
#include "zeek/EventRegistry.h"
|
|
#include "zeek/Frame.h"
|
|
#include "zeek/Func.h"
|
|
#include "zeek/Hash.h"
|
|
#include "zeek/IPAddr.h"
|
|
#include "zeek/RE.h"
|
|
#include "zeek/RunState.h"
|
|
#include "zeek/Scope.h"
|
|
#include "zeek/Stmt.h"
|
|
#include "zeek/Traverse.h"
|
|
#include "zeek/Trigger.h"
|
|
#include "zeek/broker/Data.h"
|
|
#include "zeek/digest.h"
|
|
#include "zeek/module_util.h"
|
|
#include "zeek/script_opt/ExprOptInfo.h"
|
|
|
|
namespace zeek::detail
|
|
{
|
|
|
|
static bool init_tag_check(const Expr* expr, const char* name, TypeTag expect_tag, TypeTag init_tag)
|
|
{
|
|
if ( expect_tag == init_tag )
|
|
return true;
|
|
|
|
auto msg = util::fmt("unexpected use of %s in '%s' initialization", name, type_name(init_tag));
|
|
expr->Error(msg);
|
|
return false;
|
|
}
|
|
|
|
const char* expr_name(BroExprTag t)
|
|
{
|
|
static const char* expr_names[int(NUM_EXPRS)] = {
|
|
"name",
|
|
"const",
|
|
"(*)",
|
|
"++",
|
|
"--",
|
|
"!",
|
|
"~",
|
|
"+",
|
|
"-",
|
|
"+",
|
|
"-",
|
|
"+=",
|
|
"-=",
|
|
"*",
|
|
"/",
|
|
"%",
|
|
"&",
|
|
"|",
|
|
"^",
|
|
"&&",
|
|
"||",
|
|
"<",
|
|
"<=",
|
|
"==",
|
|
"!=",
|
|
">=",
|
|
">",
|
|
"?:",
|
|
"ref",
|
|
"=",
|
|
"[]",
|
|
"$",
|
|
"?$",
|
|
"[=]",
|
|
"table()",
|
|
"set()",
|
|
"vector()",
|
|
"$=",
|
|
"in",
|
|
"<<>>",
|
|
"()",
|
|
"function()",
|
|
"event",
|
|
"schedule",
|
|
"coerce",
|
|
"record_coerce",
|
|
"table_coerce",
|
|
"vector_coerce",
|
|
"sizeof",
|
|
"cast",
|
|
"is",
|
|
"[:]=",
|
|
"inline()",
|
|
"[]=",
|
|
"$=",
|
|
"vec+=",
|
|
"to_any_coerce",
|
|
"from_any_coerce",
|
|
"from_any_vec_coerce",
|
|
"any[]",
|
|
"nop",
|
|
|
|
};
|
|
|
|
if ( int(t) >= NUM_EXPRS )
|
|
{
|
|
static char errbuf[512];
|
|
|
|
// This isn't quite right - we return a static buffer,
|
|
// so multiple calls to expr_name() could lead to confusion
|
|
// by overwriting the buffer. But oh well.
|
|
snprintf(errbuf, sizeof(errbuf), "%d: not an expression tag", int(t));
|
|
return errbuf;
|
|
}
|
|
|
|
return expr_names[int(t)];
|
|
}
|
|
|
|
Expr::Expr(BroExprTag arg_tag) : tag(arg_tag), paren(false), type(nullptr)
|
|
{
|
|
SetLocationInfo(&start_location, &end_location);
|
|
opt_info = new ExprOptInfo();
|
|
}
|
|
|
|
Expr::~Expr()
|
|
{
|
|
delete opt_info;
|
|
}
|
|
|
|
const ListExpr* Expr::AsListExpr() const
|
|
{
|
|
CHECK_TAG(tag, EXPR_LIST, "Expr::AsListExpr", expr_name)
|
|
return (const ListExpr*)this;
|
|
}
|
|
|
|
ListExpr* Expr::AsListExpr()
|
|
{
|
|
CHECK_TAG(tag, EXPR_LIST, "Expr::AsListExpr", expr_name)
|
|
return (ListExpr*)this;
|
|
}
|
|
|
|
ListExprPtr Expr::AsListExprPtr()
|
|
{
|
|
CHECK_TAG(tag, EXPR_LIST, "Expr::AsListExpr", expr_name)
|
|
return {NewRef{}, (ListExpr*)this};
|
|
}
|
|
|
|
const NameExpr* Expr::AsNameExpr() const
|
|
{
|
|
CHECK_TAG(tag, EXPR_NAME, "Expr::AsNameExpr", expr_name)
|
|
return (const NameExpr*)this;
|
|
}
|
|
|
|
NameExpr* Expr::AsNameExpr()
|
|
{
|
|
CHECK_TAG(tag, EXPR_NAME, "Expr::AsNameExpr", expr_name)
|
|
return (NameExpr*)this;
|
|
}
|
|
|
|
NameExprPtr Expr::AsNameExprPtr()
|
|
{
|
|
CHECK_TAG(tag, EXPR_NAME, "Expr::AsNameExpr", expr_name)
|
|
return {NewRef{}, (NameExpr*)this};
|
|
}
|
|
|
|
const ConstExpr* Expr::AsConstExpr() const
|
|
{
|
|
CHECK_TAG(tag, EXPR_CONST, "Expr::AsConstExpr", expr_name)
|
|
return (const ConstExpr*)this;
|
|
}
|
|
|
|
ConstExprPtr Expr::AsConstExprPtr()
|
|
{
|
|
CHECK_TAG(tag, EXPR_CONST, "Expr::AsConstExpr", expr_name)
|
|
return {NewRef{}, (ConstExpr*)this};
|
|
}
|
|
|
|
const CallExpr* Expr::AsCallExpr() const
|
|
{
|
|
CHECK_TAG(tag, EXPR_CALL, "Expr::AsCallExpr", expr_name)
|
|
return (const CallExpr*)this;
|
|
}
|
|
|
|
const AssignExpr* Expr::AsAssignExpr() const
|
|
{
|
|
CHECK_TAG(tag, EXPR_ASSIGN, "Expr::AsAssignExpr", expr_name)
|
|
return (const AssignExpr*)this;
|
|
}
|
|
|
|
AssignExpr* Expr::AsAssignExpr()
|
|
{
|
|
CHECK_TAG(tag, EXPR_ASSIGN, "Expr::AsAssignExpr", expr_name)
|
|
return (AssignExpr*)this;
|
|
}
|
|
|
|
const IndexExpr* Expr::AsIndexExpr() const
|
|
{
|
|
CHECK_TAG(tag, EXPR_INDEX, "Expr::AsIndexExpr", expr_name)
|
|
return (const IndexExpr*)this;
|
|
}
|
|
|
|
IndexExpr* Expr::AsIndexExpr()
|
|
{
|
|
CHECK_TAG(tag, EXPR_INDEX, "Expr::AsIndexExpr", expr_name)
|
|
return (IndexExpr*)this;
|
|
}
|
|
|
|
const EventExpr* Expr::AsEventExpr() const
|
|
{
|
|
CHECK_TAG(tag, EXPR_EVENT, "Expr::AsEventExpr", expr_name)
|
|
return (const EventExpr*)this;
|
|
}
|
|
|
|
EventExprPtr Expr::AsEventExprPtr()
|
|
{
|
|
CHECK_TAG(tag, EXPR_EVENT, "Expr::AsEventExpr", expr_name)
|
|
return {NewRef{}, (EventExpr*)this};
|
|
}
|
|
|
|
const RefExpr* Expr::AsRefExpr() const
|
|
{
|
|
CHECK_TAG(tag, EXPR_REF, "Expr::AsRefExpr", expr_name)
|
|
return (const RefExpr*)this;
|
|
}
|
|
|
|
RefExprPtr Expr::AsRefExprPtr()
|
|
{
|
|
CHECK_TAG(tag, EXPR_REF, "Expr::AsRefExpr", expr_name)
|
|
return {NewRef{}, (RefExpr*)this};
|
|
}
|
|
|
|
bool Expr::CanAdd() const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool Expr::CanDel() const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
void Expr::Add(Frame* /* f */)
|
|
{
|
|
Internal("Expr::Add called");
|
|
}
|
|
|
|
void Expr::Delete(Frame* /* f */)
|
|
{
|
|
Internal("Expr::Delete called");
|
|
}
|
|
|
|
ExprPtr Expr::MakeLvalue()
|
|
{
|
|
if ( ! IsError() )
|
|
ExprError("can't be assigned to");
|
|
|
|
return {NewRef{}, this};
|
|
}
|
|
|
|
bool Expr::InvertSense()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
void Expr::EvalIntoAggregate(const TypePtr& /* t */, ValPtr /* aggr */, Frame* /* f */) const
|
|
{
|
|
Internal("Expr::EvalIntoAggregate called");
|
|
}
|
|
|
|
void Expr::Assign(Frame* /* f */, ValPtr /* v */)
|
|
{
|
|
Internal("Expr::Assign called");
|
|
}
|
|
|
|
void Expr::AssignToIndex(ValPtr v1, ValPtr v2, ValPtr v3) const
|
|
{
|
|
bool iterators_invalidated;
|
|
|
|
auto error_msg = assign_to_index(std::move(v1), std::move(v2), std::move(v3),
|
|
iterators_invalidated);
|
|
|
|
if ( iterators_invalidated )
|
|
{
|
|
ODesc d;
|
|
Describe(&d);
|
|
reporter->PushLocation(GetLocationInfo());
|
|
reporter->Warning("possible loop/iterator invalidation caused by expression: %s",
|
|
d.Description());
|
|
reporter->PopLocation();
|
|
}
|
|
|
|
if ( error_msg )
|
|
RuntimeErrorWithCallStack(error_msg);
|
|
}
|
|
|
|
static int get_slice_index(int idx, int len)
|
|
{
|
|
if ( abs(idx) > len )
|
|
idx = idx > 0 ? len : 0; // Clamp maximum positive/negative indices.
|
|
else if ( idx < 0 )
|
|
idx += len; // Map to a positive index.
|
|
|
|
return idx;
|
|
}
|
|
|
|
const char* assign_to_index(ValPtr v1, ValPtr v2, ValPtr v3, bool& iterators_invalidated)
|
|
{
|
|
iterators_invalidated = false;
|
|
|
|
if ( ! v1 || ! v2 || ! v3 )
|
|
return nullptr;
|
|
|
|
// Hold an extra reference in case the ownership transfer
|
|
// to the table/vector goes wrong and we still want to obtain
|
|
// diagnostic info from the original value after the assignment
|
|
// already unref'd.
|
|
auto v_extra = v3;
|
|
|
|
switch ( v1->GetType()->Tag() )
|
|
{
|
|
case TYPE_VECTOR:
|
|
{
|
|
const ListVal* lv = v2->AsListVal();
|
|
VectorVal* v1_vect = v1->AsVectorVal();
|
|
|
|
if ( lv->Length() > 1 )
|
|
{
|
|
auto len = v1_vect->Size();
|
|
bro_int_t first = get_slice_index(lv->Idx(0)->CoerceToInt(), len);
|
|
bro_int_t last = get_slice_index(lv->Idx(1)->CoerceToInt(), len);
|
|
|
|
// Remove the elements from the vector within the slice.
|
|
for ( auto idx = first; idx < last; idx++ )
|
|
v1_vect->Remove(first);
|
|
|
|
// Insert the new elements starting at the first
|
|
// position.
|
|
|
|
VectorVal* v_vect = v3->AsVectorVal();
|
|
|
|
for ( auto idx = 0u; idx < v_vect->Size(); idx++, first++ )
|
|
v1_vect->Insert(first, v_vect->ValAt(idx));
|
|
}
|
|
|
|
else if ( ! v1_vect->Assign(lv->Idx(0)->CoerceToUnsigned(), std::move(v3)) )
|
|
{
|
|
v3 = std::move(v_extra);
|
|
|
|
if ( v3 )
|
|
{
|
|
ODesc d;
|
|
v3->Describe(&d);
|
|
const auto& vt = v3->GetType();
|
|
auto vtt = vt->Tag();
|
|
std::string tn = vtt == TYPE_RECORD ? vt->GetName() : type_name(vtt);
|
|
return util::fmt(
|
|
"vector index assignment failed for invalid type '%s', value: %s",
|
|
tn.data(), d.Description());
|
|
}
|
|
else
|
|
return "assignment failed with null value";
|
|
}
|
|
break;
|
|
}
|
|
|
|
case TYPE_TABLE:
|
|
{
|
|
if ( ! v1->AsTableVal()->Assign(std::move(v2), std::move(v3), true,
|
|
&iterators_invalidated) )
|
|
{
|
|
v3 = std::move(v_extra);
|
|
|
|
if ( v3 )
|
|
{
|
|
ODesc d;
|
|
v3->Describe(&d);
|
|
const auto& vt = v3->GetType();
|
|
auto vtt = vt->Tag();
|
|
std::string tn = vtt == TYPE_RECORD ? vt->GetName() : type_name(vtt);
|
|
return util::fmt(
|
|
"table index assignment failed for invalid type '%s', value: %s", tn.data(),
|
|
d.Description());
|
|
}
|
|
else
|
|
return "assignment failed with null value";
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case TYPE_STRING:
|
|
return "assignment via string index accessor not allowed";
|
|
break;
|
|
|
|
default:
|
|
return "bad index expression type in assignment";
|
|
break;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
TypePtr Expr::InitType() const
|
|
{
|
|
return type;
|
|
}
|
|
|
|
bool Expr::IsRecordElement(TypeDecl* /* td */) const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool Expr::IsPure() const
|
|
{
|
|
return true;
|
|
}
|
|
|
|
ValPtr Expr::InitVal(const TypePtr& t, ValPtr aggr) const
|
|
{
|
|
if ( aggr )
|
|
{
|
|
Error("bad initializer");
|
|
return nullptr;
|
|
}
|
|
|
|
if ( IsError() )
|
|
return nullptr;
|
|
|
|
return check_and_promote(Eval(nullptr), t, true);
|
|
}
|
|
|
|
bool Expr::IsError() const
|
|
{
|
|
return type && type->Tag() == TYPE_ERROR;
|
|
}
|
|
|
|
void Expr::SetError()
|
|
{
|
|
SetType(error_type());
|
|
}
|
|
|
|
void Expr::SetError(const char* msg)
|
|
{
|
|
Error(msg);
|
|
SetError();
|
|
}
|
|
|
|
bool Expr::IsZero() const
|
|
{
|
|
return IsConst() && ExprVal()->IsZero();
|
|
}
|
|
|
|
bool Expr::IsOne() const
|
|
{
|
|
return IsConst() && ExprVal()->IsOne();
|
|
}
|
|
|
|
void Expr::Describe(ODesc* d) const
|
|
{
|
|
if ( IsParen() && ! d->IsBinary() )
|
|
d->Add("(");
|
|
|
|
if ( d->IsPortable() || d->IsBinary() )
|
|
AddTag(d);
|
|
|
|
ExprDescribe(d);
|
|
|
|
if ( IsParen() && ! d->IsBinary() )
|
|
d->Add(")");
|
|
}
|
|
|
|
void Expr::AddTag(ODesc* d) const
|
|
{
|
|
if ( d->IsBinary() )
|
|
d->Add(int(Tag()));
|
|
else
|
|
d->AddSP(expr_name(Tag()));
|
|
}
|
|
|
|
void Expr::Canonicize() { }
|
|
|
|
void Expr::SetType(TypePtr t)
|
|
{
|
|
if ( ! type || type->Tag() != TYPE_ERROR )
|
|
type = std::move(t);
|
|
}
|
|
|
|
void Expr::ExprError(const char msg[])
|
|
{
|
|
Error(msg);
|
|
SetError();
|
|
}
|
|
|
|
void Expr::RuntimeError(const std::string& msg) const
|
|
{
|
|
reporter->ExprRuntimeError(this, "%s", msg.data());
|
|
}
|
|
|
|
void Expr::RuntimeErrorWithCallStack(const std::string& msg) const
|
|
{
|
|
auto rcs = render_call_stack();
|
|
|
|
if ( rcs.empty() )
|
|
reporter->ExprRuntimeError(this, "%s", msg.data());
|
|
else
|
|
{
|
|
ODesc d;
|
|
d.SetShort();
|
|
Describe(&d);
|
|
reporter->RuntimeError(GetLocationInfo(), "%s, expression: %s, call stack: %s", msg.data(),
|
|
d.Description(), rcs.data());
|
|
}
|
|
}
|
|
|
|
NameExpr::NameExpr(IDPtr arg_id, bool const_init) : Expr(EXPR_NAME), id(std::move(arg_id))
|
|
{
|
|
in_const_init = const_init;
|
|
|
|
if ( id->IsType() )
|
|
SetType(make_intrusive<TypeType>(id->GetType()));
|
|
else
|
|
SetType(id->GetType());
|
|
|
|
EventHandler* h = event_registry->Lookup(id->Name());
|
|
if ( h )
|
|
h->SetUsed();
|
|
}
|
|
|
|
// This isn't in-lined to avoid needing to pull in ID.h.
|
|
const IDPtr& NameExpr::IdPtr() const
|
|
{
|
|
return id;
|
|
}
|
|
|
|
ValPtr NameExpr::Eval(Frame* f) const
|
|
{
|
|
ValPtr v;
|
|
|
|
if ( id->IsType() )
|
|
return make_intrusive<TypeVal>(id->GetType(), true);
|
|
|
|
if ( id->IsGlobal() )
|
|
v = id->GetVal();
|
|
|
|
else if ( f )
|
|
v = f->GetElementByID(id);
|
|
|
|
else
|
|
// No frame - evaluating for purposes of resolving a
|
|
// compile-time constant.
|
|
return nullptr;
|
|
|
|
if ( v )
|
|
return v;
|
|
else
|
|
{
|
|
RuntimeError("value used but not set");
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
ExprPtr NameExpr::MakeLvalue()
|
|
{
|
|
if ( id->IsType() )
|
|
ExprError("Type name is not an lvalue");
|
|
|
|
if ( id->IsConst() && ! in_const_init )
|
|
ExprError("const is not a modifiable lvalue");
|
|
|
|
if ( id->IsOption() && ! in_const_init )
|
|
ExprError("option is not a modifiable lvalue");
|
|
|
|
return make_intrusive<RefExpr>(IntrusivePtr{NewRef{}, this});
|
|
}
|
|
|
|
void NameExpr::Assign(Frame* f, ValPtr v)
|
|
{
|
|
if ( id->IsGlobal() )
|
|
id->SetVal(std::move(v));
|
|
else
|
|
f->SetElement(id, std::move(v));
|
|
}
|
|
|
|
bool NameExpr::IsPure() const
|
|
{
|
|
return id->IsConst();
|
|
}
|
|
|
|
TraversalCode NameExpr::Traverse(TraversalCallback* cb) const
|
|
{
|
|
TraversalCode tc = cb->PreExpr(this);
|
|
HANDLE_TC_EXPR_PRE(tc);
|
|
|
|
tc = id->Traverse(cb);
|
|
HANDLE_TC_EXPR_PRE(tc);
|
|
|
|
tc = cb->PostExpr(this);
|
|
HANDLE_TC_EXPR_POST(tc);
|
|
}
|
|
|
|
void NameExpr::ExprDescribe(ODesc* d) const
|
|
{
|
|
if ( d->IsReadable() )
|
|
d->Add(id->Name());
|
|
else
|
|
{
|
|
if ( d->IsPortable() )
|
|
d->Add(id->Name());
|
|
else
|
|
d->AddCS(id->Name());
|
|
}
|
|
}
|
|
|
|
ConstExpr::ConstExpr(ValPtr arg_val) : Expr(EXPR_CONST), val(std::move(arg_val))
|
|
{
|
|
if ( val )
|
|
{
|
|
if ( val->GetType()->Tag() == TYPE_LIST && val->AsListVal()->Length() == 1 )
|
|
val = val->AsListVal()->Idx(0);
|
|
|
|
SetType(val->GetType());
|
|
}
|
|
else
|
|
SetError();
|
|
}
|
|
|
|
void ConstExpr::ExprDescribe(ODesc* d) const
|
|
{
|
|
val->Describe(d);
|
|
}
|
|
|
|
ValPtr ConstExpr::Eval(Frame* /* f */) const
|
|
{
|
|
return {NewRef{}, Value()};
|
|
}
|
|
|
|
TraversalCode ConstExpr::Traverse(TraversalCallback* cb) const
|
|
{
|
|
TraversalCode tc = cb->PreExpr(this);
|
|
HANDLE_TC_EXPR_PRE(tc);
|
|
|
|
tc = cb->PostExpr(this);
|
|
HANDLE_TC_EXPR_POST(tc);
|
|
}
|
|
|
|
UnaryExpr::UnaryExpr(BroExprTag arg_tag, ExprPtr arg_op) : Expr(arg_tag), op(std::move(arg_op))
|
|
{
|
|
if ( op->IsError() )
|
|
SetError();
|
|
}
|
|
|
|
ValPtr UnaryExpr::Eval(Frame* f) const
|
|
{
|
|
if ( IsError() )
|
|
return nullptr;
|
|
|
|
auto v = op->Eval(f);
|
|
|
|
if ( ! v )
|
|
return nullptr;
|
|
|
|
if ( is_vector(v) && Tag() != EXPR_IS && Tag() != EXPR_CAST &&
|
|
// The following allows passing vectors-by-reference to
|
|
// functions that use vector-of-any for generic vector
|
|
// manipulation ...
|
|
Tag() != EXPR_TO_ANY_COERCE &&
|
|
// ... and the following to avoid vectorizing operations
|
|
// on vector-of-any's
|
|
Tag() != EXPR_FROM_ANY_COERCE )
|
|
{
|
|
VectorVal* v_op = v->AsVectorVal();
|
|
VectorTypePtr out_t;
|
|
|
|
if ( GetType()->Tag() == TYPE_ANY )
|
|
out_t = v->GetType<VectorType>();
|
|
else
|
|
out_t = GetType<VectorType>();
|
|
|
|
auto result = make_intrusive<VectorVal>(std::move(out_t));
|
|
|
|
for ( unsigned int i = 0; i < v_op->Size(); ++i )
|
|
{
|
|
auto vop = v_op->ValAt(i);
|
|
if ( vop )
|
|
result->Assign(i, Fold(vop.get()));
|
|
else
|
|
result->Assign(i, nullptr);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
else
|
|
return Fold(v.get());
|
|
}
|
|
|
|
bool UnaryExpr::IsPure() const
|
|
{
|
|
return op->IsPure();
|
|
}
|
|
|
|
TraversalCode UnaryExpr::Traverse(TraversalCallback* cb) const
|
|
{
|
|
TraversalCode tc = cb->PreExpr(this);
|
|
HANDLE_TC_EXPR_PRE(tc);
|
|
|
|
tc = op->Traverse(cb);
|
|
HANDLE_TC_EXPR_PRE(tc);
|
|
|
|
tc = cb->PostExpr(this);
|
|
HANDLE_TC_EXPR_POST(tc);
|
|
}
|
|
|
|
ValPtr UnaryExpr::Fold(Val* v) const
|
|
{
|
|
return {NewRef{}, v};
|
|
}
|
|
|
|
void UnaryExpr::ExprDescribe(ODesc* d) const
|
|
{
|
|
bool is_coerce = Tag() == EXPR_ARITH_COERCE || Tag() == EXPR_RECORD_COERCE ||
|
|
Tag() == EXPR_TABLE_COERCE;
|
|
|
|
if ( d->IsReadable() )
|
|
{
|
|
if ( is_coerce )
|
|
d->Add("(coerce ");
|
|
else if ( Tag() != EXPR_REF )
|
|
d->Add(expr_name(Tag()));
|
|
}
|
|
|
|
op->Describe(d);
|
|
|
|
if ( d->IsReadable() && is_coerce )
|
|
{
|
|
d->Add(" to ");
|
|
GetType()->Describe(d);
|
|
d->Add(")");
|
|
}
|
|
}
|
|
|
|
ValPtr BinaryExpr::Eval(Frame* f) const
|
|
{
|
|
if ( IsError() )
|
|
return nullptr;
|
|
|
|
auto v1 = op1->Eval(f);
|
|
|
|
if ( ! v1 )
|
|
return nullptr;
|
|
|
|
auto v2 = op2->Eval(f);
|
|
|
|
if ( ! v2 )
|
|
return nullptr;
|
|
|
|
bool is_vec1 = is_vector(v1);
|
|
bool is_vec2 = is_vector(v2);
|
|
|
|
if ( is_vec1 && is_vec2 )
|
|
{ // fold pairs of elements
|
|
VectorVal* v_op1 = v1->AsVectorVal();
|
|
VectorVal* v_op2 = v2->AsVectorVal();
|
|
|
|
if ( v_op1->Size() != v_op2->Size() )
|
|
{
|
|
RuntimeError("vector operands are of different sizes");
|
|
return nullptr;
|
|
}
|
|
|
|
auto v_result = make_intrusive<VectorVal>(GetType<VectorType>());
|
|
|
|
for ( unsigned int i = 0; i < v_op1->Size(); ++i )
|
|
{
|
|
auto v1_i = v_op1->ValAt(i);
|
|
auto v2_i = v_op2->ValAt(i);
|
|
if ( v1_i && v2_i )
|
|
v_result->Assign(i, Fold(v_op1->ValAt(i).get(), v_op2->ValAt(i).get()));
|
|
else
|
|
v_result->Assign(i, nullptr);
|
|
}
|
|
|
|
return v_result;
|
|
}
|
|
|
|
if ( IsVector(GetType()->Tag()) && (is_vec1 || is_vec2) )
|
|
{ // fold vector against scalar
|
|
VectorVal* vv = (is_vec1 ? v1 : v2)->AsVectorVal();
|
|
auto v_result = make_intrusive<VectorVal>(GetType<VectorType>());
|
|
|
|
for ( unsigned int i = 0; i < vv->Size(); ++i )
|
|
{
|
|
auto vv_i = vv->ValAt(i);
|
|
if ( vv_i )
|
|
v_result->Assign(i,
|
|
is_vec1 ? Fold(vv_i.get(), v2.get()) : Fold(v1.get(), vv_i.get()));
|
|
else
|
|
v_result->Assign(i, nullptr);
|
|
}
|
|
|
|
return v_result;
|
|
}
|
|
|
|
// scalar op scalar
|
|
return Fold(v1.get(), v2.get());
|
|
}
|
|
|
|
bool BinaryExpr::IsPure() const
|
|
{
|
|
return op1->IsPure() && op2->IsPure();
|
|
}
|
|
|
|
TraversalCode BinaryExpr::Traverse(TraversalCallback* cb) const
|
|
{
|
|
TraversalCode tc = cb->PreExpr(this);
|
|
HANDLE_TC_EXPR_PRE(tc);
|
|
|
|
tc = op1->Traverse(cb);
|
|
HANDLE_TC_EXPR_PRE(tc);
|
|
|
|
tc = op2->Traverse(cb);
|
|
HANDLE_TC_EXPR_PRE(tc);
|
|
|
|
tc = cb->PostExpr(this);
|
|
HANDLE_TC_EXPR_POST(tc);
|
|
}
|
|
|
|
void BinaryExpr::ExprDescribe(ODesc* d) const
|
|
{
|
|
op1->Describe(d);
|
|
|
|
d->SP();
|
|
if ( d->IsReadable() )
|
|
d->AddSP(expr_name(Tag()));
|
|
|
|
op2->Describe(d);
|
|
}
|
|
|
|
ValPtr BinaryExpr::Fold(Val* v1, Val* v2) const
|
|
{
|
|
InternalTypeTag it = v1->GetType()->InternalType();
|
|
|
|
if ( it == TYPE_INTERNAL_STRING )
|
|
return StringFold(v1, v2);
|
|
|
|
if ( v1->GetType()->Tag() == TYPE_PATTERN )
|
|
return PatternFold(v1, v2);
|
|
|
|
if ( v1->GetType()->IsSet() )
|
|
return SetFold(v1, v2);
|
|
|
|
if ( it == TYPE_INTERNAL_ADDR )
|
|
return AddrFold(v1, v2);
|
|
|
|
if ( it == TYPE_INTERNAL_SUBNET )
|
|
return SubNetFold(v1, v2);
|
|
|
|
bro_int_t i1 = 0, i2 = 0, i3 = 0;
|
|
bro_uint_t u1 = 0, u2 = 0, u3 = 0;
|
|
double d1 = 0.0, d2 = 0.0, d3 = 0.0;
|
|
bool is_integral = false;
|
|
bool is_unsigned = false;
|
|
|
|
if ( it == TYPE_INTERNAL_INT )
|
|
{
|
|
i1 = v1->InternalInt();
|
|
i2 = v2->InternalInt();
|
|
is_integral = true;
|
|
}
|
|
else if ( it == TYPE_INTERNAL_UNSIGNED )
|
|
{
|
|
u1 = v1->InternalUnsigned();
|
|
u2 = v2->InternalUnsigned();
|
|
is_unsigned = true;
|
|
}
|
|
else if ( it == TYPE_INTERNAL_DOUBLE )
|
|
{
|
|
d1 = v1->InternalDouble();
|
|
d2 = v2->InternalDouble();
|
|
}
|
|
else
|
|
RuntimeErrorWithCallStack("bad type in BinaryExpr::Fold");
|
|
|
|
switch ( tag )
|
|
{
|
|
#define DO_INT_FOLD(op) \
|
|
if ( is_integral ) \
|
|
i3 = i1 op i2; \
|
|
else if ( is_unsigned ) \
|
|
u3 = u1 op u2; \
|
|
else \
|
|
RuntimeErrorWithCallStack("bad type in BinaryExpr::Fold");
|
|
|
|
#define DO_UINT_FOLD(op) \
|
|
if ( is_unsigned ) \
|
|
u3 = u1 op u2; \
|
|
else \
|
|
RuntimeErrorWithCallStack("bad type in BinaryExpr::Fold");
|
|
|
|
#define DO_FOLD(op) \
|
|
if ( is_integral ) \
|
|
i3 = i1 op i2; \
|
|
else if ( is_unsigned ) \
|
|
u3 = u1 op u2; \
|
|
else \
|
|
d3 = d1 op d2;
|
|
|
|
#define DO_INT_VAL_FOLD(op) \
|
|
if ( is_integral ) \
|
|
i3 = i1 op i2; \
|
|
else if ( is_unsigned ) \
|
|
i3 = u1 op u2; \
|
|
else \
|
|
i3 = d1 op d2;
|
|
|
|
case EXPR_ADD:
|
|
case EXPR_ADD_TO:
|
|
DO_FOLD(+);
|
|
break;
|
|
case EXPR_SUB:
|
|
case EXPR_REMOVE_FROM:
|
|
DO_FOLD(-);
|
|
break;
|
|
case EXPR_TIMES:
|
|
DO_FOLD(*);
|
|
break;
|
|
case EXPR_DIVIDE:
|
|
{
|
|
if ( is_integral )
|
|
{
|
|
if ( i2 == 0 )
|
|
RuntimeError("division by zero");
|
|
|
|
i3 = i1 / i2;
|
|
}
|
|
|
|
else if ( is_unsigned )
|
|
{
|
|
if ( u2 == 0 )
|
|
RuntimeError("division by zero");
|
|
|
|
u3 = u1 / u2;
|
|
}
|
|
else
|
|
{
|
|
if ( d2 == 0 )
|
|
RuntimeError("division by zero");
|
|
|
|
d3 = d1 / d2;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case EXPR_MOD:
|
|
{
|
|
if ( is_integral )
|
|
{
|
|
if ( i2 == 0 )
|
|
RuntimeError("modulo by zero");
|
|
|
|
i3 = i1 % i2;
|
|
}
|
|
|
|
else if ( is_unsigned )
|
|
{
|
|
if ( u2 == 0 )
|
|
RuntimeError("modulo by zero");
|
|
|
|
u3 = u1 % u2;
|
|
}
|
|
|
|
else
|
|
RuntimeErrorWithCallStack("bad type in BinaryExpr::Fold");
|
|
}
|
|
|
|
break;
|
|
|
|
case EXPR_AND:
|
|
DO_UINT_FOLD(&);
|
|
break;
|
|
case EXPR_OR:
|
|
DO_UINT_FOLD(|);
|
|
break;
|
|
case EXPR_XOR:
|
|
DO_UINT_FOLD(^);
|
|
break;
|
|
|
|
case EXPR_AND_AND:
|
|
DO_INT_FOLD(&&);
|
|
break;
|
|
case EXPR_OR_OR:
|
|
DO_INT_FOLD(||);
|
|
break;
|
|
|
|
case EXPR_LT:
|
|
DO_INT_VAL_FOLD(<);
|
|
break;
|
|
case EXPR_LE:
|
|
DO_INT_VAL_FOLD(<=);
|
|
break;
|
|
case EXPR_EQ:
|
|
DO_INT_VAL_FOLD(==);
|
|
break;
|
|
case EXPR_NE:
|
|
DO_INT_VAL_FOLD(!=);
|
|
break;
|
|
case EXPR_GE:
|
|
DO_INT_VAL_FOLD(>=);
|
|
break;
|
|
case EXPR_GT:
|
|
DO_INT_VAL_FOLD(>);
|
|
break;
|
|
|
|
default:
|
|
BadTag("BinaryExpr::Fold", expr_name(tag));
|
|
}
|
|
|
|
const auto& ret_type = IsVector(GetType()->Tag()) ? GetType()->Yield() : GetType();
|
|
|
|
if ( ret_type->Tag() == TYPE_INTERVAL )
|
|
return make_intrusive<IntervalVal>(d3);
|
|
else if ( ret_type->Tag() == TYPE_TIME )
|
|
return make_intrusive<TimeVal>(d3);
|
|
else if ( ret_type->Tag() == TYPE_DOUBLE )
|
|
return make_intrusive<DoubleVal>(d3);
|
|
else if ( ret_type->InternalType() == TYPE_INTERNAL_UNSIGNED )
|
|
return val_mgr->Count(u3);
|
|
else if ( ret_type->Tag() == TYPE_BOOL )
|
|
return val_mgr->Bool(i3);
|
|
else
|
|
return val_mgr->Int(i3);
|
|
}
|
|
|
|
ValPtr BinaryExpr::StringFold(Val* v1, Val* v2) const
|
|
{
|
|
const String* s1 = v1->AsString();
|
|
const String* s2 = v2->AsString();
|
|
int result = 0;
|
|
|
|
switch ( tag )
|
|
{
|
|
#undef DO_FOLD
|
|
#define DO_FOLD(sense) \
|
|
{ \
|
|
result = Bstr_cmp(s1, s2) sense 0; \
|
|
break; \
|
|
}
|
|
|
|
case EXPR_LT:
|
|
DO_FOLD(<)
|
|
case EXPR_LE:
|
|
DO_FOLD(<=)
|
|
case EXPR_EQ:
|
|
DO_FOLD(==)
|
|
case EXPR_NE:
|
|
DO_FOLD(!=)
|
|
case EXPR_GE:
|
|
DO_FOLD(>=)
|
|
case EXPR_GT:
|
|
DO_FOLD(>)
|
|
|
|
case EXPR_ADD:
|
|
case EXPR_ADD_TO:
|
|
{
|
|
std::vector<const String*> strings;
|
|
strings.push_back(s1);
|
|
strings.push_back(s2);
|
|
|
|
return make_intrusive<StringVal>(concatenate(strings));
|
|
}
|
|
|
|
default:
|
|
BadTag("BinaryExpr::StringFold", expr_name(tag));
|
|
}
|
|
|
|
return val_mgr->Bool(result);
|
|
}
|
|
|
|
ValPtr BinaryExpr::PatternFold(Val* v1, Val* v2) const
|
|
{
|
|
const RE_Matcher* re1 = v1->AsPattern();
|
|
const RE_Matcher* re2 = v2->AsPattern();
|
|
|
|
if ( tag != EXPR_AND && tag != EXPR_OR )
|
|
BadTag("BinaryExpr::PatternFold");
|
|
|
|
RE_Matcher* res = tag == EXPR_AND ? RE_Matcher_conjunction(re1, re2)
|
|
: RE_Matcher_disjunction(re1, re2);
|
|
|
|
return make_intrusive<PatternVal>(res);
|
|
}
|
|
|
|
ValPtr BinaryExpr::SetFold(Val* v1, Val* v2) const
|
|
{
|
|
TableVal* tv1 = v1->AsTableVal();
|
|
TableVal* tv2 = v2->AsTableVal();
|
|
bool res = false;
|
|
|
|
switch ( tag )
|
|
{
|
|
case EXPR_AND:
|
|
return tv1->Intersection(*tv2);
|
|
|
|
case EXPR_OR:
|
|
{
|
|
auto rval = v1->Clone();
|
|
|
|
if ( ! tv2->AddTo(rval.get(), false, false) )
|
|
reporter->InternalError("set union failed to type check");
|
|
|
|
return rval;
|
|
}
|
|
|
|
case EXPR_SUB:
|
|
{
|
|
auto rval = v1->Clone();
|
|
|
|
if ( ! tv2->RemoveFrom(rval.get()) )
|
|
reporter->InternalError("set difference failed to type check");
|
|
|
|
return rval;
|
|
}
|
|
|
|
case EXPR_EQ:
|
|
res = tv1->EqualTo(*tv2);
|
|
break;
|
|
|
|
case EXPR_NE:
|
|
res = ! tv1->EqualTo(*tv2);
|
|
break;
|
|
|
|
case EXPR_LT:
|
|
res = tv1->IsSubsetOf(*tv2) && tv1->Size() < tv2->Size();
|
|
break;
|
|
|
|
case EXPR_LE:
|
|
res = tv1->IsSubsetOf(*tv2);
|
|
break;
|
|
|
|
case EXPR_GE:
|
|
case EXPR_GT:
|
|
// These should't happen due to canonicalization.
|
|
reporter->InternalError("confusion over canonicalization in set comparison");
|
|
break;
|
|
|
|
default:
|
|
BadTag("BinaryExpr::SetFold", expr_name(tag));
|
|
return nullptr;
|
|
}
|
|
|
|
return val_mgr->Bool(res);
|
|
}
|
|
|
|
ValPtr BinaryExpr::AddrFold(Val* v1, Val* v2) const
|
|
{
|
|
IPAddr a1 = v1->AsAddr();
|
|
IPAddr a2 = v2->AsAddr();
|
|
bool result = false;
|
|
|
|
switch ( tag )
|
|
{
|
|
|
|
case EXPR_LT:
|
|
result = a1 < a2;
|
|
break;
|
|
case EXPR_LE:
|
|
result = a1 < a2 || a1 == a2;
|
|
break;
|
|
case EXPR_EQ:
|
|
result = a1 == a2;
|
|
break;
|
|
case EXPR_NE:
|
|
result = a1 != a2;
|
|
break;
|
|
case EXPR_GE:
|
|
result = ! (a1 < a2);
|
|
break;
|
|
case EXPR_GT:
|
|
result = (! (a1 < a2)) && (a1 != a2);
|
|
break;
|
|
|
|
default:
|
|
BadTag("BinaryExpr::AddrFold", expr_name(tag));
|
|
}
|
|
|
|
return val_mgr->Bool(result);
|
|
}
|
|
|
|
ValPtr BinaryExpr::SubNetFold(Val* v1, Val* v2) const
|
|
{
|
|
const IPPrefix& n1 = v1->AsSubNet();
|
|
const IPPrefix& n2 = v2->AsSubNet();
|
|
|
|
bool result = n1 == n2;
|
|
|
|
if ( tag == EXPR_NE )
|
|
result = ! result;
|
|
|
|
return val_mgr->Bool(result);
|
|
}
|
|
|
|
void BinaryExpr::SwapOps()
|
|
{
|
|
// We could check here whether the operator is commutative.
|
|
using std::swap;
|
|
swap(op1, op2);
|
|
}
|
|
|
|
void BinaryExpr::PromoteOps(TypeTag t)
|
|
{
|
|
TypeTag bt1 = op1->GetType()->Tag();
|
|
TypeTag bt2 = op2->GetType()->Tag();
|
|
|
|
bool is_vec1 = IsVector(bt1);
|
|
bool is_vec2 = IsVector(bt2);
|
|
|
|
if ( is_vec1 )
|
|
bt1 = op1->GetType()->AsVectorType()->Yield()->Tag();
|
|
if ( is_vec2 )
|
|
bt2 = op2->GetType()->AsVectorType()->Yield()->Tag();
|
|
|
|
if ( (is_vec1 || is_vec2) && ! (is_vec1 && is_vec2) )
|
|
reporter->Warning("mixing vector and scalar operands is deprecated");
|
|
|
|
if ( bt1 != t )
|
|
op1 = make_intrusive<ArithCoerceExpr>(op1, t);
|
|
if ( bt2 != t )
|
|
op2 = make_intrusive<ArithCoerceExpr>(op2, t);
|
|
}
|
|
|
|
void BinaryExpr::PromoteType(TypeTag t, bool is_vector)
|
|
{
|
|
PromoteOps(t);
|
|
|
|
if ( is_vector )
|
|
SetType(make_intrusive<VectorType>(base_type(t)));
|
|
else
|
|
SetType(base_type(t));
|
|
}
|
|
|
|
void BinaryExpr::PromoteForInterval(ExprPtr& op)
|
|
{
|
|
if ( is_vector(op1) || is_vector(op2) )
|
|
SetType(make_intrusive<VectorType>(base_type(TYPE_INTERVAL)));
|
|
else
|
|
SetType(base_type(TYPE_INTERVAL));
|
|
|
|
if ( op->GetType()->Tag() != TYPE_DOUBLE )
|
|
op = make_intrusive<ArithCoerceExpr>(op, TYPE_DOUBLE);
|
|
}
|
|
|
|
CloneExpr::CloneExpr(ExprPtr arg_op) : UnaryExpr(EXPR_CLONE, std::move(arg_op))
|
|
{
|
|
if ( IsError() )
|
|
return;
|
|
|
|
SetType(op->GetType());
|
|
}
|
|
|
|
ValPtr CloneExpr::Eval(Frame* f) const
|
|
{
|
|
if ( IsError() )
|
|
return nullptr;
|
|
|
|
if ( auto v = op->Eval(f) )
|
|
return Fold(v.get());
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
ValPtr CloneExpr::Fold(Val* v) const
|
|
{
|
|
return v->Clone();
|
|
}
|
|
|
|
IncrExpr::IncrExpr(BroExprTag arg_tag, ExprPtr arg_op) : UnaryExpr(arg_tag, arg_op->MakeLvalue())
|
|
{
|
|
if ( IsError() )
|
|
return;
|
|
|
|
const auto& t = op->GetType();
|
|
|
|
if ( IsVector(t->Tag()) )
|
|
{
|
|
if ( ! IsIntegral(t->AsVectorType()->Yield()->Tag()) )
|
|
ExprError("vector elements must be integral for increment operator");
|
|
else
|
|
{
|
|
reporter->Warning("increment/decrement operations for vectors deprecated");
|
|
SetType(t);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( ! IsIntegral(t->Tag()) )
|
|
ExprError("requires an integral operand");
|
|
else
|
|
SetType(t);
|
|
}
|
|
}
|
|
|
|
ValPtr IncrExpr::DoSingleEval(Frame* f, Val* v) const
|
|
{
|
|
bro_int_t k = v->CoerceToInt();
|
|
|
|
if ( Tag() == EXPR_INCR )
|
|
++k;
|
|
else
|
|
{
|
|
--k;
|
|
|
|
if ( k < 0 && v->GetType()->InternalType() == TYPE_INTERNAL_UNSIGNED )
|
|
RuntimeError("count underflow");
|
|
}
|
|
|
|
const auto& ret_type = IsVector(GetType()->Tag()) ? GetType()->Yield() : GetType();
|
|
|
|
if ( ret_type->Tag() == TYPE_INT )
|
|
return val_mgr->Int(k);
|
|
else
|
|
return val_mgr->Count(k);
|
|
}
|
|
|
|
ValPtr IncrExpr::Eval(Frame* f) const
|
|
{
|
|
auto v = op->Eval(f);
|
|
|
|
if ( ! v )
|
|
return nullptr;
|
|
|
|
if ( is_vector(v) )
|
|
{
|
|
VectorValPtr v_vec{NewRef{}, v->AsVectorVal()};
|
|
|
|
for ( unsigned int i = 0; i < v_vec->Size(); ++i )
|
|
{
|
|
auto elt = v_vec->ValAt(i);
|
|
if ( elt )
|
|
v_vec->Assign(i, DoSingleEval(f, elt.get()));
|
|
}
|
|
|
|
op->Assign(f, std::move(v_vec));
|
|
return v;
|
|
}
|
|
else
|
|
{
|
|
auto new_v = DoSingleEval(f, v.get());
|
|
op->Assign(f, new_v);
|
|
return new_v;
|
|
}
|
|
}
|
|
|
|
bool IncrExpr::IsPure() const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
ComplementExpr::ComplementExpr(ExprPtr arg_op) : UnaryExpr(EXPR_COMPLEMENT, std::move(arg_op))
|
|
{
|
|
if ( IsError() )
|
|
return;
|
|
|
|
const auto& t = op->GetType();
|
|
TypeTag bt = t->Tag();
|
|
|
|
if ( bt != TYPE_COUNT )
|
|
ExprError("requires \"count\" operand");
|
|
else
|
|
SetType(base_type(TYPE_COUNT));
|
|
}
|
|
|
|
ValPtr ComplementExpr::Fold(Val* v) const
|
|
{
|
|
return val_mgr->Count(~v->InternalUnsigned());
|
|
}
|
|
|
|
NotExpr::NotExpr(ExprPtr arg_op) : UnaryExpr(EXPR_NOT, std::move(arg_op))
|
|
{
|
|
if ( IsError() )
|
|
return;
|
|
|
|
TypeTag bt = op->GetType()->Tag();
|
|
|
|
if ( ! IsIntegral(bt) && bt != TYPE_BOOL )
|
|
ExprError("requires an integral or boolean operand");
|
|
else
|
|
SetType(base_type(TYPE_BOOL));
|
|
}
|
|
|
|
ValPtr NotExpr::Fold(Val* v) const
|
|
{
|
|
return val_mgr->Bool(! v->InternalInt());
|
|
}
|
|
|
|
PosExpr::PosExpr(ExprPtr arg_op) : UnaryExpr(EXPR_POSITIVE, std::move(arg_op))
|
|
{
|
|
if ( IsError() )
|
|
return;
|
|
|
|
const auto& t = IsVector(op->GetType()->Tag()) ? op->GetType()->Yield() : op->GetType();
|
|
|
|
TypeTag bt = t->Tag();
|
|
TypePtr base_result_type;
|
|
|
|
if ( IsIntegral(bt) )
|
|
// Promote count and counter to int.
|
|
base_result_type = base_type(TYPE_INT);
|
|
else if ( bt == TYPE_INTERVAL || bt == TYPE_DOUBLE )
|
|
base_result_type = t;
|
|
else
|
|
ExprError("requires an integral or double operand");
|
|
|
|
if ( is_vector(op) )
|
|
SetType(make_intrusive<VectorType>(std::move(base_result_type)));
|
|
else
|
|
SetType(std::move(base_result_type));
|
|
}
|
|
|
|
ValPtr PosExpr::Fold(Val* v) const
|
|
{
|
|
TypeTag t = v->GetType()->Tag();
|
|
|
|
if ( t == TYPE_DOUBLE || t == TYPE_INTERVAL || t == TYPE_INT )
|
|
return {NewRef{}, v};
|
|
else
|
|
return val_mgr->Int(v->CoerceToInt());
|
|
}
|
|
|
|
NegExpr::NegExpr(ExprPtr arg_op) : UnaryExpr(EXPR_NEGATE, std::move(arg_op))
|
|
{
|
|
if ( IsError() )
|
|
return;
|
|
|
|
const auto& t = IsVector(op->GetType()->Tag()) ? op->GetType()->Yield() : op->GetType();
|
|
|
|
TypeTag bt = t->Tag();
|
|
TypePtr base_result_type;
|
|
|
|
if ( IsIntegral(bt) )
|
|
// Promote count and counter to int.
|
|
base_result_type = base_type(TYPE_INT);
|
|
else if ( bt == TYPE_INTERVAL || bt == TYPE_DOUBLE )
|
|
base_result_type = t;
|
|
else
|
|
ExprError("requires an integral or double operand");
|
|
|
|
if ( is_vector(op) )
|
|
SetType(make_intrusive<VectorType>(std::move(base_result_type)));
|
|
else
|
|
SetType(std::move(base_result_type));
|
|
}
|
|
|
|
ValPtr NegExpr::Fold(Val* v) const
|
|
{
|
|
if ( v->GetType()->Tag() == TYPE_DOUBLE )
|
|
return make_intrusive<DoubleVal>(-v->InternalDouble());
|
|
else if ( v->GetType()->Tag() == TYPE_INTERVAL )
|
|
return make_intrusive<IntervalVal>(-v->InternalDouble());
|
|
else
|
|
return val_mgr->Int(-v->CoerceToInt());
|
|
}
|
|
|
|
SizeExpr::SizeExpr(ExprPtr arg_op) : UnaryExpr(EXPR_SIZE, std::move(arg_op))
|
|
{
|
|
if ( IsError() )
|
|
return;
|
|
|
|
auto& t = op->GetType();
|
|
|
|
if ( t->Tag() == TYPE_ANY )
|
|
SetType(base_type(TYPE_ANY));
|
|
else if ( t->Tag() == TYPE_FILE || t->Tag() == TYPE_SUBNET ||
|
|
t->InternalType() == TYPE_INTERNAL_DOUBLE )
|
|
SetType(base_type(TYPE_DOUBLE));
|
|
else
|
|
SetType(base_type(TYPE_COUNT));
|
|
}
|
|
|
|
ValPtr SizeExpr::Eval(Frame* f) const
|
|
{
|
|
auto v = op->Eval(f);
|
|
|
|
if ( ! v )
|
|
return nullptr;
|
|
|
|
return Fold(v.get());
|
|
}
|
|
|
|
ValPtr SizeExpr::Fold(Val* v) const
|
|
{
|
|
return v->SizeVal();
|
|
}
|
|
|
|
AddExpr::AddExpr(ExprPtr arg_op1, ExprPtr arg_op2)
|
|
: BinaryExpr(EXPR_ADD, std::move(arg_op1), std::move(arg_op2))
|
|
{
|
|
if ( IsError() )
|
|
return;
|
|
|
|
TypeTag bt1 = op1->GetType()->Tag();
|
|
|
|
if ( IsVector(bt1) )
|
|
bt1 = op1->GetType()->AsVectorType()->Yield()->Tag();
|
|
|
|
TypeTag bt2 = op2->GetType()->Tag();
|
|
|
|
if ( IsVector(bt2) )
|
|
bt2 = op2->GetType()->AsVectorType()->Yield()->Tag();
|
|
|
|
TypePtr base_result_type;
|
|
|
|
if ( bt2 == TYPE_INTERVAL && (bt1 == TYPE_TIME || bt1 == TYPE_INTERVAL) )
|
|
base_result_type = base_type(bt1);
|
|
else if ( bt2 == TYPE_TIME && bt1 == TYPE_INTERVAL )
|
|
base_result_type = base_type(bt2);
|
|
else if ( BothArithmetic(bt1, bt2) )
|
|
PromoteType(max_type(bt1, bt2), is_vector(op1) || is_vector(op2));
|
|
else if ( BothString(bt1, bt2) )
|
|
base_result_type = base_type(bt1);
|
|
else
|
|
ExprError("requires arithmetic operands");
|
|
|
|
if ( base_result_type )
|
|
{
|
|
if ( is_vector(op1) || is_vector(op2) )
|
|
SetType(make_intrusive<VectorType>(std::move(base_result_type)));
|
|
else
|
|
SetType(std::move(base_result_type));
|
|
}
|
|
}
|
|
|
|
void AddExpr::Canonicize()
|
|
{
|
|
if ( expr_greater(op2.get(), op1.get()) ||
|
|
(op1->GetType()->Tag() == TYPE_INTERVAL && op2->GetType()->Tag() == TYPE_TIME) ||
|
|
(op2->IsConst() && ! is_vector(op2->ExprVal()) && ! op1->IsConst()) )
|
|
SwapOps();
|
|
}
|
|
|
|
AddToExpr::AddToExpr(ExprPtr arg_op1, ExprPtr arg_op2)
|
|
: BinaryExpr(EXPR_ADD_TO, is_vector(arg_op1) ? std::move(arg_op1) : arg_op1->MakeLvalue(),
|
|
std::move(arg_op2))
|
|
{
|
|
if ( IsError() )
|
|
return;
|
|
|
|
TypeTag bt1 = op1->GetType()->Tag();
|
|
TypeTag bt2 = op2->GetType()->Tag();
|
|
|
|
if ( BothArithmetic(bt1, bt2) )
|
|
PromoteType(max_type(bt1, bt2), is_vector(op1) || is_vector(op2));
|
|
else if ( BothString(bt1, bt2) || BothInterval(bt1, bt2) )
|
|
SetType(base_type(bt1));
|
|
|
|
else if ( IsVector(bt1) )
|
|
{
|
|
bt1 = op1->GetType()->AsVectorType()->Yield()->Tag();
|
|
|
|
if ( IsArithmetic(bt1) )
|
|
{
|
|
if ( IsArithmetic(bt2) )
|
|
{
|
|
if ( bt2 != bt1 )
|
|
op2 = make_intrusive<ArithCoerceExpr>(std::move(op2), bt1);
|
|
|
|
SetType(op1->GetType());
|
|
}
|
|
|
|
else
|
|
ExprError("appending non-arithmetic to arithmetic vector");
|
|
}
|
|
|
|
else if ( bt1 != bt2 && bt1 != TYPE_ANY )
|
|
ExprError(
|
|
util::fmt("incompatible vector append: %s and %s", type_name(bt1), type_name(bt2)));
|
|
|
|
else
|
|
SetType(op1->GetType());
|
|
}
|
|
|
|
else
|
|
ExprError("requires two arithmetic or two string operands");
|
|
}
|
|
|
|
ValPtr AddToExpr::Eval(Frame* f) const
|
|
{
|
|
auto v1 = op1->Eval(f);
|
|
|
|
if ( ! v1 )
|
|
return nullptr;
|
|
|
|
auto v2 = op2->Eval(f);
|
|
|
|
if ( ! v2 )
|
|
return nullptr;
|
|
|
|
if ( is_vector(v1) )
|
|
{
|
|
VectorVal* vv = v1->AsVectorVal();
|
|
|
|
if ( ! vv->Assign(vv->Size(), v2) )
|
|
RuntimeError("type-checking failed in vector append");
|
|
|
|
return v1;
|
|
}
|
|
|
|
if ( auto result = Fold(v1.get(), v2.get()) )
|
|
{
|
|
op1->Assign(f, result);
|
|
return result;
|
|
}
|
|
else
|
|
return nullptr;
|
|
}
|
|
|
|
SubExpr::SubExpr(ExprPtr arg_op1, ExprPtr arg_op2)
|
|
: BinaryExpr(EXPR_SUB, std::move(arg_op1), std::move(arg_op2))
|
|
{
|
|
if ( IsError() )
|
|
return;
|
|
|
|
const auto& t1 = op1->GetType();
|
|
const auto& t2 = op2->GetType();
|
|
|
|
TypeTag bt1 = t1->Tag();
|
|
if ( IsVector(bt1) )
|
|
bt1 = t1->AsVectorType()->Yield()->Tag();
|
|
|
|
TypeTag bt2 = t2->Tag();
|
|
if ( IsVector(bt2) )
|
|
bt2 = t2->AsVectorType()->Yield()->Tag();
|
|
|
|
TypePtr base_result_type;
|
|
|
|
if ( bt2 == TYPE_INTERVAL && (bt1 == TYPE_TIME || bt1 == TYPE_INTERVAL) )
|
|
base_result_type = base_type(bt1);
|
|
|
|
else if ( bt1 == TYPE_TIME && bt2 == TYPE_TIME )
|
|
SetType(base_type(TYPE_INTERVAL));
|
|
|
|
else if ( t1->IsSet() && t2->IsSet() )
|
|
{
|
|
if ( same_type(t1, t2) )
|
|
SetType(op1->GetType());
|
|
else
|
|
ExprError("incompatible \"set\" operands");
|
|
}
|
|
|
|
else if ( BothArithmetic(bt1, bt2) )
|
|
PromoteType(max_type(bt1, bt2), is_vector(op1) || is_vector(op2));
|
|
|
|
else
|
|
ExprError("requires arithmetic operands");
|
|
|
|
if ( base_result_type )
|
|
{
|
|
if ( is_vector(op1) || is_vector(op2) )
|
|
SetType(make_intrusive<VectorType>(std::move(base_result_type)));
|
|
else
|
|
SetType(std::move(base_result_type));
|
|
}
|
|
}
|
|
|
|
RemoveFromExpr::RemoveFromExpr(ExprPtr arg_op1, ExprPtr arg_op2)
|
|
: BinaryExpr(EXPR_REMOVE_FROM, arg_op1->MakeLvalue(), std::move(arg_op2))
|
|
{
|
|
if ( IsError() )
|
|
return;
|
|
|
|
TypeTag bt1 = op1->GetType()->Tag();
|
|
TypeTag bt2 = op2->GetType()->Tag();
|
|
|
|
if ( BothArithmetic(bt1, bt2) )
|
|
PromoteType(max_type(bt1, bt2), is_vector(op1) || is_vector(op2));
|
|
else if ( BothInterval(bt1, bt2) )
|
|
SetType(base_type(bt1));
|
|
else
|
|
ExprError("requires two arithmetic operands");
|
|
}
|
|
|
|
ValPtr RemoveFromExpr::Eval(Frame* f) const
|
|
{
|
|
auto v1 = op1->Eval(f);
|
|
|
|
if ( ! v1 )
|
|
return nullptr;
|
|
|
|
auto v2 = op2->Eval(f);
|
|
|
|
if ( ! v2 )
|
|
return nullptr;
|
|
|
|
if ( auto result = Fold(v1.get(), v2.get()) )
|
|
{
|
|
op1->Assign(f, result);
|
|
return result;
|
|
}
|
|
else
|
|
return nullptr;
|
|
}
|
|
|
|
TimesExpr::TimesExpr(ExprPtr arg_op1, ExprPtr arg_op2)
|
|
: BinaryExpr(EXPR_TIMES, std::move(arg_op1), std::move(arg_op2))
|
|
{
|
|
if ( IsError() )
|
|
return;
|
|
|
|
Canonicize();
|
|
|
|
TypeTag bt1 = op1->GetType()->Tag();
|
|
|
|
if ( IsVector(bt1) )
|
|
bt1 = op1->GetType()->AsVectorType()->Yield()->Tag();
|
|
|
|
TypeTag bt2 = op2->GetType()->Tag();
|
|
|
|
if ( IsVector(bt2) )
|
|
bt2 = op2->GetType()->AsVectorType()->Yield()->Tag();
|
|
|
|
if ( bt1 == TYPE_INTERVAL || bt2 == TYPE_INTERVAL )
|
|
{
|
|
if ( IsArithmetic(bt1) || IsArithmetic(bt2) )
|
|
PromoteForInterval(IsArithmetic(bt1) ? op1 : op2);
|
|
else
|
|
ExprError("multiplication with interval requires arithmetic operand");
|
|
}
|
|
else if ( BothArithmetic(bt1, bt2) )
|
|
PromoteType(max_type(bt1, bt2), is_vector(op1) || is_vector(op2));
|
|
else
|
|
ExprError("requires arithmetic operands");
|
|
}
|
|
|
|
void TimesExpr::Canonicize()
|
|
{
|
|
if ( expr_greater(op2.get(), op1.get()) || op2->GetType()->Tag() == TYPE_INTERVAL ||
|
|
(op2->IsConst() && ! is_vector(op2->ExprVal()) && ! op1->IsConst()) )
|
|
SwapOps();
|
|
}
|
|
|
|
DivideExpr::DivideExpr(ExprPtr arg_op1, ExprPtr arg_op2)
|
|
: BinaryExpr(EXPR_DIVIDE, std::move(arg_op1), std::move(arg_op2))
|
|
{
|
|
if ( IsError() )
|
|
return;
|
|
|
|
TypeTag bt1 = op1->GetType()->Tag();
|
|
|
|
if ( IsVector(bt1) )
|
|
bt1 = op1->GetType()->AsVectorType()->Yield()->Tag();
|
|
|
|
TypeTag bt2 = op2->GetType()->Tag();
|
|
|
|
if ( IsVector(bt2) )
|
|
bt2 = op2->GetType()->AsVectorType()->Yield()->Tag();
|
|
|
|
if ( bt1 == TYPE_INTERVAL || bt2 == TYPE_INTERVAL )
|
|
{
|
|
if ( IsArithmetic(bt1) || IsArithmetic(bt2) )
|
|
PromoteForInterval(IsArithmetic(bt1) ? op1 : op2);
|
|
else if ( bt1 == TYPE_INTERVAL && bt2 == TYPE_INTERVAL )
|
|
{
|
|
if ( is_vector(op1) || is_vector(op2) )
|
|
SetType(make_intrusive<VectorType>(base_type(TYPE_DOUBLE)));
|
|
else
|
|
SetType(base_type(TYPE_DOUBLE));
|
|
}
|
|
else
|
|
ExprError("division of interval requires arithmetic operand");
|
|
}
|
|
|
|
else if ( BothArithmetic(bt1, bt2) )
|
|
PromoteType(max_type(bt1, bt2), is_vector(op1) || is_vector(op2));
|
|
|
|
else if ( bt1 == TYPE_ADDR && ! is_vector(op2) && (bt2 == TYPE_COUNT || bt2 == TYPE_INT) )
|
|
SetType(base_type(TYPE_SUBNET));
|
|
|
|
else
|
|
ExprError("requires arithmetic operands");
|
|
}
|
|
|
|
ValPtr DivideExpr::AddrFold(Val* v1, Val* v2) const
|
|
{
|
|
uint32_t mask;
|
|
|
|
if ( v2->GetType()->Tag() == TYPE_COUNT )
|
|
mask = static_cast<uint32_t>(v2->InternalUnsigned());
|
|
else
|
|
mask = static_cast<uint32_t>(v2->InternalInt());
|
|
|
|
auto& a = v1->AsAddr();
|
|
|
|
if ( a.GetFamily() == IPv4 )
|
|
{
|
|
if ( mask > 32 )
|
|
RuntimeError(util::fmt("bad IPv4 subnet prefix length: %" PRIu32, mask));
|
|
}
|
|
else
|
|
{
|
|
if ( mask > 128 )
|
|
RuntimeError(util::fmt("bad IPv6 subnet prefix length: %" PRIu32, mask));
|
|
}
|
|
|
|
return make_intrusive<SubNetVal>(a, mask);
|
|
}
|
|
|
|
ModExpr::ModExpr(ExprPtr arg_op1, ExprPtr arg_op2)
|
|
: BinaryExpr(EXPR_MOD, std::move(arg_op1), std::move(arg_op2))
|
|
{
|
|
if ( IsError() )
|
|
return;
|
|
|
|
TypeTag bt1 = op1->GetType()->Tag();
|
|
|
|
if ( IsVector(bt1) )
|
|
bt1 = op1->GetType()->AsVectorType()->Yield()->Tag();
|
|
|
|
TypeTag bt2 = op2->GetType()->Tag();
|
|
|
|
if ( IsVector(bt2) )
|
|
bt2 = op2->GetType()->AsVectorType()->Yield()->Tag();
|
|
|
|
if ( BothIntegral(bt1, bt2) )
|
|
PromoteType(max_type(bt1, bt2), is_vector(op1) || is_vector(op2));
|
|
else
|
|
ExprError("requires integral operands");
|
|
}
|
|
|
|
BoolExpr::BoolExpr(BroExprTag arg_tag, ExprPtr arg_op1, ExprPtr arg_op2)
|
|
: BinaryExpr(arg_tag, std::move(arg_op1), std::move(arg_op2))
|
|
{
|
|
if ( IsError() )
|
|
return;
|
|
|
|
TypeTag bt1 = op1->GetType()->Tag();
|
|
|
|
if ( IsVector(bt1) )
|
|
bt1 = op1->GetType()->AsVectorType()->Yield()->Tag();
|
|
|
|
TypeTag bt2 = op2->GetType()->Tag();
|
|
|
|
if ( IsVector(bt2) )
|
|
bt2 = op2->GetType()->AsVectorType()->Yield()->Tag();
|
|
|
|
if ( BothBool(bt1, bt2) )
|
|
{
|
|
if ( is_vector(op1) || is_vector(op2) )
|
|
{
|
|
if ( ! (is_vector(op1) && is_vector(op2)) )
|
|
reporter->Warning("mixing vector and scalar operands is deprecated");
|
|
SetType(make_intrusive<VectorType>(base_type(TYPE_BOOL)));
|
|
}
|
|
else
|
|
SetType(base_type(TYPE_BOOL));
|
|
}
|
|
else
|
|
ExprError("requires boolean operands");
|
|
}
|
|
|
|
ValPtr BoolExpr::DoSingleEval(Frame* f, ValPtr v1, Expr* op2) const
|
|
{
|
|
if ( ! v1 )
|
|
return nullptr;
|
|
|
|
if ( tag == EXPR_AND_AND )
|
|
{
|
|
if ( v1->IsZero() )
|
|
return v1;
|
|
else
|
|
return op2->Eval(f);
|
|
}
|
|
|
|
else
|
|
{
|
|
if ( v1->IsZero() )
|
|
return op2->Eval(f);
|
|
else
|
|
return v1;
|
|
}
|
|
}
|
|
|
|
ValPtr BoolExpr::Eval(Frame* f) const
|
|
{
|
|
if ( IsError() )
|
|
return nullptr;
|
|
|
|
auto v1 = op1->Eval(f);
|
|
|
|
if ( ! v1 )
|
|
return nullptr;
|
|
|
|
bool is_vec1 = is_vector(op1);
|
|
bool is_vec2 = is_vector(op2);
|
|
|
|
// Handle scalar op scalar
|
|
if ( ! is_vec1 && ! is_vec2 )
|
|
return DoSingleEval(f, std::move(v1), op2.get());
|
|
|
|
// Handle scalar op vector or vector op scalar
|
|
// We can't short-circuit everything since we need to eval
|
|
// a vector in order to find out its length.
|
|
if ( ! (is_vec1 && is_vec2) )
|
|
{ // Only one is a vector.
|
|
ValPtr scalar_v;
|
|
VectorValPtr vector_v;
|
|
|
|
if ( is_vec1 )
|
|
{
|
|
scalar_v = op2->Eval(f);
|
|
vector_v = {AdoptRef{}, v1.release()->AsVectorVal()};
|
|
}
|
|
else
|
|
{
|
|
scalar_v = std::move(v1);
|
|
vector_v = {AdoptRef{}, op2->Eval(f).release()->AsVectorVal()};
|
|
}
|
|
|
|
if ( ! scalar_v || ! vector_v )
|
|
return nullptr;
|
|
|
|
VectorValPtr result;
|
|
|
|
// It's either an EXPR_AND_AND or an EXPR_OR_OR.
|
|
bool is_and = (tag == EXPR_AND_AND);
|
|
|
|
if ( scalar_v->IsZero() == is_and )
|
|
{
|
|
result = make_intrusive<VectorVal>(GetType<VectorType>());
|
|
result->Resize(vector_v->Size());
|
|
result->AssignRepeat(0, result->Size(), std::move(scalar_v));
|
|
}
|
|
else
|
|
result = std::move(vector_v);
|
|
|
|
return result;
|
|
}
|
|
|
|
// Only case remaining: both are vectors.
|
|
auto v2 = op2->Eval(f);
|
|
|
|
if ( ! v2 )
|
|
return nullptr;
|
|
|
|
VectorVal* vec_v1 = v1->AsVectorVal();
|
|
VectorVal* vec_v2 = v2->AsVectorVal();
|
|
|
|
if ( vec_v1->Size() != vec_v2->Size() )
|
|
{
|
|
RuntimeError("vector operands have different sizes");
|
|
return nullptr;
|
|
}
|
|
|
|
auto result = make_intrusive<VectorVal>(GetType<VectorType>());
|
|
result->Resize(vec_v1->Size());
|
|
|
|
for ( unsigned int i = 0; i < vec_v1->Size(); ++i )
|
|
{
|
|
const auto op1 = vec_v1->BoolAt(i);
|
|
const auto op2 = vec_v2->BoolAt(i);
|
|
|
|
bool local_result = (tag == EXPR_AND_AND) ? (op1 && op2) : (op1 || op2);
|
|
|
|
result->Assign(i, val_mgr->Bool(local_result));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
BitExpr::BitExpr(BroExprTag arg_tag, ExprPtr arg_op1, ExprPtr arg_op2)
|
|
: BinaryExpr(arg_tag, std::move(arg_op1), std::move(arg_op2))
|
|
{
|
|
if ( IsError() )
|
|
return;
|
|
|
|
const auto& t1 = op1->GetType();
|
|
const auto& t2 = op2->GetType();
|
|
|
|
TypeTag bt1 = t1->Tag();
|
|
|
|
if ( IsVector(bt1) )
|
|
bt1 = t1->AsVectorType()->Yield()->Tag();
|
|
|
|
TypeTag bt2 = t2->Tag();
|
|
|
|
if ( IsVector(bt2) )
|
|
bt2 = t2->AsVectorType()->Yield()->Tag();
|
|
|
|
if ( (bt1 == TYPE_COUNT) && (bt2 == TYPE_COUNT) )
|
|
{
|
|
if ( is_vector(op1) || is_vector(op2) )
|
|
SetType(make_intrusive<VectorType>(base_type(TYPE_COUNT)));
|
|
else
|
|
SetType(base_type(TYPE_COUNT));
|
|
}
|
|
|
|
else if ( bt1 == TYPE_PATTERN )
|
|
{
|
|
if ( bt2 != TYPE_PATTERN )
|
|
ExprError("cannot mix pattern and non-pattern operands");
|
|
else if ( tag == EXPR_XOR )
|
|
ExprError("'^' operator does not apply to patterns");
|
|
else
|
|
SetType(base_type(TYPE_PATTERN));
|
|
}
|
|
|
|
else if ( t1->IsSet() && t2->IsSet() )
|
|
{
|
|
if ( same_type(t1, t2) )
|
|
SetType(op1->GetType());
|
|
else
|
|
ExprError("incompatible \"set\" operands");
|
|
}
|
|
|
|
else
|
|
ExprError("requires \"count\" or compatible \"set\" operands");
|
|
}
|
|
|
|
EqExpr::EqExpr(BroExprTag arg_tag, ExprPtr arg_op1, ExprPtr arg_op2)
|
|
: BinaryExpr(arg_tag, std::move(arg_op1), std::move(arg_op2))
|
|
{
|
|
if ( IsError() )
|
|
return;
|
|
|
|
Canonicize();
|
|
|
|
const auto& t1 = op1->GetType();
|
|
const auto& t2 = op2->GetType();
|
|
|
|
TypeTag bt1 = t1->Tag();
|
|
if ( IsVector(bt1) )
|
|
bt1 = t1->AsVectorType()->Yield()->Tag();
|
|
|
|
TypeTag bt2 = t2->Tag();
|
|
if ( IsVector(bt2) )
|
|
bt2 = t2->AsVectorType()->Yield()->Tag();
|
|
|
|
if ( is_vector(op1) || is_vector(op2) )
|
|
SetType(make_intrusive<VectorType>(base_type(TYPE_BOOL)));
|
|
else
|
|
SetType(base_type(TYPE_BOOL));
|
|
|
|
if ( BothArithmetic(bt1, bt2) )
|
|
PromoteOps(max_type(bt1, bt2));
|
|
|
|
else if ( EitherArithmetic(bt1, bt2) &&
|
|
// Allow comparisons with zero.
|
|
((bt1 == TYPE_TIME && op2->IsZero()) || (bt2 == TYPE_TIME && op1->IsZero())) )
|
|
PromoteOps(TYPE_TIME);
|
|
|
|
else if ( bt1 == bt2 )
|
|
{
|
|
switch ( bt1 )
|
|
{
|
|
case TYPE_BOOL:
|
|
case TYPE_TIME:
|
|
case TYPE_INTERVAL:
|
|
case TYPE_STRING:
|
|
case TYPE_PORT:
|
|
case TYPE_ADDR:
|
|
case TYPE_SUBNET:
|
|
case TYPE_ERROR:
|
|
case TYPE_FUNC:
|
|
break;
|
|
|
|
case TYPE_ENUM:
|
|
if ( ! same_type(t1, t2) )
|
|
ExprError("illegal enum comparison");
|
|
break;
|
|
|
|
case TYPE_TABLE:
|
|
if ( t1->IsSet() && t2->IsSet() )
|
|
{
|
|
if ( ! same_type(t1, t2) )
|
|
ExprError("incompatible sets in comparison");
|
|
break;
|
|
}
|
|
|
|
// FALL THROUGH
|
|
|
|
default:
|
|
ExprError("illegal comparison");
|
|
}
|
|
}
|
|
|
|
else if ( bt1 == TYPE_PATTERN && bt2 == TYPE_STRING )
|
|
;
|
|
|
|
else
|
|
ExprError("type clash in comparison");
|
|
}
|
|
|
|
void EqExpr::Canonicize()
|
|
{
|
|
if ( op2->GetType()->Tag() == TYPE_PATTERN )
|
|
SwapOps();
|
|
|
|
else if ( op1->GetType()->Tag() == TYPE_PATTERN )
|
|
;
|
|
|
|
else if ( expr_greater(op2.get(), op1.get()) )
|
|
SwapOps();
|
|
}
|
|
|
|
ValPtr EqExpr::Fold(Val* v1, Val* v2) const
|
|
{
|
|
if ( op1->GetType()->Tag() == TYPE_PATTERN )
|
|
{
|
|
auto re = v1->As<PatternVal*>();
|
|
const String* s = v2->AsString();
|
|
if ( tag == EXPR_EQ )
|
|
return val_mgr->Bool(re->MatchExactly(s));
|
|
else
|
|
return val_mgr->Bool(! re->MatchExactly(s));
|
|
}
|
|
else if ( op1->GetType()->Tag() == TYPE_FUNC )
|
|
{
|
|
auto res = v1->AsFunc() == v2->AsFunc();
|
|
return val_mgr->Bool(tag == EXPR_EQ ? res : ! res);
|
|
}
|
|
|
|
else
|
|
return BinaryExpr::Fold(v1, v2);
|
|
}
|
|
|
|
bool EqExpr::InvertSense()
|
|
{
|
|
tag = (tag == EXPR_EQ ? EXPR_NE : EXPR_EQ);
|
|
return true;
|
|
}
|
|
|
|
RelExpr::RelExpr(BroExprTag arg_tag, ExprPtr arg_op1, ExprPtr arg_op2)
|
|
: BinaryExpr(arg_tag, std::move(arg_op1), std::move(arg_op2))
|
|
{
|
|
if ( IsError() )
|
|
return;
|
|
|
|
Canonicize();
|
|
|
|
const auto& t1 = op1->GetType();
|
|
const auto& t2 = op2->GetType();
|
|
|
|
TypeTag bt1 = t1->Tag();
|
|
if ( IsVector(bt1) )
|
|
bt1 = t1->AsVectorType()->Yield()->Tag();
|
|
|
|
TypeTag bt2 = t2->Tag();
|
|
if ( IsVector(bt2) )
|
|
bt2 = t2->AsVectorType()->Yield()->Tag();
|
|
|
|
if ( is_vector(op1) || is_vector(op2) )
|
|
SetType(make_intrusive<VectorType>(base_type(TYPE_BOOL)));
|
|
else
|
|
SetType(base_type(TYPE_BOOL));
|
|
|
|
if ( BothArithmetic(bt1, bt2) )
|
|
PromoteOps(max_type(bt1, bt2));
|
|
|
|
else if ( t1->IsSet() && t2->IsSet() )
|
|
{
|
|
if ( ! same_type(t1, t2) )
|
|
ExprError("incompatible sets in comparison");
|
|
}
|
|
|
|
else if ( bt1 != bt2 )
|
|
ExprError("operands must be of the same type");
|
|
|
|
else if ( bt1 != TYPE_TIME && bt1 != TYPE_INTERVAL && bt1 != TYPE_PORT && bt1 != TYPE_ADDR &&
|
|
bt1 != TYPE_STRING )
|
|
ExprError("illegal comparison");
|
|
}
|
|
|
|
void RelExpr::Canonicize()
|
|
{
|
|
if ( tag == EXPR_GT )
|
|
{
|
|
SwapOps();
|
|
tag = EXPR_LT;
|
|
}
|
|
|
|
else if ( tag == EXPR_GE )
|
|
{
|
|
SwapOps();
|
|
tag = EXPR_LE;
|
|
}
|
|
}
|
|
|
|
bool RelExpr::InvertSense()
|
|
{
|
|
switch ( tag )
|
|
{
|
|
case EXPR_LT:
|
|
tag = EXPR_GE;
|
|
return true;
|
|
case EXPR_LE:
|
|
tag = EXPR_GT;
|
|
return true;
|
|
case EXPR_GE:
|
|
tag = EXPR_LT;
|
|
return true;
|
|
case EXPR_GT:
|
|
tag = EXPR_LE;
|
|
return true;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
CondExpr::CondExpr(ExprPtr arg_op1, ExprPtr arg_op2, ExprPtr arg_op3)
|
|
: Expr(EXPR_COND), op1(std::move(arg_op1)), op2(std::move(arg_op2)), op3(std::move(arg_op3))
|
|
{
|
|
TypeTag bt1 = op1->GetType()->Tag();
|
|
|
|
if ( IsVector(bt1) )
|
|
bt1 = op1->GetType()->AsVectorType()->Yield()->Tag();
|
|
|
|
if ( op1->IsError() || op2->IsError() || op3->IsError() )
|
|
SetError();
|
|
|
|
else if ( bt1 != TYPE_BOOL )
|
|
ExprError("requires boolean conditional");
|
|
|
|
else
|
|
{
|
|
TypeTag bt2 = op2->GetType()->Tag();
|
|
TypeTag bt3 = op3->GetType()->Tag();
|
|
|
|
if ( is_vector(op1) )
|
|
{
|
|
if ( ! (is_vector(op2) && is_vector(op3)) )
|
|
{
|
|
ExprError("vector conditional requires vector alternatives");
|
|
return;
|
|
}
|
|
|
|
bt2 = op2->GetType()->AsVectorType()->Yield()->Tag();
|
|
bt3 = op3->GetType()->AsVectorType()->Yield()->Tag();
|
|
}
|
|
|
|
if ( BothArithmetic(bt2, bt3) )
|
|
{
|
|
TypeTag t = max_type(bt2, bt3);
|
|
if ( bt2 != t )
|
|
op2 = make_intrusive<ArithCoerceExpr>(std::move(op2), t);
|
|
if ( bt3 != t )
|
|
op3 = make_intrusive<ArithCoerceExpr>(std::move(op3), t);
|
|
|
|
if ( is_vector(op1) )
|
|
SetType(make_intrusive<VectorType>(base_type(t)));
|
|
else
|
|
SetType(base_type(t));
|
|
}
|
|
|
|
else if ( bt2 != bt3 )
|
|
ExprError("operands must be of the same type");
|
|
|
|
else
|
|
{
|
|
if ( is_vector(op1) )
|
|
{
|
|
ExprError("vector conditional type clash between alternatives");
|
|
return;
|
|
}
|
|
|
|
if ( bt2 == zeek::TYPE_TABLE )
|
|
{
|
|
auto tt2 = op2->GetType<TableType>();
|
|
auto tt3 = op3->GetType<TableType>();
|
|
|
|
if ( tt2->IsUnspecifiedTable() )
|
|
op2 = make_intrusive<TableCoerceExpr>(std::move(op2), std::move(tt3));
|
|
else if ( tt3->IsUnspecifiedTable() )
|
|
op3 = make_intrusive<TableCoerceExpr>(std::move(op3), std::move(tt2));
|
|
else if ( ! same_type(op2->GetType(), op3->GetType()) )
|
|
ExprError("operands must be of the same type");
|
|
}
|
|
else if ( bt2 == zeek::TYPE_VECTOR )
|
|
{
|
|
auto vt2 = op2->GetType<VectorType>();
|
|
auto vt3 = op3->GetType<VectorType>();
|
|
|
|
if ( vt2->IsUnspecifiedVector() )
|
|
op2 = make_intrusive<VectorCoerceExpr>(std::move(op2), std::move(vt3));
|
|
else if ( vt3->IsUnspecifiedVector() )
|
|
op3 = make_intrusive<VectorCoerceExpr>(std::move(op3), std::move(vt2));
|
|
else if ( ! same_type(op2->GetType(), op3->GetType()) )
|
|
ExprError("operands must be of the same type");
|
|
}
|
|
else if ( ! same_type(op2->GetType(), op3->GetType()) )
|
|
// Records could potentially also coerce, but may have some cases
|
|
// where the coercion direction is ambiguous.
|
|
ExprError("operands must be of the same type");
|
|
|
|
if ( ! IsError() )
|
|
SetType(op2->GetType());
|
|
}
|
|
}
|
|
}
|
|
|
|
ValPtr CondExpr::Eval(Frame* f) const
|
|
{
|
|
if ( ! is_vector(op1) )
|
|
{
|
|
// Scalar case
|
|
auto false_eval = op1->Eval(f)->IsZero();
|
|
return (false_eval ? op3 : op2)->Eval(f);
|
|
}
|
|
|
|
// Vector case: no mixed scalar/vector cases allowed
|
|
auto v1 = op1->Eval(f);
|
|
|
|
if ( ! v1 )
|
|
return nullptr;
|
|
|
|
auto v2 = op2->Eval(f);
|
|
|
|
if ( ! v2 )
|
|
return nullptr;
|
|
|
|
auto v3 = op3->Eval(f);
|
|
|
|
if ( ! v3 )
|
|
return nullptr;
|
|
|
|
VectorVal* cond = v1->AsVectorVal();
|
|
VectorVal* a = v2->AsVectorVal();
|
|
VectorVal* b = v3->AsVectorVal();
|
|
|
|
if ( cond->Size() != a->Size() || a->Size() != b->Size() )
|
|
{
|
|
RuntimeError("vectors in conditional expression have different sizes");
|
|
return nullptr;
|
|
}
|
|
|
|
auto result = make_intrusive<VectorVal>(GetType<VectorType>());
|
|
result->Resize(cond->Size());
|
|
|
|
for ( unsigned int i = 0; i < cond->Size(); ++i )
|
|
{
|
|
auto local_cond = cond->BoolAt(i);
|
|
auto v = local_cond ? a->ValAt(i) : b->ValAt(i);
|
|
result->Assign(i, v);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
bool CondExpr::IsPure() const
|
|
{
|
|
return op1->IsPure() && op2->IsPure() && op3->IsPure();
|
|
}
|
|
|
|
TraversalCode CondExpr::Traverse(TraversalCallback* cb) const
|
|
{
|
|
TraversalCode tc = cb->PreExpr(this);
|
|
HANDLE_TC_EXPR_PRE(tc);
|
|
|
|
tc = op1->Traverse(cb);
|
|
HANDLE_TC_EXPR_PRE(tc);
|
|
|
|
tc = op2->Traverse(cb);
|
|
HANDLE_TC_EXPR_PRE(tc);
|
|
|
|
tc = op3->Traverse(cb);
|
|
HANDLE_TC_EXPR_PRE(tc);
|
|
|
|
tc = cb->PostExpr(this);
|
|
HANDLE_TC_EXPR_POST(tc);
|
|
}
|
|
|
|
void CondExpr::ExprDescribe(ODesc* d) const
|
|
{
|
|
op1->Describe(d);
|
|
d->AddSP(" ?");
|
|
op2->Describe(d);
|
|
d->AddSP(" :");
|
|
op3->Describe(d);
|
|
}
|
|
|
|
RefExpr::RefExpr(ExprPtr arg_op) : UnaryExpr(EXPR_REF, std::move(arg_op))
|
|
{
|
|
if ( IsError() )
|
|
return;
|
|
|
|
if ( ! is_assignable(op->GetType()->Tag()) )
|
|
ExprError("illegal assignment target");
|
|
else
|
|
SetType(op->GetType());
|
|
}
|
|
|
|
ExprPtr RefExpr::MakeLvalue()
|
|
{
|
|
return {NewRef{}, this};
|
|
}
|
|
|
|
void RefExpr::Assign(Frame* f, ValPtr v)
|
|
{
|
|
op->Assign(f, std::move(v));
|
|
}
|
|
|
|
AssignExpr::AssignExpr(ExprPtr arg_op1, ExprPtr arg_op2, bool arg_is_init, ValPtr arg_val,
|
|
const AttributesPtr& attrs, bool typecheck)
|
|
: BinaryExpr(EXPR_ASSIGN, arg_is_init ? std::move(arg_op1) : arg_op1->MakeLvalue(),
|
|
std::move(arg_op2))
|
|
{
|
|
val = nullptr;
|
|
is_init = arg_is_init;
|
|
|
|
if ( IsError() )
|
|
return;
|
|
|
|
if ( arg_val )
|
|
SetType(arg_val->GetType());
|
|
else
|
|
SetType(op1->GetType());
|
|
|
|
if ( is_init )
|
|
{
|
|
SetLocationInfo(op1->GetLocationInfo(), op2->GetLocationInfo());
|
|
return;
|
|
}
|
|
|
|
if ( typecheck )
|
|
// We discard the status from TypeCheck since it has already
|
|
// generated error messages.
|
|
(void)TypeCheck(attrs);
|
|
|
|
val = std::move(arg_val);
|
|
|
|
SetLocationInfo(op1->GetLocationInfo(), op2->GetLocationInfo());
|
|
}
|
|
|
|
bool AssignExpr::TypeCheck(const AttributesPtr& attrs)
|
|
{
|
|
TypeTag bt1 = op1->GetType()->Tag();
|
|
TypeTag bt2 = op2->GetType()->Tag();
|
|
|
|
if ( bt1 == TYPE_LIST && bt2 == TYPE_ANY )
|
|
// This is ok because we cannot explicitly declare lists on
|
|
// the script level.
|
|
return true;
|
|
|
|
// This should be one of them, but not both (i.e. XOR)
|
|
if ( ((bt1 == TYPE_ENUM) ^ (bt2 == TYPE_ENUM)) )
|
|
{
|
|
ExprError("can't convert to/from enumerated type");
|
|
return false;
|
|
}
|
|
|
|
if ( IsArithmetic(bt1) )
|
|
return TypeCheckArithmetics(bt1, bt2);
|
|
|
|
if ( bt1 == TYPE_TIME && IsArithmetic(bt2) && op2->IsZero() )
|
|
{ // Allow assignments to zero as a special case.
|
|
op2 = make_intrusive<ArithCoerceExpr>(std::move(op2), bt1);
|
|
return true;
|
|
}
|
|
|
|
if ( bt1 == TYPE_TABLE && bt2 == bt1 && op2->GetType()->AsTableType()->IsUnspecifiedTable() )
|
|
{
|
|
op2 = make_intrusive<TableCoerceExpr>(std::move(op2), op1->GetType<TableType>());
|
|
return true;
|
|
}
|
|
|
|
if ( bt1 == TYPE_TABLE && op2->Tag() == EXPR_LIST )
|
|
{
|
|
std::unique_ptr<std::vector<AttrPtr>> attr_copy;
|
|
|
|
if ( attrs )
|
|
attr_copy = std::make_unique<std::vector<AttrPtr>>(attrs->GetAttrs());
|
|
|
|
if ( op1->GetType()->IsSet() )
|
|
op2 = make_intrusive<SetConstructorExpr>(cast_intrusive<ListExpr>(op2),
|
|
std::move(attr_copy), op1->GetType());
|
|
else
|
|
op2 = make_intrusive<TableConstructorExpr>(cast_intrusive<ListExpr>(op2),
|
|
std::move(attr_copy), op1->GetType());
|
|
|
|
// The constructor expressions are performing the type
|
|
// checking and will set op2 to an error state on failure.
|
|
return ! op2->IsError();
|
|
}
|
|
|
|
if ( bt1 == TYPE_VECTOR )
|
|
{
|
|
if ( bt2 == bt1 && op2->GetType()->AsVectorType()->IsUnspecifiedVector() )
|
|
{
|
|
op2 = make_intrusive<VectorCoerceExpr>(std::move(op2), op1->GetType<VectorType>());
|
|
return true;
|
|
}
|
|
|
|
if ( op2->Tag() == EXPR_LIST )
|
|
{
|
|
op2 = make_intrusive<VectorConstructorExpr>(
|
|
IntrusivePtr{AdoptRef{}, op2.release()->AsListExpr()}, op1->GetType());
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if ( op1->GetType()->Tag() == TYPE_RECORD && op2->GetType()->Tag() == TYPE_RECORD )
|
|
{
|
|
if ( same_type(op1->GetType(), op2->GetType()) )
|
|
return true;
|
|
|
|
// Need to coerce.
|
|
op2 = make_intrusive<RecordCoerceExpr>(std::move(op2), op1->GetType<RecordType>());
|
|
return true;
|
|
}
|
|
|
|
if ( ! same_type(op1->GetType(), op2->GetType()) )
|
|
{
|
|
if ( bt1 == TYPE_TABLE && bt2 == TYPE_TABLE )
|
|
{
|
|
if ( op2->Tag() == EXPR_SET_CONSTRUCTOR )
|
|
{
|
|
// Some elements in constructor list must not match, see if
|
|
// we can create a new constructor now that the expected type
|
|
// of LHS is known and let it do coercions where possible.
|
|
SetConstructorExpr* sce = dynamic_cast<SetConstructorExpr*>(op2.get());
|
|
|
|
if ( ! sce )
|
|
{
|
|
ExprError("Failed typecast to SetConstructorExpr");
|
|
return false;
|
|
}
|
|
|
|
ListExpr* ctor_list = dynamic_cast<ListExpr*>(sce->Op());
|
|
|
|
if ( ! ctor_list )
|
|
{
|
|
ExprError("Failed typecast to ListExpr");
|
|
return false;
|
|
}
|
|
|
|
std::unique_ptr<std::vector<AttrPtr>> attr_copy;
|
|
|
|
if ( sce->GetAttrs() )
|
|
{
|
|
const auto& a = sce->GetAttrs()->GetAttrs();
|
|
attr_copy = std::make_unique<std::vector<AttrPtr>>(a);
|
|
}
|
|
|
|
int errors_before = reporter->Errors();
|
|
op2 = make_intrusive<SetConstructorExpr>(IntrusivePtr{NewRef{}, ctor_list},
|
|
std::move(attr_copy), op1->GetType());
|
|
int errors_after = reporter->Errors();
|
|
|
|
if ( errors_after > errors_before )
|
|
{
|
|
ExprError("type clash in assignment");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
ExprError("type clash in assignment");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool AssignExpr::TypeCheckArithmetics(TypeTag bt1, TypeTag bt2)
|
|
{
|
|
if ( ! IsArithmetic(bt2) )
|
|
{
|
|
ExprError(util::fmt("assignment of non-arithmetic value to arithmetic (%s/%s)",
|
|
type_name(bt1), type_name(bt2)));
|
|
return false;
|
|
}
|
|
|
|
if ( bt1 == TYPE_DOUBLE )
|
|
{
|
|
PromoteOps(TYPE_DOUBLE);
|
|
return true;
|
|
}
|
|
|
|
if ( bt2 == TYPE_DOUBLE )
|
|
{
|
|
Warn("dangerous assignment of double to integral");
|
|
op2 = make_intrusive<ArithCoerceExpr>(std::move(op2), bt1);
|
|
bt2 = op2->GetType()->Tag();
|
|
}
|
|
|
|
if ( bt1 == TYPE_INT )
|
|
PromoteOps(TYPE_INT);
|
|
else
|
|
{
|
|
if ( bt2 == TYPE_INT )
|
|
{
|
|
Warn("dangerous assignment of integer to count");
|
|
op2 = make_intrusive<ArithCoerceExpr>(std::move(op2), bt1);
|
|
}
|
|
|
|
// Assignment of count to counter or vice
|
|
// versa is allowed, and requires no
|
|
// coercion.
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
ValPtr AssignExpr::Eval(Frame* f) const
|
|
{
|
|
if ( is_init )
|
|
{
|
|
RuntimeError("illegal assignment in initialization");
|
|
return nullptr;
|
|
}
|
|
|
|
if ( auto v = op2->Eval(f) )
|
|
{
|
|
op1->Assign(f, v);
|
|
|
|
if ( val )
|
|
return val;
|
|
|
|
return v;
|
|
}
|
|
else
|
|
return nullptr;
|
|
}
|
|
|
|
TypePtr AssignExpr::InitType() const
|
|
{
|
|
if ( op1->Tag() != EXPR_LIST )
|
|
{
|
|
Error("bad initializer, first operand should be a list");
|
|
return nullptr;
|
|
}
|
|
|
|
const auto& tl = op1->GetType();
|
|
if ( tl->Tag() != TYPE_LIST )
|
|
Internal("inconsistent list expr in AssignExpr::InitType");
|
|
|
|
return make_intrusive<TableType>(IntrusivePtr{NewRef{}, tl->AsTypeList()}, op2->GetType());
|
|
}
|
|
|
|
void AssignExpr::EvalIntoAggregate(const TypePtr& t, ValPtr aggr, Frame* f) const
|
|
{
|
|
if ( IsError() )
|
|
return;
|
|
|
|
TypeDecl td;
|
|
|
|
if ( IsRecordElement(&td) )
|
|
{
|
|
if ( t->Tag() != TYPE_RECORD )
|
|
{
|
|
RuntimeError("not a record initializer");
|
|
return;
|
|
}
|
|
|
|
const RecordType* rt = t->AsRecordType();
|
|
int field = rt->FieldOffset(td.id);
|
|
|
|
if ( field < 0 )
|
|
{
|
|
RuntimeError("no such field");
|
|
return;
|
|
}
|
|
|
|
RecordVal* aggr_r = aggr->AsRecordVal();
|
|
|
|
auto v = op2->Eval(f);
|
|
|
|
if ( v )
|
|
aggr_r->Assign(field, std::move(v));
|
|
|
|
return;
|
|
}
|
|
|
|
if ( op1->Tag() != EXPR_LIST )
|
|
RuntimeError("bad table insertion");
|
|
|
|
TableVal* tv = aggr->AsTableVal();
|
|
|
|
auto index = op1->Eval(f);
|
|
auto v = check_and_promote(op2->Eval(f), t->Yield(), true);
|
|
|
|
if ( ! index || ! v )
|
|
return;
|
|
|
|
if ( ! tv->Assign(std::move(index), std::move(v)) )
|
|
RuntimeError("type clash in table assignment");
|
|
}
|
|
|
|
ValPtr AssignExpr::InitVal(const TypePtr& t, ValPtr aggr) const
|
|
{
|
|
if ( ! aggr )
|
|
{
|
|
Error("assignment in initialization");
|
|
return nullptr;
|
|
}
|
|
|
|
if ( IsError() )
|
|
return nullptr;
|
|
|
|
TypeDecl td;
|
|
|
|
if ( IsRecordElement(&td) )
|
|
{
|
|
if ( t->Tag() != TYPE_RECORD )
|
|
{
|
|
Error("not a record initializer", t.get());
|
|
return nullptr;
|
|
}
|
|
|
|
const RecordType* rt = t->AsRecordType();
|
|
int field = rt->FieldOffset(td.id);
|
|
|
|
if ( field < 0 )
|
|
{
|
|
Error("no such field");
|
|
return nullptr;
|
|
}
|
|
|
|
if ( aggr->GetType()->Tag() != TYPE_RECORD )
|
|
Internal("bad aggregate in AssignExpr::InitVal");
|
|
|
|
RecordVal* aggr_r = aggr->AsRecordVal();
|
|
|
|
auto v = op2->InitVal(rt->GetFieldType(td.id), nullptr);
|
|
|
|
if ( ! v )
|
|
return nullptr;
|
|
|
|
aggr_r->Assign(field, v);
|
|
return v;
|
|
}
|
|
|
|
else if ( op1->Tag() == EXPR_LIST )
|
|
{
|
|
if ( t->Tag() != TYPE_TABLE )
|
|
{
|
|
Error("not a table initialization", t.get());
|
|
return nullptr;
|
|
}
|
|
|
|
if ( aggr->GetType()->Tag() != TYPE_TABLE )
|
|
Internal("bad aggregate in AssignExpr::InitVal");
|
|
|
|
auto tv = cast_intrusive<TableVal>(std::move(aggr));
|
|
const TableType* tt = tv->GetType()->AsTableType();
|
|
const auto& yt = tv->GetType()->Yield();
|
|
|
|
auto index = op1->InitVal(tt->GetIndices(), nullptr);
|
|
|
|
if ( yt->Tag() == TYPE_RECORD )
|
|
{
|
|
if ( op2->GetType()->Tag() != TYPE_RECORD )
|
|
{
|
|
Error(util::fmt("type mismatch in table value initialization: "
|
|
"assigning '%s' to table with values of type '%s'",
|
|
type_name(op2->GetType()->Tag()), type_name(yt->Tag())));
|
|
return nullptr;
|
|
}
|
|
|
|
if ( ! same_type(*yt, *op2->GetType()) &&
|
|
! record_promotion_compatible(yt->AsRecordType(), op2->GetType()->AsRecordType()) )
|
|
{
|
|
Error("type mismatch in table value initialization: "
|
|
"incompatible record types");
|
|
return nullptr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( ! same_type(*yt, *op2->GetType(), true) )
|
|
{
|
|
Error(util::fmt("type mismatch in table value initialization: "
|
|
"assigning '%s' to table with values of type '%s'",
|
|
type_name(op2->GetType()->Tag()), type_name(yt->Tag())));
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
auto v = op2->InitVal(yt, nullptr);
|
|
|
|
if ( ! index || ! v )
|
|
return nullptr;
|
|
|
|
if ( ! tv->ExpandAndInit(std::move(index), std::move(v)) )
|
|
return nullptr;
|
|
|
|
return tv;
|
|
}
|
|
|
|
else
|
|
{
|
|
Error("illegal initializer");
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
bool AssignExpr::IsRecordElement(TypeDecl* td) const
|
|
{
|
|
if ( op1->Tag() == EXPR_NAME )
|
|
{
|
|
if ( td )
|
|
{
|
|
const NameExpr* n = (const NameExpr*)op1.get();
|
|
td->type = op2->GetType();
|
|
td->id = util::copy_string(n->Id()->Name());
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool AssignExpr::IsPure() const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
IndexSliceAssignExpr::IndexSliceAssignExpr(ExprPtr op1, ExprPtr op2, bool is_init)
|
|
: AssignExpr(std::move(op1), std::move(op2), is_init)
|
|
{
|
|
}
|
|
|
|
ValPtr IndexSliceAssignExpr::Eval(Frame* f) const
|
|
{
|
|
if ( is_init )
|
|
{
|
|
RuntimeError("illegal assignment in initialization");
|
|
return nullptr;
|
|
}
|
|
|
|
if ( auto v = op2->Eval(f) )
|
|
op1->Assign(f, std::move(v));
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
IndexExpr::IndexExpr(ExprPtr arg_op1, ListExprPtr arg_op2, bool arg_is_slice)
|
|
: BinaryExpr(EXPR_INDEX, std::move(arg_op1), std::move(arg_op2)), is_slice(arg_is_slice)
|
|
{
|
|
if ( IsError() )
|
|
return;
|
|
|
|
if ( is_slice )
|
|
{
|
|
if ( ! IsString(op1->GetType()->Tag()) && ! IsVector(op1->GetType()->Tag()) )
|
|
ExprError("slice notation indexing only supported for strings and vectors currently");
|
|
}
|
|
|
|
else if ( IsString(op1->GetType()->Tag()) )
|
|
{
|
|
if ( op2->AsListExpr()->Exprs().length() != 1 )
|
|
ExprError("invalid string index expression");
|
|
}
|
|
|
|
if ( IsError() )
|
|
return;
|
|
|
|
int match_type = op1->GetType()->MatchesIndex(op2->AsListExpr());
|
|
|
|
if ( match_type == DOES_NOT_MATCH_INDEX )
|
|
{
|
|
std::string error_msg = util::fmt(
|
|
"expression with type '%s' is not a type that can be indexed",
|
|
type_name(op1->GetType()->Tag()));
|
|
SetError(error_msg.data());
|
|
}
|
|
|
|
else if ( ! op1->GetType()->Yield() )
|
|
{
|
|
if ( IsString(op1->GetType()->Tag()) && match_type == MATCHES_INDEX_SCALAR )
|
|
SetType(base_type(TYPE_STRING));
|
|
else
|
|
// It's a set - so indexing it yields void. We don't
|
|
// directly generate an error message, though, since this
|
|
// expression might be part of an add/delete statement,
|
|
// rather than yielding a value.
|
|
SetType(base_type(TYPE_VOID));
|
|
}
|
|
|
|
else if ( match_type == MATCHES_INDEX_SCALAR )
|
|
SetType(op1->GetType()->Yield());
|
|
|
|
else if ( match_type == MATCHES_INDEX_VECTOR )
|
|
SetType(make_intrusive<VectorType>(op1->GetType()->Yield()));
|
|
|
|
else
|
|
ExprError("Unknown MatchesIndex() return value");
|
|
}
|
|
|
|
bool IndexExpr::CanAdd() const
|
|
{
|
|
if ( IsError() )
|
|
return true; // avoid cascading the error report
|
|
|
|
// "add" only allowed if our type is "set".
|
|
return op1->GetType()->IsSet();
|
|
}
|
|
|
|
bool IndexExpr::CanDel() const
|
|
{
|
|
if ( IsError() )
|
|
return true; // avoid cascading the error report
|
|
|
|
return op1->GetType()->Tag() == TYPE_TABLE;
|
|
}
|
|
|
|
void IndexExpr::Add(Frame* f)
|
|
{
|
|
if ( IsError() )
|
|
return;
|
|
|
|
auto v1 = op1->Eval(f);
|
|
|
|
if ( ! v1 )
|
|
return;
|
|
|
|
auto v2 = op2->Eval(f);
|
|
|
|
if ( ! v2 )
|
|
return;
|
|
|
|
bool iterators_invalidated = false;
|
|
v1->AsTableVal()->Assign(std::move(v2), nullptr, true, &iterators_invalidated);
|
|
|
|
if ( iterators_invalidated )
|
|
{
|
|
ODesc d;
|
|
Describe(&d);
|
|
reporter->PushLocation(GetLocationInfo());
|
|
reporter->Warning("possible loop/iterator invalidation caused by expression: %s",
|
|
d.Description());
|
|
reporter->PopLocation();
|
|
}
|
|
}
|
|
|
|
void IndexExpr::Delete(Frame* f)
|
|
{
|
|
if ( IsError() )
|
|
return;
|
|
|
|
auto v1 = op1->Eval(f);
|
|
|
|
if ( ! v1 )
|
|
return;
|
|
|
|
auto v2 = op2->Eval(f);
|
|
|
|
if ( ! v2 )
|
|
return;
|
|
|
|
bool iterators_invalidated = false;
|
|
v1->AsTableVal()->Remove(*v2, true, &iterators_invalidated);
|
|
|
|
if ( iterators_invalidated )
|
|
{
|
|
ODesc d;
|
|
Describe(&d);
|
|
reporter->PushLocation(GetLocationInfo());
|
|
reporter->Warning("possible loop/iterator invalidation caused by expression: %s",
|
|
d.Description());
|
|
reporter->PopLocation();
|
|
}
|
|
}
|
|
|
|
ExprPtr IndexExpr::MakeLvalue()
|
|
{
|
|
if ( IsString(op1->GetType()->Tag()) )
|
|
ExprError("cannot assign to string index expression");
|
|
|
|
return make_intrusive<RefExpr>(IntrusivePtr{NewRef{}, this});
|
|
}
|
|
|
|
ValPtr IndexExpr::Eval(Frame* f) const
|
|
{
|
|
auto v1 = op1->Eval(f);
|
|
|
|
if ( ! v1 )
|
|
return nullptr;
|
|
|
|
auto v2 = op2->Eval(f);
|
|
|
|
if ( ! v2 )
|
|
return nullptr;
|
|
|
|
Val* indv = v2->AsListVal()->Idx(0).get();
|
|
|
|
if ( is_vector(v1) && is_vector(indv) )
|
|
{
|
|
VectorVal* v_v1 = v1->AsVectorVal();
|
|
VectorVal* v_v2 = indv->AsVectorVal();
|
|
auto vt = cast_intrusive<VectorType>(GetType());
|
|
|
|
// Booleans select each element (or not).
|
|
if ( IsBool(v_v2->GetType()->Yield()->Tag()) )
|
|
{
|
|
if ( v_v1->Size() != v_v2->Size() )
|
|
{
|
|
RuntimeError("size mismatch, boolean index and vector");
|
|
return nullptr;
|
|
}
|
|
|
|
return vector_bool_select(vt, v_v1, v_v2);
|
|
}
|
|
else
|
|
// Elements are indices.
|
|
return vector_int_select(vt, v_v1, v_v2);
|
|
}
|
|
else
|
|
return Fold(v1.get(), v2.get());
|
|
}
|
|
|
|
ValPtr IndexExpr::Fold(Val* v1, Val* v2) const
|
|
{
|
|
if ( IsError() )
|
|
return nullptr;
|
|
|
|
ValPtr v;
|
|
|
|
switch ( v1->GetType()->Tag() )
|
|
{
|
|
case TYPE_VECTOR:
|
|
{
|
|
VectorVal* vect = v1->AsVectorVal();
|
|
const ListVal* lv = v2->AsListVal();
|
|
|
|
if ( lv->Length() == 1 )
|
|
v = vect->ValAt(lv->Idx(0)->CoerceToUnsigned());
|
|
else
|
|
return index_slice(vect, lv);
|
|
}
|
|
break;
|
|
|
|
case TYPE_TABLE:
|
|
v = v1->AsTableVal()->FindOrDefault({NewRef{}, v2});
|
|
break;
|
|
|
|
case TYPE_STRING:
|
|
return index_string(v1->AsString(), v2->AsListVal());
|
|
|
|
default:
|
|
RuntimeError("type cannot be indexed");
|
|
break;
|
|
}
|
|
|
|
if ( v )
|
|
return v;
|
|
|
|
RuntimeError("no such index");
|
|
return nullptr;
|
|
}
|
|
|
|
StringValPtr index_string(const String* s, const ListVal* lv)
|
|
{
|
|
int len = s->Len();
|
|
String* substring = nullptr;
|
|
|
|
if ( lv->Length() == 1 )
|
|
{
|
|
bro_int_t idx = lv->Idx(0)->AsInt();
|
|
|
|
if ( idx < 0 )
|
|
idx += len;
|
|
|
|
// Out-of-range index will return null pointer.
|
|
substring = s->GetSubstring(idx, 1);
|
|
}
|
|
else
|
|
{
|
|
bro_int_t first = get_slice_index(lv->Idx(0)->AsInt(), len);
|
|
bro_int_t last = get_slice_index(lv->Idx(1)->AsInt(), len);
|
|
bro_int_t substring_len = last - first;
|
|
|
|
if ( substring_len < 0 )
|
|
substring = nullptr;
|
|
else
|
|
substring = s->GetSubstring(first, substring_len);
|
|
}
|
|
|
|
return make_intrusive<StringVal>(substring ? substring : new String(""));
|
|
}
|
|
|
|
VectorValPtr index_slice(VectorVal* vect, const ListVal* lv)
|
|
{
|
|
auto first = lv->Idx(0)->CoerceToInt();
|
|
auto last = lv->Idx(1)->CoerceToInt();
|
|
return index_slice(vect, first, last);
|
|
}
|
|
|
|
VectorValPtr index_slice(VectorVal* vect, int _first, int _last)
|
|
{
|
|
size_t len = vect->Size();
|
|
auto result = make_intrusive<VectorVal>(vect->GetType<VectorType>());
|
|
|
|
bro_int_t first = get_slice_index(_first, len);
|
|
bro_int_t last = get_slice_index(_last, len);
|
|
bro_int_t sub_length = last - first;
|
|
|
|
if ( sub_length >= 0 )
|
|
{
|
|
result->Resize(sub_length);
|
|
|
|
for ( bro_int_t idx = first; idx < last; idx++ )
|
|
result->Assign(idx - first, vect->ValAt(idx));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
VectorValPtr vector_bool_select(VectorTypePtr vt, const VectorVal* v1, const VectorVal* v2)
|
|
{
|
|
auto v_result = make_intrusive<VectorVal>(std::move(vt));
|
|
|
|
for ( unsigned int i = 0; i < v2->Size(); ++i )
|
|
if ( v2->BoolAt(i) )
|
|
v_result->Assign(v_result->Size() + 1, v1->ValAt(i));
|
|
|
|
return v_result;
|
|
}
|
|
|
|
VectorValPtr vector_int_select(VectorTypePtr vt, const VectorVal* v1, const VectorVal* v2)
|
|
{
|
|
auto v_result = make_intrusive<VectorVal>(std::move(vt));
|
|
|
|
// The elements are indices.
|
|
//
|
|
// ### Should handle negative indices here like S does, i.e.,
|
|
// by excluding those elements. Probably only do this if *all*
|
|
// are negative.
|
|
v_result->Resize(v2->Size());
|
|
for ( unsigned int i = 0; i < v2->Size(); ++i )
|
|
v_result->Assign(i, v1->ValAt(v2->ValAt(i)->CoerceToInt()));
|
|
|
|
return v_result;
|
|
}
|
|
|
|
void IndexExpr::Assign(Frame* f, ValPtr v)
|
|
{
|
|
if ( IsError() )
|
|
return;
|
|
|
|
auto v1 = op1->Eval(f);
|
|
auto v2 = op2->Eval(f);
|
|
|
|
AssignToIndex(v1, v2, v);
|
|
}
|
|
|
|
void IndexExpr::ExprDescribe(ODesc* d) const
|
|
{
|
|
op1->Describe(d);
|
|
if ( d->IsReadable() )
|
|
d->Add("[");
|
|
|
|
op2->Describe(d);
|
|
if ( d->IsReadable() )
|
|
d->Add("]");
|
|
}
|
|
|
|
FieldExpr::FieldExpr(ExprPtr arg_op, const char* arg_field_name)
|
|
: UnaryExpr(EXPR_FIELD, std::move(arg_op)), field_name(util::copy_string(arg_field_name)),
|
|
td(nullptr), field(0)
|
|
{
|
|
if ( IsError() )
|
|
return;
|
|
|
|
if ( ! IsRecord(op->GetType()->Tag()) )
|
|
ExprError("not a record");
|
|
else
|
|
{
|
|
RecordType* rt = op->GetType()->AsRecordType();
|
|
field = rt->FieldOffset(field_name);
|
|
|
|
if ( field < 0 )
|
|
ExprError("no such field in record");
|
|
else
|
|
{
|
|
SetType(rt->GetFieldType(field));
|
|
td = rt->FieldDecl(field);
|
|
|
|
if ( rt->IsFieldDeprecated(field) )
|
|
reporter->Warning("%s", rt->GetFieldDeprecationWarning(field, false).c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
FieldExpr::~FieldExpr()
|
|
{
|
|
delete[] field_name;
|
|
}
|
|
|
|
ExprPtr FieldExpr::MakeLvalue()
|
|
{
|
|
return make_intrusive<RefExpr>(IntrusivePtr{NewRef{}, this});
|
|
}
|
|
|
|
bool FieldExpr::CanDel() const
|
|
{
|
|
return td->GetAttr(ATTR_DEFAULT) || td->GetAttr(ATTR_OPTIONAL);
|
|
}
|
|
|
|
void FieldExpr::Assign(Frame* f, ValPtr v)
|
|
{
|
|
if ( IsError() )
|
|
return;
|
|
|
|
if ( auto op_v = op->Eval(f) )
|
|
{
|
|
RecordVal* r = op_v->AsRecordVal();
|
|
r->Assign(field, std::move(v));
|
|
}
|
|
}
|
|
|
|
void FieldExpr::Delete(Frame* f)
|
|
{
|
|
Assign(f, nullptr);
|
|
}
|
|
|
|
ValPtr FieldExpr::Fold(Val* v) const
|
|
{
|
|
if ( const auto& result = v->AsRecordVal()->GetField(field) )
|
|
return result;
|
|
|
|
// Check for &default.
|
|
const Attr* def_attr = td ? td->GetAttr(ATTR_DEFAULT).get() : nullptr;
|
|
|
|
if ( def_attr )
|
|
return def_attr->GetExpr()->Eval(nullptr);
|
|
else
|
|
{
|
|
RuntimeError("field value missing");
|
|
assert(false);
|
|
return nullptr; // Will never get here, but compiler can't tell.
|
|
}
|
|
}
|
|
|
|
void FieldExpr::ExprDescribe(ODesc* d) const
|
|
{
|
|
op->Describe(d);
|
|
if ( d->IsReadable() )
|
|
d->Add("$");
|
|
|
|
if ( IsError() )
|
|
d->Add("<error>");
|
|
else if ( d->IsReadable() )
|
|
d->Add(field_name);
|
|
else
|
|
d->Add(field);
|
|
}
|
|
|
|
HasFieldExpr::HasFieldExpr(ExprPtr arg_op, const char* arg_field_name)
|
|
: UnaryExpr(EXPR_HAS_FIELD, std::move(arg_op)), field_name(arg_field_name), field(0)
|
|
{
|
|
if ( IsError() )
|
|
return;
|
|
|
|
if ( ! IsRecord(op->GetType()->Tag()) )
|
|
ExprError("not a record");
|
|
else
|
|
{
|
|
RecordType* rt = op->GetType()->AsRecordType();
|
|
field = rt->FieldOffset(field_name);
|
|
|
|
if ( field < 0 )
|
|
ExprError("no such field in record");
|
|
else if ( rt->IsFieldDeprecated(field) )
|
|
reporter->Warning("%s", rt->GetFieldDeprecationWarning(field, true).c_str());
|
|
|
|
SetType(base_type(TYPE_BOOL));
|
|
}
|
|
}
|
|
|
|
HasFieldExpr::~HasFieldExpr()
|
|
{
|
|
delete field_name;
|
|
}
|
|
|
|
ValPtr HasFieldExpr::Fold(Val* v) const
|
|
{
|
|
auto rv = v->AsRecordVal();
|
|
return val_mgr->Bool(rv->HasField(field));
|
|
}
|
|
|
|
void HasFieldExpr::ExprDescribe(ODesc* d) const
|
|
{
|
|
op->Describe(d);
|
|
|
|
if ( d->IsReadable() )
|
|
d->Add("?$");
|
|
|
|
if ( IsError() )
|
|
d->Add("<error>");
|
|
else if ( d->IsReadable() )
|
|
d->Add(field_name);
|
|
else
|
|
d->Add(field);
|
|
}
|
|
|
|
RecordConstructorExpr::RecordConstructorExpr(ListExprPtr constructor_list)
|
|
: Expr(EXPR_RECORD_CONSTRUCTOR), op(std::move(constructor_list)), map(std::nullopt)
|
|
{
|
|
if ( IsError() )
|
|
return;
|
|
|
|
// Spin through the list, which should be comprised only of
|
|
// record-field-assign expressions, and build up a
|
|
// record type to associate with this constructor.
|
|
const ExprPList& exprs = op->AsListExpr()->Exprs();
|
|
type_decl_list* record_types = new type_decl_list(exprs.length());
|
|
|
|
const Expr* constructor_error_expr = nullptr;
|
|
|
|
for ( const auto& e : exprs )
|
|
{
|
|
if ( e->Tag() != EXPR_FIELD_ASSIGN )
|
|
{
|
|
// Don't generate the error yet, as reporting it
|
|
// requires that we have a well-formed type.
|
|
constructor_error_expr = e;
|
|
SetError();
|
|
continue;
|
|
}
|
|
|
|
FieldAssignExpr* field = (FieldAssignExpr*)e;
|
|
const auto& field_type = field->GetType();
|
|
char* field_name = util::copy_string(field->FieldName());
|
|
record_types->push_back(new TypeDecl(field_name, field_type));
|
|
}
|
|
|
|
SetType(make_intrusive<RecordType>(record_types));
|
|
|
|
if ( constructor_error_expr )
|
|
Error("bad type in record constructor", constructor_error_expr);
|
|
}
|
|
|
|
RecordConstructorExpr::RecordConstructorExpr(RecordTypePtr known_rt, ListExprPtr constructor_list)
|
|
: Expr(EXPR_RECORD_CONSTRUCTOR), op(std::move(constructor_list))
|
|
{
|
|
if ( IsError() )
|
|
return;
|
|
|
|
SetType(known_rt);
|
|
|
|
const auto& exprs = op->AsListExpr()->Exprs();
|
|
map = std::vector<int>(exprs.length());
|
|
|
|
int i = 0;
|
|
for ( const auto& e : exprs )
|
|
{
|
|
if ( e->Tag() != EXPR_FIELD_ASSIGN )
|
|
{
|
|
Error("bad type in record constructor", e);
|
|
SetError();
|
|
continue;
|
|
}
|
|
|
|
auto field = e->AsFieldAssignExpr();
|
|
int index = known_rt->FieldOffset(field->FieldName());
|
|
|
|
if ( index < 0 )
|
|
{
|
|
Error("no such field in record", e);
|
|
SetError();
|
|
continue;
|
|
}
|
|
|
|
auto known_ft = known_rt->GetFieldType(index);
|
|
|
|
if ( ! field->PromoteTo(known_ft) )
|
|
SetError();
|
|
|
|
(*map)[i++] = index;
|
|
}
|
|
}
|
|
|
|
ValPtr RecordConstructorExpr::InitVal(const TypePtr& t, ValPtr aggr) const
|
|
{
|
|
if ( IsError() )
|
|
{
|
|
Error("bad record initializer");
|
|
return nullptr;
|
|
}
|
|
|
|
if ( ! init_tag_check(this, "record constructor", TYPE_RECORD, t->Tag()) )
|
|
return nullptr;
|
|
|
|
auto v = Eval(nullptr);
|
|
|
|
if ( v )
|
|
{
|
|
RecordVal* rv = v->AsRecordVal();
|
|
RecordTypePtr rt{NewRef{}, t->AsRecordType()};
|
|
auto aggr_rec = cast_intrusive<RecordVal>(std::move(aggr));
|
|
auto ar = rv->CoerceTo(std::move(rt), std::move(aggr_rec));
|
|
|
|
if ( ar )
|
|
return ar;
|
|
}
|
|
|
|
Error("bad record initializer");
|
|
return nullptr;
|
|
}
|
|
|
|
ValPtr RecordConstructorExpr::Eval(Frame* f) const
|
|
{
|
|
if ( IsError() )
|
|
return nullptr;
|
|
|
|
const auto& exprs = op->Exprs();
|
|
auto rt = cast_intrusive<RecordType>(type);
|
|
|
|
if ( ! map && exprs.length() != rt->NumFields() )
|
|
RuntimeErrorWithCallStack("inconsistency evaluating record constructor");
|
|
|
|
auto rv = make_intrusive<RecordVal>(rt);
|
|
|
|
for ( int i = 0; i < exprs.length(); ++i )
|
|
{
|
|
auto v_i = exprs[i]->Eval(f);
|
|
int ind = map ? (*map)[i] : i;
|
|
|
|
if ( v_i && v_i->GetType()->Tag() == TYPE_VECTOR &&
|
|
v_i->GetType<VectorType>()->IsUnspecifiedVector() )
|
|
{
|
|
const auto& t_ind = rt->GetFieldType(ind);
|
|
v_i->AsVectorVal()->Concretize(t_ind->Yield());
|
|
}
|
|
|
|
rv->Assign(ind, v_i);
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
bool RecordConstructorExpr::IsPure() const
|
|
{
|
|
return op->IsPure();
|
|
}
|
|
|
|
void RecordConstructorExpr::ExprDescribe(ODesc* d) const
|
|
{
|
|
auto& tn = type->GetName();
|
|
|
|
if ( tn.size() > 0 )
|
|
{
|
|
d->Add(tn);
|
|
d->Add("(");
|
|
op->Describe(d);
|
|
d->Add(")");
|
|
}
|
|
else
|
|
{
|
|
d->Add("[");
|
|
op->Describe(d);
|
|
d->Add("]");
|
|
}
|
|
}
|
|
|
|
TraversalCode RecordConstructorExpr::Traverse(TraversalCallback* cb) const
|
|
{
|
|
TraversalCode tc = cb->PreExpr(this);
|
|
HANDLE_TC_EXPR_PRE(tc);
|
|
|
|
tc = op->Traverse(cb);
|
|
HANDLE_TC_EXPR_PRE(tc);
|
|
|
|
tc = cb->PostExpr(this);
|
|
HANDLE_TC_EXPR_POST(tc);
|
|
}
|
|
|
|
TableConstructorExpr::TableConstructorExpr(ListExprPtr constructor_list,
|
|
std::unique_ptr<std::vector<AttrPtr>> arg_attrs,
|
|
TypePtr arg_type, AttributesPtr arg_attrs2)
|
|
: UnaryExpr(EXPR_TABLE_CONSTRUCTOR, std::move(constructor_list))
|
|
{
|
|
if ( IsError() )
|
|
return;
|
|
|
|
if ( arg_type )
|
|
{
|
|
if ( ! arg_type->IsTable() )
|
|
{
|
|
Error("bad table constructor type", arg_type.get());
|
|
SetError();
|
|
return;
|
|
}
|
|
|
|
SetType(std::move(arg_type));
|
|
}
|
|
else
|
|
{
|
|
if ( op->AsListExpr()->Exprs().empty() )
|
|
SetType(
|
|
make_intrusive<TableType>(make_intrusive<TypeList>(base_type(TYPE_ANY)), nullptr));
|
|
else
|
|
{
|
|
SetType(init_type(op.get()));
|
|
|
|
if ( ! type )
|
|
{
|
|
SetError();
|
|
return;
|
|
}
|
|
|
|
else if ( type->Tag() != TYPE_TABLE || type->AsTableType()->IsSet() )
|
|
SetError("values in table(...) constructor do not specify a table");
|
|
}
|
|
}
|
|
|
|
if ( arg_attrs )
|
|
attrs = make_intrusive<Attributes>(std::move(*arg_attrs), type, false, false);
|
|
else
|
|
attrs = arg_attrs2;
|
|
|
|
const auto& indices = type->AsTableType()->GetIndices()->GetTypes();
|
|
const ExprPList& cle = op->AsListExpr()->Exprs();
|
|
|
|
// check and promote all assign expressions in ctor list
|
|
for ( const auto& expr : cle )
|
|
{
|
|
if ( expr->Tag() != EXPR_ASSIGN )
|
|
continue;
|
|
|
|
auto idx_expr = expr->AsAssignExpr()->GetOp1();
|
|
auto val_expr = expr->AsAssignExpr()->GetOp2();
|
|
auto yield_type = GetType()->AsTableType()->Yield();
|
|
|
|
// Promote LHS
|
|
assert(idx_expr->Tag() == EXPR_LIST);
|
|
ExprPList& idx_exprs = idx_expr->AsListExpr()->Exprs();
|
|
|
|
if ( idx_exprs.length() != static_cast<int>(indices.size()) )
|
|
continue;
|
|
|
|
loop_over_list(idx_exprs, j)
|
|
{
|
|
ExprPtr idx = {NewRef{}, idx_exprs[j]};
|
|
|
|
auto promoted_idx = check_and_promote_expr(idx, indices[j]);
|
|
|
|
if ( promoted_idx )
|
|
{
|
|
if ( promoted_idx != idx )
|
|
Unref(idx_exprs.replace(j, promoted_idx.release()));
|
|
|
|
continue;
|
|
}
|
|
|
|
ExprError("inconsistent types in table constructor");
|
|
return;
|
|
}
|
|
|
|
// Promote RHS
|
|
if ( auto promoted_val = check_and_promote_expr(val_expr, yield_type) )
|
|
{
|
|
if ( promoted_val != val_expr )
|
|
expr->AsAssignExpr()->SetOp2(promoted_val);
|
|
}
|
|
else
|
|
{
|
|
ExprError("inconsistent types in table constructor");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
ValPtr TableConstructorExpr::Eval(Frame* f) const
|
|
{
|
|
if ( IsError() )
|
|
return nullptr;
|
|
|
|
auto aggr = make_intrusive<TableVal>(GetType<TableType>(), attrs);
|
|
const ExprPList& exprs = op->AsListExpr()->Exprs();
|
|
|
|
for ( const auto& expr : exprs )
|
|
expr->EvalIntoAggregate(type, aggr, f);
|
|
|
|
aggr->InitDefaultFunc(f);
|
|
|
|
return aggr;
|
|
}
|
|
|
|
ValPtr TableConstructorExpr::InitVal(const TypePtr& t, ValPtr aggr) const
|
|
{
|
|
if ( IsError() )
|
|
return nullptr;
|
|
|
|
if ( ! init_tag_check(this, "table constructor", TYPE_TABLE, t->Tag()) )
|
|
return nullptr;
|
|
|
|
auto tt = GetType<TableType>();
|
|
|
|
auto tval = aggr ? TableValPtr{AdoptRef{}, aggr.release()->AsTableVal()}
|
|
: make_intrusive<TableVal>(std::move(tt), attrs);
|
|
const ExprPList& exprs = op->AsListExpr()->Exprs();
|
|
|
|
for ( const auto& expr : exprs )
|
|
expr->EvalIntoAggregate(t, tval, nullptr);
|
|
|
|
return tval;
|
|
}
|
|
|
|
void TableConstructorExpr::ExprDescribe(ODesc* d) const
|
|
{
|
|
d->Add("table(");
|
|
op->Describe(d);
|
|
d->Add(")");
|
|
}
|
|
|
|
SetConstructorExpr::SetConstructorExpr(ListExprPtr constructor_list,
|
|
std::unique_ptr<std::vector<AttrPtr>> arg_attrs,
|
|
TypePtr arg_type, AttributesPtr arg_attrs2)
|
|
: UnaryExpr(EXPR_SET_CONSTRUCTOR, std::move(constructor_list))
|
|
{
|
|
if ( IsError() )
|
|
return;
|
|
|
|
if ( arg_type )
|
|
{
|
|
if ( ! arg_type->IsSet() )
|
|
{
|
|
Error("bad set constructor type", arg_type.get());
|
|
SetError();
|
|
return;
|
|
}
|
|
|
|
SetType(std::move(arg_type));
|
|
}
|
|
else
|
|
{
|
|
if ( op->AsListExpr()->Exprs().empty() )
|
|
SetType(make_intrusive<zeek::SetType>(make_intrusive<TypeList>(base_type(TYPE_ANY)),
|
|
nullptr));
|
|
else
|
|
SetType(init_type(op.get()));
|
|
}
|
|
|
|
if ( ! type )
|
|
SetError();
|
|
|
|
else if ( type->Tag() != TYPE_TABLE || ! type->AsTableType()->IsSet() )
|
|
SetError("values in set(...) constructor do not specify a set");
|
|
|
|
if ( arg_attrs )
|
|
attrs = make_intrusive<Attributes>(std::move(*arg_attrs), type, false, false);
|
|
else
|
|
attrs = arg_attrs2;
|
|
|
|
const auto& indices = type->AsTableType()->GetIndices()->GetTypes();
|
|
ExprPList& cle = op->AsListExpr()->Exprs();
|
|
|
|
if ( indices.size() == 1 )
|
|
{
|
|
if ( ! check_and_promote_exprs_to_type(op->AsListExpr(), indices[0]) )
|
|
ExprError("inconsistent type in set constructor");
|
|
}
|
|
|
|
else if ( indices.size() > 1 )
|
|
{
|
|
// Check/promote each expression in composite index.
|
|
loop_over_list(cle, i)
|
|
{
|
|
Expr* ce = cle[i];
|
|
ListExpr* le = ce->AsListExpr();
|
|
|
|
assert(ce->Tag() == EXPR_LIST);
|
|
if ( check_and_promote_exprs(le, type->AsTableType()->GetIndices()) )
|
|
{
|
|
if ( le != cle[i] )
|
|
cle.replace(i, le);
|
|
|
|
continue;
|
|
}
|
|
|
|
ExprError("inconsistent types in set constructor");
|
|
}
|
|
}
|
|
}
|
|
|
|
ValPtr SetConstructorExpr::Eval(Frame* f) const
|
|
{
|
|
if ( IsError() )
|
|
return nullptr;
|
|
|
|
auto aggr = make_intrusive<TableVal>(IntrusivePtr{NewRef{}, type->AsTableType()}, attrs);
|
|
const ExprPList& exprs = op->AsListExpr()->Exprs();
|
|
|
|
for ( const auto& expr : exprs )
|
|
{
|
|
auto element = expr->Eval(f);
|
|
aggr->Assign(std::move(element), nullptr);
|
|
}
|
|
|
|
return aggr;
|
|
}
|
|
|
|
ValPtr SetConstructorExpr::InitVal(const TypePtr& t, ValPtr aggr) const
|
|
{
|
|
if ( IsError() )
|
|
return nullptr;
|
|
|
|
if ( ! init_tag_check(this, "set constructor", TYPE_TABLE, t->Tag()) )
|
|
return nullptr;
|
|
|
|
const auto& index_type = t->AsTableType()->GetIndices();
|
|
auto tt = GetType<TableType>();
|
|
auto tval = aggr ? TableValPtr{AdoptRef{}, aggr.release()->AsTableVal()}
|
|
: make_intrusive<TableVal>(std::move(tt), attrs);
|
|
const ExprPList& exprs = op->AsListExpr()->Exprs();
|
|
|
|
for ( const auto& e : exprs )
|
|
{
|
|
auto element = check_and_promote(e->Eval(nullptr), index_type, true);
|
|
|
|
if ( ! element || ! tval->Assign(std::move(element), nullptr) )
|
|
{
|
|
Error(util::fmt("initialization type mismatch in set"), e);
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
return tval;
|
|
}
|
|
|
|
void SetConstructorExpr::ExprDescribe(ODesc* d) const
|
|
{
|
|
d->Add("set(");
|
|
op->Describe(d);
|
|
d->Add(")");
|
|
}
|
|
|
|
VectorConstructorExpr::VectorConstructorExpr(ListExprPtr constructor_list, TypePtr arg_type)
|
|
: UnaryExpr(EXPR_VECTOR_CONSTRUCTOR, std::move(constructor_list))
|
|
{
|
|
if ( IsError() )
|
|
return;
|
|
|
|
if ( arg_type )
|
|
{
|
|
if ( arg_type->Tag() != TYPE_VECTOR )
|
|
{
|
|
Error("bad vector constructor type", arg_type.get());
|
|
SetError();
|
|
return;
|
|
}
|
|
|
|
SetType(std::move(arg_type));
|
|
}
|
|
else
|
|
{
|
|
if ( op->AsListExpr()->Exprs().empty() )
|
|
{
|
|
// vector().
|
|
// By default, assign VOID type here. A vector with
|
|
// void type set is seen as an unspecified vector.
|
|
SetType(make_intrusive<VectorType>(base_type(TYPE_VOID)));
|
|
return;
|
|
}
|
|
|
|
if ( auto t = merge_type_list(op->AsListExpr()) )
|
|
SetType(make_intrusive<VectorType>(std::move(t)));
|
|
else
|
|
{
|
|
SetError();
|
|
return;
|
|
}
|
|
}
|
|
|
|
if ( ! check_and_promote_exprs_to_type(op->AsListExpr(), type->AsVectorType()->Yield()) )
|
|
ExprError("inconsistent types in vector constructor");
|
|
}
|
|
|
|
ValPtr VectorConstructorExpr::Eval(Frame* f) const
|
|
{
|
|
if ( IsError() )
|
|
return nullptr;
|
|
|
|
auto vec = make_intrusive<VectorVal>(GetType<VectorType>());
|
|
const ExprPList& exprs = op->AsListExpr()->Exprs();
|
|
|
|
loop_over_list(exprs, i)
|
|
{
|
|
Expr* e = exprs[i];
|
|
|
|
if ( ! vec->Assign(i, e->Eval(f)) )
|
|
{
|
|
RuntimeError(util::fmt("type mismatch at index %d", i));
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
return vec;
|
|
}
|
|
|
|
ValPtr VectorConstructorExpr::InitVal(const TypePtr& t, ValPtr aggr) const
|
|
{
|
|
if ( IsError() )
|
|
return nullptr;
|
|
|
|
if ( ! init_tag_check(this, "vector constructor", TYPE_VECTOR, t->Tag()) )
|
|
return nullptr;
|
|
|
|
auto vt = GetType<VectorType>();
|
|
auto vec = aggr ? VectorValPtr{AdoptRef{}, aggr.release()->AsVectorVal()}
|
|
: make_intrusive<VectorVal>(std::move(vt));
|
|
const ExprPList& exprs = op->AsListExpr()->Exprs();
|
|
|
|
loop_over_list(exprs, i)
|
|
{
|
|
Expr* e = exprs[i];
|
|
auto v = check_and_promote(e->Eval(nullptr), t->Yield(), true);
|
|
|
|
if ( ! v || ! vec->Assign(i, std::move(v)) )
|
|
{
|
|
Error(util::fmt("initialization type mismatch at index %d", i), e);
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
return vec;
|
|
}
|
|
|
|
void VectorConstructorExpr::ExprDescribe(ODesc* d) const
|
|
{
|
|
d->Add("vector(");
|
|
op->Describe(d);
|
|
d->Add(")");
|
|
}
|
|
|
|
FieldAssignExpr::FieldAssignExpr(const char* arg_field_name, ExprPtr value)
|
|
: UnaryExpr(EXPR_FIELD_ASSIGN, std::move(value)), field_name(arg_field_name)
|
|
{
|
|
SetType(op->GetType());
|
|
}
|
|
|
|
bool FieldAssignExpr::PromoteTo(TypePtr t)
|
|
{
|
|
op = check_and_promote_expr(op, t);
|
|
return op != nullptr;
|
|
}
|
|
|
|
void FieldAssignExpr::EvalIntoAggregate(const TypePtr& t, ValPtr aggr, Frame* f) const
|
|
{
|
|
if ( IsError() )
|
|
return;
|
|
|
|
if ( auto v = op->Eval(f) )
|
|
{
|
|
RecordVal* rec = aggr->AsRecordVal();
|
|
const RecordType* rt = t->AsRecordType();
|
|
|
|
int idx = rt->FieldOffset(field_name.c_str());
|
|
|
|
if ( idx < 0 )
|
|
reporter->InternalError("Missing record field: %s", field_name.c_str());
|
|
|
|
rec->Assign(idx, std::move(v));
|
|
}
|
|
}
|
|
|
|
bool FieldAssignExpr::IsRecordElement(TypeDecl* td) const
|
|
{
|
|
if ( td )
|
|
{
|
|
td->type = op->GetType();
|
|
td->id = util::copy_string(field_name.c_str());
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void FieldAssignExpr::ExprDescribe(ODesc* d) const
|
|
{
|
|
d->Add("$");
|
|
d->Add(FieldName());
|
|
d->Add("=");
|
|
|
|
if ( op )
|
|
op->Describe(d);
|
|
else
|
|
d->Add("<error>");
|
|
}
|
|
|
|
ArithCoerceExpr::ArithCoerceExpr(ExprPtr arg_op, TypeTag t)
|
|
: UnaryExpr(EXPR_ARITH_COERCE, std::move(arg_op))
|
|
{
|
|
if ( IsError() )
|
|
return;
|
|
|
|
TypeTag bt = op->GetType()->Tag();
|
|
TypeTag vbt = bt;
|
|
|
|
if ( IsVector(bt) )
|
|
{
|
|
SetType(make_intrusive<VectorType>(base_type(t)));
|
|
vbt = op->GetType()->AsVectorType()->Yield()->Tag();
|
|
}
|
|
else
|
|
SetType(base_type(t));
|
|
|
|
if ( (bt == TYPE_ENUM) != (t == TYPE_ENUM) )
|
|
ExprError("can't convert to/from enumerated type");
|
|
|
|
else if ( ! IsArithmetic(t) && ! IsBool(t) && t != TYPE_TIME && t != TYPE_INTERVAL )
|
|
ExprError("bad coercion");
|
|
|
|
else if ( ! IsArithmetic(bt) && ! IsBool(bt) && ! IsArithmetic(vbt) && ! IsBool(vbt) )
|
|
ExprError("bad coercion value");
|
|
}
|
|
|
|
ValPtr ArithCoerceExpr::FoldSingleVal(ValPtr v, const TypePtr& t) const
|
|
{
|
|
return check_and_promote(v, t, false, location);
|
|
}
|
|
|
|
ValPtr ArithCoerceExpr::Fold(Val* v) const
|
|
{
|
|
auto t = GetType();
|
|
|
|
if ( ! is_vector(v) )
|
|
{
|
|
// Our result type might be vector, in which case this
|
|
// invocation is being done per-element rather than on
|
|
// the whole vector. Correct the type if so.
|
|
if ( type->Tag() == TYPE_VECTOR )
|
|
t = t->AsVectorType()->Yield();
|
|
|
|
return FoldSingleVal({NewRef{}, v}, t);
|
|
}
|
|
|
|
VectorVal* vv = v->AsVectorVal();
|
|
auto result = make_intrusive<VectorVal>(cast_intrusive<VectorType>(t));
|
|
|
|
auto yt = t->AsVectorType()->Yield();
|
|
|
|
for ( unsigned int i = 0; i < vv->Size(); ++i )
|
|
{
|
|
auto elt = vv->ValAt(i);
|
|
if ( elt )
|
|
result->Assign(i, FoldSingleVal(elt, yt));
|
|
else
|
|
result->Assign(i, nullptr);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
RecordCoerceExpr::RecordCoerceExpr(ExprPtr arg_op, RecordTypePtr r)
|
|
: UnaryExpr(EXPR_RECORD_COERCE, std::move(arg_op))
|
|
{
|
|
if ( IsError() )
|
|
return;
|
|
|
|
SetType(std::move(r));
|
|
|
|
if ( GetType()->Tag() != TYPE_RECORD )
|
|
ExprError("coercion to non-record");
|
|
|
|
else if ( op->GetType()->Tag() != TYPE_RECORD )
|
|
ExprError("coercion of non-record to record");
|
|
|
|
else
|
|
{
|
|
RecordType* t_r = type->AsRecordType();
|
|
RecordType* sub_r = op->GetType()->AsRecordType();
|
|
|
|
int map_size = t_r->NumFields();
|
|
map.resize(map_size, -1); // -1 = field is not mapped
|
|
|
|
int i;
|
|
for ( i = 0; i < sub_r->NumFields(); ++i )
|
|
{
|
|
int t_i = t_r->FieldOffset(sub_r->FieldName(i));
|
|
if ( t_i < 0 )
|
|
{
|
|
ExprError(
|
|
util::fmt("orphaned field \"%s\" in record coercion", sub_r->FieldName(i)));
|
|
break;
|
|
}
|
|
|
|
const auto& sub_t_i = sub_r->GetFieldType(i);
|
|
const auto& sup_t_i = t_r->GetFieldType(t_i);
|
|
|
|
if ( ! same_type(sup_t_i, sub_t_i) )
|
|
{
|
|
auto is_arithmetic_promotable = [](zeek::Type* sup, zeek::Type* sub) -> bool
|
|
{
|
|
auto sup_tag = sup->Tag();
|
|
auto sub_tag = sub->Tag();
|
|
|
|
if ( ! BothArithmetic(sup_tag, sub_tag) )
|
|
return false;
|
|
|
|
if ( sub_tag == TYPE_DOUBLE && IsIntegral(sup_tag) )
|
|
return false;
|
|
|
|
if ( sub_tag == TYPE_INT && sup_tag == TYPE_COUNT )
|
|
return false;
|
|
|
|
return true;
|
|
};
|
|
|
|
auto is_record_promotable = [](zeek::Type* sup, zeek::Type* sub) -> bool
|
|
{
|
|
if ( sup->Tag() != TYPE_RECORD )
|
|
return false;
|
|
|
|
if ( sub->Tag() != TYPE_RECORD )
|
|
return false;
|
|
|
|
return record_promotion_compatible(sup->AsRecordType(), sub->AsRecordType());
|
|
};
|
|
|
|
if ( ! is_arithmetic_promotable(sup_t_i.get(), sub_t_i.get()) &&
|
|
! is_record_promotable(sup_t_i.get(), sub_t_i.get()) )
|
|
{
|
|
std::string error_msg = util::fmt("type clash for field \"%s\"",
|
|
sub_r->FieldName(i));
|
|
Error(error_msg.c_str(), sub_t_i.get());
|
|
SetError();
|
|
break;
|
|
}
|
|
}
|
|
|
|
map[t_i] = i;
|
|
}
|
|
|
|
if ( IsError() )
|
|
return;
|
|
|
|
for ( i = 0; i < map_size; ++i )
|
|
{
|
|
if ( map[i] == -1 )
|
|
{
|
|
if ( ! t_r->FieldDecl(i)->GetAttr(ATTR_OPTIONAL) )
|
|
{
|
|
std::string error_msg = util::fmt("non-optional field \"%s\" missing",
|
|
t_r->FieldName(i));
|
|
Error(error_msg.c_str());
|
|
SetError();
|
|
break;
|
|
}
|
|
}
|
|
else if ( t_r->IsFieldDeprecated(i) )
|
|
reporter->Warning("%s", t_r->GetFieldDeprecationWarning(i, false).c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
ValPtr RecordCoerceExpr::InitVal(const TypePtr& t, ValPtr aggr) const
|
|
{
|
|
if ( IsError() )
|
|
{
|
|
Error("bad record initializer");
|
|
return nullptr;
|
|
}
|
|
|
|
if ( ! init_tag_check(this, "record", TYPE_RECORD, t->Tag()) )
|
|
return nullptr;
|
|
|
|
if ( auto v = Eval(nullptr) )
|
|
{
|
|
RecordVal* rv = v->AsRecordVal();
|
|
RecordTypePtr rt{NewRef{}, t->AsRecordType()};
|
|
auto aggr_rec = cast_intrusive<RecordVal>(std::move(aggr));
|
|
|
|
if ( auto ar = rv->CoerceTo(std::move(rt), std::move(aggr_rec)) )
|
|
return ar;
|
|
}
|
|
|
|
Error("bad record initializer");
|
|
return nullptr;
|
|
}
|
|
|
|
ValPtr RecordCoerceExpr::Fold(Val* v) const
|
|
{
|
|
if ( same_type(GetType(), Op()->GetType()) )
|
|
return IntrusivePtr{NewRef{}, v};
|
|
|
|
auto rt = cast_intrusive<RecordType>(GetType());
|
|
return coerce_to_record(rt, v, map);
|
|
}
|
|
|
|
RecordValPtr coerce_to_record(RecordTypePtr rt, Val* v, const std::vector<int>& map)
|
|
{
|
|
int map_size = map.size();
|
|
auto val = make_intrusive<RecordVal>(rt);
|
|
RecordType* val_type = val->GetType()->AsRecordType();
|
|
|
|
RecordVal* rv = v->AsRecordVal();
|
|
|
|
for ( int i = 0; i < map_size; ++i )
|
|
{
|
|
if ( map[i] >= 0 )
|
|
{
|
|
auto rhs = rv->GetField(map[i]);
|
|
|
|
if ( ! rhs )
|
|
{
|
|
auto rv_rt = rv->GetType()->AsRecordType();
|
|
const auto& def = rv_rt->FieldDecl(map[i])->GetAttr(ATTR_DEFAULT);
|
|
|
|
if ( def )
|
|
rhs = def->GetExpr()->Eval(nullptr);
|
|
}
|
|
|
|
assert(rhs || rt->FieldDecl(i)->GetAttr(ATTR_OPTIONAL));
|
|
|
|
if ( ! rhs )
|
|
{
|
|
// Optional field is missing.
|
|
val->Remove(i);
|
|
continue;
|
|
}
|
|
|
|
const auto& rhs_type = rhs->GetType();
|
|
const auto& field_type = val_type->GetFieldType(i);
|
|
|
|
if ( rhs_type->Tag() == TYPE_RECORD && field_type->Tag() == TYPE_RECORD &&
|
|
! same_type(rhs_type, field_type) )
|
|
{
|
|
if ( auto new_val = rhs->AsRecordVal()->CoerceTo(
|
|
cast_intrusive<RecordType>(field_type)) )
|
|
rhs = std::move(new_val);
|
|
}
|
|
else if ( rhs_type->Tag() == TYPE_VECTOR && field_type->Tag() == TYPE_VECTOR &&
|
|
rhs_type->AsVectorType()->IsUnspecifiedVector() )
|
|
{
|
|
auto rhs_v = rhs->AsVectorVal();
|
|
if ( ! rhs_v->Concretize(field_type->Yield()) )
|
|
reporter->InternalError("could not concretize empty vector");
|
|
}
|
|
else if ( BothArithmetic(rhs_type->Tag(), field_type->Tag()) &&
|
|
! same_type(rhs_type, field_type) )
|
|
{
|
|
auto new_val = check_and_promote(rhs, field_type, false);
|
|
rhs = std::move(new_val);
|
|
}
|
|
|
|
val->Assign(i, std::move(rhs));
|
|
}
|
|
else
|
|
{
|
|
if ( const auto& def = rt->FieldDecl(i)->GetAttr(ATTR_DEFAULT) )
|
|
{
|
|
auto def_val = def->GetExpr()->Eval(nullptr);
|
|
const auto& def_type = def_val->GetType();
|
|
const auto& field_type = rt->GetFieldType(i);
|
|
|
|
if ( def_type->Tag() == TYPE_RECORD && field_type->Tag() == TYPE_RECORD &&
|
|
! same_type(def_type, field_type) )
|
|
{
|
|
auto tmp = def_val->AsRecordVal()->CoerceTo(
|
|
cast_intrusive<RecordType>(field_type));
|
|
|
|
if ( tmp )
|
|
def_val = std::move(tmp);
|
|
}
|
|
|
|
val->Assign(i, std::move(def_val));
|
|
}
|
|
else
|
|
val->Remove(i);
|
|
}
|
|
}
|
|
|
|
return val;
|
|
}
|
|
|
|
TableCoerceExpr::TableCoerceExpr(ExprPtr arg_op, TableTypePtr r)
|
|
: UnaryExpr(EXPR_TABLE_COERCE, std::move(arg_op))
|
|
{
|
|
if ( IsError() )
|
|
return;
|
|
|
|
SetType(std::move(r));
|
|
|
|
if ( GetType()->Tag() != TYPE_TABLE )
|
|
ExprError("coercion to non-table");
|
|
|
|
else if ( op->GetType()->Tag() != TYPE_TABLE )
|
|
ExprError("coercion of non-table/set to table/set");
|
|
}
|
|
|
|
TableCoerceExpr::~TableCoerceExpr() { }
|
|
|
|
ValPtr TableCoerceExpr::Fold(Val* v) const
|
|
{
|
|
TableVal* tv = v->AsTableVal();
|
|
|
|
if ( tv->Size() > 0 )
|
|
RuntimeErrorWithCallStack("coercion of non-empty table/set");
|
|
|
|
return make_intrusive<TableVal>(GetType<TableType>(), tv->GetAttrs());
|
|
}
|
|
|
|
VectorCoerceExpr::VectorCoerceExpr(ExprPtr arg_op, VectorTypePtr v)
|
|
: UnaryExpr(EXPR_VECTOR_COERCE, std::move(arg_op))
|
|
{
|
|
if ( IsError() )
|
|
return;
|
|
|
|
SetType(std::move(v));
|
|
|
|
if ( GetType()->Tag() != TYPE_VECTOR )
|
|
ExprError("coercion to non-vector");
|
|
|
|
else if ( op->GetType()->Tag() != TYPE_VECTOR )
|
|
ExprError("coercion of non-vector to vector");
|
|
}
|
|
|
|
VectorCoerceExpr::~VectorCoerceExpr() { }
|
|
|
|
ValPtr VectorCoerceExpr::Fold(Val* v) const
|
|
{
|
|
VectorVal* vv = v->AsVectorVal();
|
|
|
|
if ( vv->Size() > 0 )
|
|
RuntimeErrorWithCallStack("coercion of non-empty vector");
|
|
|
|
return make_intrusive<VectorVal>(GetType<VectorType>());
|
|
}
|
|
|
|
ScheduleTimer::ScheduleTimer(const EventHandlerPtr& arg_event, Args arg_args, double t)
|
|
: Timer(t, TIMER_SCHEDULE), event(arg_event), args(std::move(arg_args))
|
|
{
|
|
}
|
|
|
|
ScheduleTimer::~ScheduleTimer() { }
|
|
|
|
void ScheduleTimer::Dispatch(double /* t */, bool /* is_expire */)
|
|
{
|
|
if ( event )
|
|
event_mgr.Enqueue(event, std::move(args));
|
|
}
|
|
|
|
ScheduleExpr::ScheduleExpr(ExprPtr arg_when, EventExprPtr arg_event)
|
|
: Expr(EXPR_SCHEDULE), when(std::move(arg_when)), event(std::move(arg_event))
|
|
{
|
|
if ( IsError() || when->IsError() || event->IsError() )
|
|
return;
|
|
|
|
TypeTag bt = when->GetType()->Tag();
|
|
|
|
if ( bt != TYPE_TIME && bt != TYPE_INTERVAL )
|
|
ExprError("schedule expression requires a time or time interval");
|
|
else
|
|
SetType(base_type(TYPE_TIMER));
|
|
}
|
|
|
|
bool ScheduleExpr::IsPure() const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
ValPtr ScheduleExpr::Eval(Frame* f) const
|
|
{
|
|
if ( run_state::terminating )
|
|
return nullptr;
|
|
|
|
auto when_val = when->Eval(f);
|
|
|
|
if ( ! when_val )
|
|
return nullptr;
|
|
|
|
double dt = when_val->InternalDouble();
|
|
|
|
if ( when->GetType()->Tag() == TYPE_INTERVAL )
|
|
dt += run_state::network_time;
|
|
|
|
auto args = eval_list(f, event->Args());
|
|
|
|
if ( args )
|
|
timer_mgr->Add(new ScheduleTimer(event->Handler(), std::move(*args), dt));
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
TraversalCode ScheduleExpr::Traverse(TraversalCallback* cb) const
|
|
{
|
|
TraversalCode tc = cb->PreExpr(this);
|
|
HANDLE_TC_EXPR_PRE(tc);
|
|
|
|
tc = when->Traverse(cb);
|
|
HANDLE_TC_EXPR_PRE(tc);
|
|
|
|
tc = event->Traverse(cb);
|
|
HANDLE_TC_EXPR_PRE(tc);
|
|
|
|
tc = cb->PostExpr(this);
|
|
HANDLE_TC_EXPR_POST(tc);
|
|
}
|
|
|
|
void ScheduleExpr::ExprDescribe(ODesc* d) const
|
|
{
|
|
if ( d->IsReadable() )
|
|
d->AddSP("schedule");
|
|
|
|
when->Describe(d);
|
|
d->SP();
|
|
|
|
if ( d->IsReadable() )
|
|
{
|
|
d->Add("{");
|
|
d->PushIndent();
|
|
event->Describe(d);
|
|
d->PopIndent();
|
|
d->Add("}");
|
|
}
|
|
else
|
|
event->Describe(d);
|
|
}
|
|
|
|
InExpr::InExpr(ExprPtr arg_op1, ExprPtr arg_op2)
|
|
: BinaryExpr(EXPR_IN, std::move(arg_op1), std::move(arg_op2))
|
|
{
|
|
if ( IsError() )
|
|
return;
|
|
|
|
if ( op1->GetType()->Tag() == TYPE_PATTERN )
|
|
{
|
|
if ( op2->GetType()->Tag() == TYPE_STRING )
|
|
{
|
|
SetType(base_type(TYPE_BOOL));
|
|
return;
|
|
}
|
|
else if ( op2->GetType()->Tag() == TYPE_TABLE )
|
|
{
|
|
// fall through to type-checking at end of function
|
|
}
|
|
else
|
|
{
|
|
op2->GetType()->Error("pattern requires string or set/table index", op1.get());
|
|
SetError();
|
|
return;
|
|
}
|
|
}
|
|
|
|
if ( op1->GetType()->Tag() == TYPE_STRING && op2->GetType()->Tag() == TYPE_STRING )
|
|
{
|
|
SetType(base_type(TYPE_BOOL));
|
|
return;
|
|
}
|
|
|
|
// Check for: <addr> in <subnet>
|
|
// <addr> in set[subnet]
|
|
// <addr> in table[subnet] of ...
|
|
if ( op1->GetType()->Tag() == TYPE_ADDR )
|
|
{
|
|
if ( op2->GetType()->Tag() == TYPE_SUBNET )
|
|
{
|
|
SetType(base_type(TYPE_BOOL));
|
|
return;
|
|
}
|
|
|
|
if ( op2->GetType()->Tag() == TYPE_TABLE && op2->GetType()->AsTableType()->IsSubNetIndex() )
|
|
{
|
|
SetType(base_type(TYPE_BOOL));
|
|
return;
|
|
}
|
|
}
|
|
|
|
if ( op1->Tag() != EXPR_LIST )
|
|
op1 = make_intrusive<ListExpr>(std::move(op1));
|
|
|
|
ListExpr* lop1 = op1->AsListExpr();
|
|
|
|
if ( ! op2->GetType()->MatchesIndex(lop1) )
|
|
SetError("not an index type");
|
|
else
|
|
SetType(base_type(TYPE_BOOL));
|
|
}
|
|
|
|
ValPtr InExpr::Fold(Val* v1, Val* v2) const
|
|
{
|
|
if ( v2->GetType()->Tag() == TYPE_STRING )
|
|
{
|
|
const String* s2 = v2->AsString();
|
|
|
|
if ( v1->GetType()->Tag() == TYPE_PATTERN )
|
|
{
|
|
auto re = v1->As<PatternVal*>();
|
|
return val_mgr->Bool(re->MatchAnywhere(s2) != 0);
|
|
}
|
|
|
|
const String* s1 = v1->AsString();
|
|
|
|
// Could do better here e.g. Boyer-Moore if done repeatedly.
|
|
auto s = reinterpret_cast<const unsigned char*>(s1->CheckString());
|
|
auto res = util::strstr_n(s2->Len(), s2->Bytes(), s1->Len(), s) != -1;
|
|
return val_mgr->Bool(res);
|
|
}
|
|
|
|
if ( v1->GetType()->Tag() == TYPE_ADDR && v2->GetType()->Tag() == TYPE_SUBNET )
|
|
return val_mgr->Bool(v2->AsSubNetVal()->Contains(v1->AsAddr()));
|
|
|
|
bool res;
|
|
|
|
if ( is_vector(v2) )
|
|
{
|
|
auto vv2 = v2->AsVectorVal();
|
|
auto ind = v1->AsListVal()->Idx(0)->CoerceToUnsigned();
|
|
res = ind < vv2->Size() && vv2->ValAt(ind);
|
|
}
|
|
else
|
|
res = (bool)v2->AsTableVal()->Find({NewRef{}, v1});
|
|
|
|
return val_mgr->Bool(res);
|
|
}
|
|
|
|
CallExpr::CallExpr(ExprPtr arg_func, ListExprPtr arg_args, bool in_hook)
|
|
: Expr(EXPR_CALL), func(std::move(arg_func)), args(std::move(arg_args))
|
|
{
|
|
if ( func->IsError() || args->IsError() )
|
|
{
|
|
SetError();
|
|
return;
|
|
}
|
|
|
|
const auto& func_type = func->GetType();
|
|
|
|
if ( ! IsFunc(func_type->Tag()) )
|
|
{
|
|
func->Error("not a function");
|
|
SetError();
|
|
return;
|
|
}
|
|
|
|
if ( func_type->AsFuncType()->Flavor() == FUNC_FLAVOR_HOOK && ! in_hook )
|
|
{
|
|
func->Error("hook cannot be called directly, use hook operator");
|
|
SetError();
|
|
return;
|
|
}
|
|
|
|
if ( ! func_type->MatchesIndex(args.get()) )
|
|
SetError("argument type mismatch in function call");
|
|
else
|
|
{
|
|
const auto& yield = func_type->Yield();
|
|
|
|
if ( ! yield )
|
|
{
|
|
switch ( func_type->AsFuncType()->Flavor() )
|
|
{
|
|
|
|
case FUNC_FLAVOR_FUNCTION:
|
|
Error("function has no yield type");
|
|
SetError();
|
|
break;
|
|
|
|
case FUNC_FLAVOR_EVENT:
|
|
Error("event called in expression, use event statement instead");
|
|
SetError();
|
|
break;
|
|
|
|
case FUNC_FLAVOR_HOOK:
|
|
Error("hook has no yield type");
|
|
SetError();
|
|
break;
|
|
|
|
default:
|
|
Error("invalid function flavor");
|
|
SetError();
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
SetType(yield);
|
|
|
|
// Check for call to built-ins that can be statically analyzed.
|
|
ValPtr func_val;
|
|
|
|
if ( func->Tag() == EXPR_NAME &&
|
|
// This is cheating, but without it processing gets
|
|
// quite confused regarding "value used but not set"
|
|
// run-time errors when we apply this analysis during
|
|
// parsing. Really we should instead do it after we've
|
|
// parsed the entire set of scripts.
|
|
util::streq(((NameExpr*)func.get())->Id()->Name(), "fmt") &&
|
|
// The following is needed because fmt might not yet
|
|
// be bound as a name.
|
|
did_builtin_init && (func_val = func->Eval(nullptr)) )
|
|
{
|
|
zeek::Func* f = func_val->AsFunc();
|
|
if ( f->GetKind() == Func::BUILTIN_FUNC &&
|
|
! check_built_in_call((BuiltinFunc*)f, this) )
|
|
SetError();
|
|
}
|
|
}
|
|
}
|
|
|
|
bool CallExpr::IsPure() const
|
|
{
|
|
if ( IsError() )
|
|
return true;
|
|
|
|
if ( ! func->IsPure() )
|
|
return false;
|
|
|
|
auto func_val = func->Eval(nullptr);
|
|
|
|
if ( ! func_val )
|
|
return false;
|
|
|
|
zeek::Func* f = func_val->AsFunc();
|
|
|
|
// Only recurse for built-in functions, as recursing on script
|
|
// functions can lead to infinite recursion if the function being
|
|
// called here happens to be recursive (either directly
|
|
// or indirectly).
|
|
bool pure = false;
|
|
|
|
if ( f->GetKind() == Func::BUILTIN_FUNC )
|
|
pure = f->IsPure() && args->IsPure();
|
|
|
|
return pure;
|
|
}
|
|
|
|
ValPtr CallExpr::Eval(Frame* f) const
|
|
{
|
|
if ( IsError() )
|
|
return nullptr;
|
|
|
|
// If we are inside a trigger condition, we may have already been
|
|
// called, delayed, and then produced a result which is now cached.
|
|
// Check for that.
|
|
if ( f )
|
|
{
|
|
if ( trigger::Trigger* trigger = f->GetTrigger() )
|
|
{
|
|
if ( Val* v = trigger->Lookup(this) )
|
|
{
|
|
DBG_LOG(DBG_NOTIFIERS, "%s: provides cached function result", trigger->Name());
|
|
return {NewRef{}, v};
|
|
}
|
|
}
|
|
}
|
|
|
|
ValPtr ret;
|
|
auto func_val = func->Eval(f);
|
|
auto v = eval_list(f, args.get());
|
|
|
|
if ( func_val && v )
|
|
{
|
|
const zeek::Func* funcv = func_val->AsFunc();
|
|
const CallExpr* current_call = f ? f->GetCall() : nullptr;
|
|
|
|
if ( f )
|
|
f->SetCall(this);
|
|
|
|
auto& args = *v;
|
|
ret = funcv->Invoke(&args, f);
|
|
|
|
if ( f )
|
|
f->SetCall(current_call);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
TraversalCode CallExpr::Traverse(TraversalCallback* cb) const
|
|
{
|
|
TraversalCode tc = cb->PreExpr(this);
|
|
HANDLE_TC_EXPR_PRE(tc);
|
|
|
|
tc = func->Traverse(cb);
|
|
HANDLE_TC_EXPR_PRE(tc);
|
|
|
|
tc = args->Traverse(cb);
|
|
HANDLE_TC_EXPR_PRE(tc);
|
|
|
|
tc = cb->PostExpr(this);
|
|
HANDLE_TC_EXPR_POST(tc);
|
|
}
|
|
|
|
void CallExpr::ExprDescribe(ODesc* d) const
|
|
{
|
|
func->Describe(d);
|
|
if ( d->IsReadable() || d->IsPortable() )
|
|
{
|
|
d->Add("(");
|
|
args->Describe(d);
|
|
d->Add(")");
|
|
}
|
|
else
|
|
args->Describe(d);
|
|
}
|
|
|
|
LambdaExpr::LambdaExpr(std::unique_ptr<function_ingredients> arg_ing, IDPList arg_outer_ids,
|
|
StmtPtr when_parent)
|
|
: Expr(EXPR_LAMBDA)
|
|
{
|
|
ingredients = std::move(arg_ing);
|
|
outer_ids = std::move(arg_outer_ids);
|
|
|
|
SetType(ingredients->id->GetType());
|
|
|
|
CheckCaptures(when_parent);
|
|
|
|
// Install a dummy version of the function globally for use only
|
|
// when broker provides a closure.
|
|
auto dummy_func = make_intrusive<ScriptFunc>(ingredients->id, ingredients->body,
|
|
ingredients->inits, ingredients->frame_size,
|
|
ingredients->priority);
|
|
|
|
dummy_func->SetOuterIDs(outer_ids);
|
|
|
|
// Get the body's "string" representation.
|
|
ODesc d;
|
|
dummy_func->Describe(&d);
|
|
|
|
for ( ;; )
|
|
{
|
|
hash128_t h;
|
|
KeyedHash::Hash128(d.Bytes(), d.Len(), &h);
|
|
|
|
my_name = "lambda_<" + std::to_string(h[0]) + ">";
|
|
auto fullname = make_full_var_name(current_module.data(), my_name.data());
|
|
const auto& id = global_scope()->Find(fullname);
|
|
|
|
if ( id )
|
|
// Just try again to make a unique lambda name.
|
|
// If two peer processes need to agree on the same
|
|
// lambda name, this assumes they're loading the same
|
|
// scripts and thus have the same hash collisions.
|
|
d.Add(" ");
|
|
else
|
|
break;
|
|
}
|
|
|
|
// Install that in the global_scope
|
|
auto id = install_ID(my_name.c_str(), current_module.c_str(), true, false);
|
|
|
|
// Update lamb's name
|
|
dummy_func->SetName(my_name.c_str());
|
|
|
|
auto v = make_intrusive<FuncVal>(std::move(dummy_func));
|
|
id->SetVal(std::move(v));
|
|
id->SetType(ingredients->id->GetType());
|
|
id->SetConst();
|
|
}
|
|
|
|
void LambdaExpr::CheckCaptures(StmtPtr when_parent)
|
|
{
|
|
auto ft = type->AsFuncType();
|
|
const auto& captures = ft->GetCaptures();
|
|
|
|
capture_by_ref = false;
|
|
|
|
if ( ! captures )
|
|
{
|
|
if ( outer_ids.size() > 0 )
|
|
{
|
|
// TODO: Remove in v5.1: these deprecated closure semantics
|
|
reporter->Warning(
|
|
"use of outer identifiers in lambdas without [] captures is deprecated: %s%s",
|
|
outer_ids.size() > 1 ? "e.g., " : "", outer_ids[0]->Name());
|
|
capture_by_ref = true;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
std::set<const ID*> outer_is_matched;
|
|
std::set<const ID*> capture_is_matched;
|
|
|
|
auto desc = when_parent ? "\"when\" statement" : "lambda";
|
|
|
|
for ( const auto& c : *captures )
|
|
{
|
|
auto cid = c.id.get();
|
|
|
|
if ( ! cid )
|
|
// This happens for undefined/inappropriate
|
|
// identifiers listed in captures. There's
|
|
// already been an error message.
|
|
continue;
|
|
|
|
if ( capture_is_matched.count(cid) > 0 )
|
|
{
|
|
auto msg = util::fmt("%s listed multiple times in capture", cid->Name());
|
|
if ( when_parent )
|
|
when_parent->Error(msg);
|
|
else
|
|
ExprError(msg);
|
|
continue;
|
|
}
|
|
|
|
for ( auto id : outer_ids )
|
|
if ( cid == id )
|
|
{
|
|
outer_is_matched.insert(id);
|
|
capture_is_matched.insert(cid);
|
|
break;
|
|
}
|
|
}
|
|
|
|
for ( auto id : outer_ids )
|
|
if ( outer_is_matched.count(id) == 0 )
|
|
{
|
|
auto msg = util::fmt("%s is used inside %s but not captured", id->Name(), desc);
|
|
if ( when_parent )
|
|
when_parent->Error(msg);
|
|
else
|
|
ExprError(msg);
|
|
}
|
|
|
|
for ( const auto& c : *captures )
|
|
{
|
|
auto cid = c.id.get();
|
|
if ( cid && capture_is_matched.count(cid) == 0 )
|
|
{
|
|
auto msg = util::fmt("%s is captured but not used inside %s", cid->Name(), desc);
|
|
if ( when_parent )
|
|
when_parent->Error(msg);
|
|
else
|
|
ExprError(msg);
|
|
}
|
|
}
|
|
}
|
|
|
|
ScopePtr LambdaExpr::GetScope() const
|
|
{
|
|
return ingredients->scope;
|
|
}
|
|
|
|
ValPtr LambdaExpr::Eval(Frame* f) const
|
|
{
|
|
auto lamb = make_intrusive<ScriptFunc>(ingredients->id, ingredients->body, ingredients->inits,
|
|
ingredients->frame_size, ingredients->priority);
|
|
|
|
if ( capture_by_ref )
|
|
lamb->AddClosure(outer_ids, f);
|
|
else
|
|
lamb->CreateCaptures(f);
|
|
|
|
// Set name to corresponding dummy func.
|
|
// Allows for lookups by the receiver.
|
|
lamb->SetName(my_name.c_str());
|
|
|
|
return make_intrusive<FuncVal>(std::move(lamb));
|
|
}
|
|
|
|
void LambdaExpr::ExprDescribe(ODesc* d) const
|
|
{
|
|
d->Add(expr_name(Tag()));
|
|
ingredients->body->Describe(d);
|
|
}
|
|
|
|
TraversalCode LambdaExpr::Traverse(TraversalCallback* cb) const
|
|
{
|
|
TraversalCode tc = cb->PreExpr(this);
|
|
HANDLE_TC_EXPR_PRE(tc);
|
|
|
|
tc = ingredients->body->Traverse(cb);
|
|
HANDLE_TC_STMT_PRE(tc);
|
|
|
|
tc = cb->PostExpr(this);
|
|
HANDLE_TC_EXPR_POST(tc);
|
|
}
|
|
|
|
EventExpr::EventExpr(const char* arg_name, ListExprPtr arg_args)
|
|
: Expr(EXPR_EVENT), name(arg_name), args(std::move(arg_args))
|
|
{
|
|
EventHandler* h = event_registry->Lookup(name);
|
|
|
|
if ( ! h )
|
|
{
|
|
h = new EventHandler(name.c_str());
|
|
event_registry->Register(h);
|
|
}
|
|
|
|
h->SetUsed();
|
|
|
|
handler = h;
|
|
|
|
if ( args->IsError() )
|
|
{
|
|
SetError();
|
|
return;
|
|
}
|
|
|
|
const auto& func_type = h->GetType();
|
|
|
|
if ( ! func_type )
|
|
{
|
|
Error("not an event");
|
|
SetError();
|
|
return;
|
|
}
|
|
|
|
if ( ! func_type->MatchesIndex(args.get()) )
|
|
SetError("argument type mismatch in event invocation");
|
|
else
|
|
{
|
|
if ( func_type->Yield() )
|
|
{
|
|
Error("function invoked as an event");
|
|
SetError();
|
|
}
|
|
}
|
|
}
|
|
|
|
ValPtr EventExpr::Eval(Frame* f) const
|
|
{
|
|
if ( IsError() )
|
|
return nullptr;
|
|
|
|
auto v = eval_list(f, args.get());
|
|
|
|
if ( handler )
|
|
event_mgr.Enqueue(handler, std::move(*v));
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
TraversalCode EventExpr::Traverse(TraversalCallback* cb) const
|
|
{
|
|
TraversalCode tc = cb->PreExpr(this);
|
|
HANDLE_TC_EXPR_PRE(tc);
|
|
|
|
tc = args->Traverse(cb);
|
|
HANDLE_TC_EXPR_PRE(tc);
|
|
|
|
tc = cb->PostExpr(this);
|
|
HANDLE_TC_EXPR_POST(tc);
|
|
}
|
|
|
|
void EventExpr::ExprDescribe(ODesc* d) const
|
|
{
|
|
d->Add(name.c_str());
|
|
if ( d->IsReadable() || d->IsPortable() )
|
|
{
|
|
d->Add("(");
|
|
args->Describe(d);
|
|
d->Add(")");
|
|
}
|
|
else
|
|
args->Describe(d);
|
|
}
|
|
|
|
ListExpr::ListExpr() : Expr(EXPR_LIST)
|
|
{
|
|
SetType(make_intrusive<TypeList>());
|
|
}
|
|
|
|
ListExpr::ListExpr(ExprPtr e) : Expr(EXPR_LIST)
|
|
{
|
|
SetType(make_intrusive<TypeList>());
|
|
Append(std::move(e));
|
|
}
|
|
|
|
ListExpr::~ListExpr()
|
|
{
|
|
for ( const auto& expr : exprs )
|
|
Unref(expr);
|
|
}
|
|
|
|
void ListExpr::Append(ExprPtr e)
|
|
{
|
|
exprs.push_back(e.release());
|
|
((TypeList*)type.get())->Append(exprs.back()->GetType());
|
|
}
|
|
|
|
bool ListExpr::IsPure() const
|
|
{
|
|
for ( const auto& expr : exprs )
|
|
if ( ! expr->IsPure() )
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
ValPtr ListExpr::Eval(Frame* f) const
|
|
{
|
|
auto v = make_intrusive<ListVal>(TYPE_ANY);
|
|
|
|
for ( const auto& expr : exprs )
|
|
{
|
|
auto ev = expr->Eval(f);
|
|
|
|
if ( ! ev )
|
|
{
|
|
RuntimeError("uninitialized list value");
|
|
return nullptr;
|
|
}
|
|
|
|
v->Append(std::move(ev));
|
|
}
|
|
|
|
return v;
|
|
}
|
|
|
|
TypePtr ListExpr::InitType() const
|
|
{
|
|
if ( exprs.empty() )
|
|
{
|
|
Error("empty list in untyped initialization");
|
|
return nullptr;
|
|
}
|
|
|
|
if ( exprs[0]->IsRecordElement(nullptr) )
|
|
{
|
|
type_decl_list* types = new type_decl_list(exprs.length());
|
|
for ( const auto& expr : exprs )
|
|
{
|
|
TypeDecl* td = new TypeDecl(nullptr, nullptr);
|
|
if ( ! expr->IsRecordElement(td) )
|
|
{
|
|
expr->Error("record element expected");
|
|
delete td;
|
|
delete types;
|
|
return nullptr;
|
|
}
|
|
|
|
types->push_back(td);
|
|
}
|
|
|
|
return make_intrusive<RecordType>(types);
|
|
}
|
|
|
|
else
|
|
{
|
|
auto tl = make_intrusive<TypeList>();
|
|
|
|
for ( const auto& e : exprs )
|
|
{
|
|
const auto& ti = e->GetType();
|
|
|
|
// Collapse any embedded sets or lists.
|
|
if ( ti->IsSet() || ti->Tag() == TYPE_LIST )
|
|
{
|
|
TypeList* til = ti->IsSet() ? ti->AsSetType()->GetIndices().get()
|
|
: ti->AsTypeList();
|
|
|
|
if ( ! til->IsPure() || ! til->AllMatch(til->GetPureType(), true) )
|
|
tl->Append({NewRef{}, til});
|
|
else
|
|
tl->Append(til->GetPureType());
|
|
}
|
|
else
|
|
tl->Append(ti);
|
|
}
|
|
|
|
return tl;
|
|
}
|
|
}
|
|
|
|
ValPtr ListExpr::InitVal(const TypePtr& t, ValPtr aggr) const
|
|
{
|
|
// While fairly similar to the EvalIntoAggregate() code,
|
|
// we keep this separate since it also deals with initialization
|
|
// idioms such as embedded aggregates and cross-product
|
|
// expansion.
|
|
if ( IsError() )
|
|
return nullptr;
|
|
|
|
// Check whether each element of this list itself matches t,
|
|
// in which case we should expand as a ListVal.
|
|
if ( ! aggr && type->AsTypeList()->AllMatch(t, true) )
|
|
{
|
|
auto v = make_intrusive<ListVal>(TYPE_ANY);
|
|
const auto& tl = type->AsTypeList()->GetTypes();
|
|
|
|
if ( exprs.length() != static_cast<int>(tl.size()) )
|
|
{
|
|
Error("index mismatch", t.get());
|
|
return nullptr;
|
|
}
|
|
|
|
loop_over_list(exprs, i)
|
|
{
|
|
auto vi = exprs[i]->InitVal(tl[i], nullptr);
|
|
if ( ! vi )
|
|
return nullptr;
|
|
|
|
v->Append(std::move(vi));
|
|
}
|
|
|
|
return v;
|
|
}
|
|
|
|
if ( t->Tag() == TYPE_LIST )
|
|
{
|
|
if ( aggr )
|
|
{
|
|
Error("bad use of list in initialization", t.get());
|
|
return nullptr;
|
|
}
|
|
|
|
const auto& tl = t->AsTypeList()->GetTypes();
|
|
|
|
if ( exprs.length() != static_cast<int>(tl.size()) )
|
|
{
|
|
Error("index mismatch", t.get());
|
|
return nullptr;
|
|
}
|
|
|
|
auto v = make_intrusive<ListVal>(TYPE_ANY);
|
|
|
|
loop_over_list(exprs, i)
|
|
{
|
|
auto vi = exprs[i]->InitVal(tl[i], nullptr);
|
|
|
|
if ( ! vi )
|
|
return nullptr;
|
|
|
|
v->Append(std::move(vi));
|
|
}
|
|
|
|
return v;
|
|
}
|
|
|
|
if ( t->Tag() != TYPE_RECORD && t->Tag() != TYPE_TABLE && t->Tag() != TYPE_VECTOR )
|
|
{
|
|
if ( exprs.length() == 1 )
|
|
// Allow "global x:int = { 5 }"
|
|
return exprs[0]->InitVal(t, aggr);
|
|
else
|
|
{
|
|
Error("aggregate initializer for scalar type", t.get());
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
if ( ! aggr )
|
|
Internal("missing aggregate in ListExpr::InitVal");
|
|
|
|
if ( t->IsSet() )
|
|
return AddSetInit(t, std::move(aggr));
|
|
|
|
if ( t->Tag() == TYPE_VECTOR )
|
|
{
|
|
// v: vector = [10, 20, 30];
|
|
VectorVal* vec = aggr->AsVectorVal();
|
|
|
|
loop_over_list(exprs, i)
|
|
{
|
|
ExprPtr e = {NewRef{}, exprs[i]};
|
|
const auto& vyt = vec->GetType()->AsVectorType()->Yield();
|
|
auto promoted_e = check_and_promote_expr(e, vyt);
|
|
|
|
if ( promoted_e )
|
|
e = promoted_e;
|
|
|
|
if ( ! vec->Assign(i, e->Eval(nullptr)) )
|
|
{
|
|
e->Error(util::fmt("type mismatch at index %d", i));
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
return aggr;
|
|
}
|
|
|
|
// If we got this far, then it's either a table or record
|
|
// initialization. Both of those involve AssignExpr's, which
|
|
// know how to add themselves to a table or record. Another
|
|
// possibility is an expression that evaluates itself to a
|
|
// table, which we can then add to the aggregate.
|
|
for ( const auto& e : exprs )
|
|
{
|
|
if ( e->Tag() == EXPR_ASSIGN || e->Tag() == EXPR_FIELD_ASSIGN )
|
|
{
|
|
if ( ! e->InitVal(t, aggr) )
|
|
return nullptr;
|
|
}
|
|
else
|
|
{
|
|
if ( t->Tag() == TYPE_RECORD )
|
|
{
|
|
e->Error("bad record initializer", t.get());
|
|
return nullptr;
|
|
}
|
|
|
|
auto v = e->Eval(nullptr);
|
|
|
|
if ( ! same_type(v->GetType(), t) )
|
|
{
|
|
v->GetType()->Error("type clash in table initializer", t.get());
|
|
return nullptr;
|
|
}
|
|
|
|
if ( ! v->AsTableVal()->AddTo(aggr->AsTableVal(), true) )
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
return aggr;
|
|
}
|
|
|
|
ValPtr ListExpr::AddSetInit(TypePtr t, ValPtr aggr) const
|
|
{
|
|
if ( aggr->GetType()->Tag() != TYPE_TABLE )
|
|
Internal("bad aggregate in ListExpr::AddSetInit");
|
|
|
|
TableVal* tv = aggr->AsTableVal();
|
|
const TableType* tt = tv->GetType()->AsTableType();
|
|
TypeListPtr it = tt->GetIndices();
|
|
|
|
for ( const auto& expr : exprs )
|
|
{
|
|
ValPtr element;
|
|
|
|
if ( expr->GetType()->IsSet() )
|
|
// A set to flatten.
|
|
element = expr->Eval(nullptr);
|
|
else if ( expr->GetType()->Tag() == TYPE_LIST )
|
|
element = expr->InitVal(it, nullptr);
|
|
else
|
|
element = expr->InitVal(it->GetTypes()[0], nullptr);
|
|
|
|
if ( ! element )
|
|
return nullptr;
|
|
|
|
if ( element->GetType()->IsSet() )
|
|
{
|
|
if ( ! same_type(element->GetType(), t) )
|
|
{
|
|
element->Error("type clash in set initializer", t.get());
|
|
return nullptr;
|
|
}
|
|
|
|
if ( ! element->AsTableVal()->AddTo(tv, true) )
|
|
return nullptr;
|
|
|
|
continue;
|
|
}
|
|
|
|
if ( expr->GetType()->Tag() == TYPE_LIST )
|
|
element = check_and_promote(std::move(element), it, true);
|
|
else
|
|
element = check_and_promote(std::move(element), it->GetTypes()[0], true);
|
|
|
|
if ( ! element )
|
|
return nullptr;
|
|
|
|
if ( ! tv->ExpandAndInit(std::move(element), nullptr) )
|
|
return nullptr;
|
|
}
|
|
|
|
return aggr;
|
|
}
|
|
|
|
void ListExpr::ExprDescribe(ODesc* d) const
|
|
{
|
|
d->AddCount(exprs.length());
|
|
|
|
loop_over_list(exprs, i)
|
|
{
|
|
if ( (d->IsReadable() || d->IsPortable()) && i > 0 )
|
|
d->Add(", ");
|
|
|
|
exprs[i]->Describe(d);
|
|
}
|
|
}
|
|
|
|
ExprPtr ListExpr::MakeLvalue()
|
|
{
|
|
for ( const auto& expr : exprs )
|
|
if ( expr->Tag() != EXPR_NAME )
|
|
ExprError("can only assign to list of identifiers");
|
|
|
|
return make_intrusive<RefExpr>(IntrusivePtr{NewRef{}, this});
|
|
}
|
|
|
|
void ListExpr::Assign(Frame* f, ValPtr v)
|
|
{
|
|
ListVal* lv = v->AsListVal();
|
|
|
|
if ( exprs.length() != lv->Length() )
|
|
RuntimeError("mismatch in list lengths");
|
|
|
|
loop_over_list(exprs, i) exprs[i]->Assign(f, lv->Idx(i));
|
|
}
|
|
|
|
TraversalCode ListExpr::Traverse(TraversalCallback* cb) const
|
|
{
|
|
TraversalCode tc = cb->PreExpr(this);
|
|
HANDLE_TC_EXPR_PRE(tc);
|
|
|
|
for ( const auto& expr : exprs )
|
|
{
|
|
tc = expr->Traverse(cb);
|
|
HANDLE_TC_EXPR_PRE(tc);
|
|
}
|
|
|
|
tc = cb->PostExpr(this);
|
|
HANDLE_TC_EXPR_POST(tc);
|
|
}
|
|
|
|
RecordAssignExpr::RecordAssignExpr(const ExprPtr& record, const ExprPtr& init_list, bool is_init)
|
|
{
|
|
const ExprPList& inits = init_list->AsListExpr()->Exprs();
|
|
|
|
RecordType* lhs = record->GetType()->AsRecordType();
|
|
|
|
// The inits have two forms:
|
|
// 1) other records -- use all matching field names+types
|
|
// 2) a string indicating the field name, then (as the next element)
|
|
// the value to use for that field.
|
|
|
|
for ( const auto& init : inits )
|
|
{
|
|
if ( init->GetType()->Tag() == TYPE_RECORD )
|
|
{
|
|
RecordType* t = init->GetType()->AsRecordType();
|
|
|
|
for ( int j = 0; j < t->NumFields(); ++j )
|
|
{
|
|
const char* field_name = t->FieldName(j);
|
|
int field = lhs->FieldOffset(field_name);
|
|
|
|
if ( field >= 0 && same_type(lhs->GetFieldType(field), t->GetFieldType(j)) )
|
|
{
|
|
auto fe_lhs = make_intrusive<FieldExpr>(record, field_name);
|
|
auto fe_rhs = make_intrusive<FieldExpr>(IntrusivePtr{NewRef{}, init},
|
|
field_name);
|
|
Append(get_assign_expr(std::move(fe_lhs), std::move(fe_rhs), is_init));
|
|
}
|
|
}
|
|
}
|
|
|
|
else if ( init->Tag() == EXPR_FIELD_ASSIGN )
|
|
{
|
|
FieldAssignExpr* rf = (FieldAssignExpr*)init;
|
|
rf->Ref();
|
|
|
|
const char* field_name = ""; // rf->FieldName();
|
|
if ( lhs->HasField(field_name) )
|
|
{
|
|
auto fe_lhs = make_intrusive<FieldExpr>(record, field_name);
|
|
ExprPtr fe_rhs = {NewRef{}, rf->Op()};
|
|
Append(get_assign_expr(std::move(fe_lhs), std::move(fe_rhs), is_init));
|
|
}
|
|
else
|
|
{
|
|
std::string s = "No such field '";
|
|
s += field_name;
|
|
s += "'";
|
|
init_list->SetError(s.c_str());
|
|
}
|
|
}
|
|
|
|
else
|
|
{
|
|
init_list->SetError("bad record initializer");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
CastExpr::CastExpr(ExprPtr arg_op, TypePtr t) : UnaryExpr(EXPR_CAST, std::move(arg_op))
|
|
{
|
|
auto stype = Op()->GetType();
|
|
|
|
SetType(std::move(t));
|
|
|
|
if ( ! can_cast_value_to_type(stype.get(), GetType().get()) )
|
|
ExprError("cast not supported");
|
|
}
|
|
|
|
ValPtr CastExpr::Fold(Val* v) const
|
|
{
|
|
std::string error;
|
|
auto res = cast_value({NewRef{}, v}, GetType(), error);
|
|
|
|
if ( ! res )
|
|
RuntimeError(error.c_str());
|
|
|
|
return res;
|
|
}
|
|
|
|
ValPtr cast_value(ValPtr v, const TypePtr& t, std::string& error)
|
|
{
|
|
auto nv = cast_value_to_type(v.get(), t.get());
|
|
|
|
if ( nv )
|
|
return nv;
|
|
|
|
ODesc d;
|
|
|
|
d.Add("invalid cast of value with type '");
|
|
v->GetType()->Describe(&d);
|
|
d.Add("' to type '");
|
|
t->Describe(&d);
|
|
d.Add("'");
|
|
|
|
if ( same_type(v->GetType(), Broker::detail::DataVal::ScriptDataType()) &&
|
|
! v->AsRecordVal()->HasField(0) )
|
|
d.Add(" (nil $data field)");
|
|
|
|
error = d.Description();
|
|
return nullptr;
|
|
}
|
|
|
|
void CastExpr::ExprDescribe(ODesc* d) const
|
|
{
|
|
Op()->Describe(d);
|
|
d->Add(" as ");
|
|
GetType()->Describe(d);
|
|
}
|
|
|
|
IsExpr::IsExpr(ExprPtr arg_op, TypePtr arg_t)
|
|
: UnaryExpr(EXPR_IS, std::move(arg_op)), t(std::move(arg_t))
|
|
{
|
|
SetType(base_type(TYPE_BOOL));
|
|
}
|
|
|
|
ValPtr IsExpr::Fold(Val* v) const
|
|
{
|
|
if ( IsError() )
|
|
return nullptr;
|
|
|
|
return val_mgr->Bool(can_cast_value_to_type(v, t.get()));
|
|
}
|
|
|
|
void IsExpr::ExprDescribe(ODesc* d) const
|
|
{
|
|
Op()->Describe(d);
|
|
d->Add(" is ");
|
|
t->Describe(d);
|
|
}
|
|
|
|
ExprPtr get_assign_expr(ExprPtr op1, ExprPtr op2, bool is_init)
|
|
{
|
|
if ( op1->GetType()->Tag() == TYPE_RECORD && op2->GetType()->Tag() == TYPE_LIST )
|
|
return make_intrusive<RecordAssignExpr>(std::move(op1), std::move(op2), is_init);
|
|
|
|
else if ( op1->Tag() == EXPR_INDEX && op1->AsIndexExpr()->IsSlice() )
|
|
return make_intrusive<IndexSliceAssignExpr>(std::move(op1), std::move(op2), is_init);
|
|
|
|
else
|
|
return make_intrusive<AssignExpr>(std::move(op1), std::move(op2), is_init);
|
|
}
|
|
|
|
ExprPtr check_and_promote_expr(ExprPtr e, TypePtr t)
|
|
{
|
|
const auto& et = e->GetType();
|
|
TypeTag e_tag = et->Tag();
|
|
TypeTag t_tag = t->Tag();
|
|
|
|
if ( t_tag == TYPE_ANY )
|
|
{
|
|
if ( e_tag != TYPE_ANY )
|
|
return make_intrusive<CoerceToAnyExpr>(e);
|
|
|
|
return e;
|
|
}
|
|
|
|
if ( e_tag == TYPE_ANY )
|
|
return make_intrusive<CoerceFromAnyExpr>(e, t);
|
|
|
|
if ( EitherArithmetic(t_tag, e_tag) )
|
|
{
|
|
if ( e_tag == t_tag )
|
|
return e;
|
|
|
|
if ( ! BothArithmetic(t_tag, e_tag) )
|
|
{
|
|
t->Error("arithmetic mixed with non-arithmetic", e.get());
|
|
return nullptr;
|
|
}
|
|
|
|
TypeTag mt = max_type(t_tag, e_tag);
|
|
if ( mt != t_tag )
|
|
{
|
|
t->Error("over-promotion of arithmetic value", e.get());
|
|
return nullptr;
|
|
}
|
|
|
|
return make_intrusive<ArithCoerceExpr>(e, t_tag);
|
|
}
|
|
|
|
if ( t->Tag() == TYPE_RECORD && et->Tag() == TYPE_RECORD )
|
|
{
|
|
RecordType* t_r = t->AsRecordType();
|
|
RecordType* et_r = et->AsRecordType();
|
|
|
|
if ( same_type(t, et) )
|
|
return e;
|
|
|
|
if ( record_promotion_compatible(t_r, et_r) )
|
|
return make_intrusive<RecordCoerceExpr>(e, IntrusivePtr{NewRef{}, t_r});
|
|
|
|
t->Error("incompatible record types", e.get());
|
|
return nullptr;
|
|
}
|
|
|
|
if ( ! same_type(t, et) )
|
|
{
|
|
if ( t->Tag() == TYPE_TABLE && et->Tag() == TYPE_TABLE &&
|
|
et->AsTableType()->IsUnspecifiedTable() )
|
|
return make_intrusive<TableCoerceExpr>(e, IntrusivePtr{NewRef{}, t->AsTableType()});
|
|
|
|
if ( t->Tag() == TYPE_VECTOR && et->Tag() == TYPE_VECTOR &&
|
|
et->AsVectorType()->IsUnspecifiedVector() )
|
|
return make_intrusive<VectorCoerceExpr>(e, IntrusivePtr{NewRef{}, t->AsVectorType()});
|
|
|
|
if ( t->Tag() != TYPE_ERROR && et->Tag() != TYPE_ERROR )
|
|
t->Error("type clash", e.get());
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
return e;
|
|
}
|
|
|
|
bool check_and_promote_exprs(ListExpr* const elements, const TypeListPtr& types)
|
|
{
|
|
ExprPList& el = elements->Exprs();
|
|
const auto& tl = types->GetTypes();
|
|
|
|
if ( tl.size() == 1 && tl[0]->Tag() == TYPE_ANY )
|
|
return true;
|
|
|
|
if ( el.length() != static_cast<int>(tl.size()) )
|
|
{
|
|
types->Error("indexing mismatch", elements);
|
|
return false;
|
|
}
|
|
|
|
loop_over_list(el, i)
|
|
{
|
|
ExprPtr e = {NewRef{}, el[i]};
|
|
auto promoted_e = check_and_promote_expr(e, tl[i]);
|
|
|
|
if ( ! promoted_e )
|
|
{
|
|
e->Error("type mismatch", tl[i].get());
|
|
return false;
|
|
}
|
|
|
|
if ( promoted_e != e )
|
|
Unref(el.replace(i, promoted_e.release()));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool check_and_promote_args(ListExpr* const args, const RecordType* types)
|
|
{
|
|
ExprPList& el = args->Exprs();
|
|
int ntypes = types->NumFields();
|
|
|
|
// give variadic BIFs automatic pass
|
|
if ( ntypes == 1 && types->FieldDecl(0)->type->Tag() == TYPE_ANY )
|
|
return true;
|
|
|
|
if ( el.length() < ntypes )
|
|
{
|
|
std::vector<ExprPtr> def_elements;
|
|
|
|
// Start from rightmost parameter, work backward to fill in missing
|
|
// arguments using &default expressions.
|
|
for ( int i = ntypes - 1; i >= el.length(); --i )
|
|
{
|
|
auto td = types->FieldDecl(i);
|
|
const auto& def_attr = td->attrs ? td->attrs->Find(ATTR_DEFAULT).get() : nullptr;
|
|
|
|
if ( ! def_attr )
|
|
{
|
|
types->Error("parameter mismatch", args);
|
|
return false;
|
|
}
|
|
|
|
// Don't use the default expression directly, as
|
|
// doing so will wind up sharing its code across
|
|
// different invocations that use the default
|
|
// argument. That works okay for the interpreter,
|
|
// but if we transform the code we want that done
|
|
// separately for each instance, rather than
|
|
// one instance inheriting the transformed version
|
|
// from another.
|
|
const auto& e = def_attr->GetExpr();
|
|
def_elements.emplace_back(e->Duplicate());
|
|
}
|
|
|
|
auto ne = def_elements.size();
|
|
while ( ne )
|
|
el.push_back(def_elements[--ne].release());
|
|
}
|
|
|
|
auto tl = make_intrusive<TypeList>();
|
|
|
|
for ( int i = 0; i < types->NumFields(); ++i )
|
|
tl->Append(types->GetFieldType(i));
|
|
|
|
int rval = check_and_promote_exprs(args, tl);
|
|
|
|
return rval;
|
|
}
|
|
|
|
bool check_and_promote_exprs_to_type(ListExpr* const elements, TypePtr t)
|
|
{
|
|
ExprPList& el = elements->Exprs();
|
|
|
|
if ( t->Tag() == TYPE_ANY )
|
|
return true;
|
|
|
|
loop_over_list(el, i)
|
|
{
|
|
ExprPtr e = {NewRef{}, el[i]};
|
|
auto promoted_e = check_and_promote_expr(e, t);
|
|
|
|
if ( ! promoted_e )
|
|
{
|
|
e->Error("type mismatch", t.get());
|
|
return false;
|
|
}
|
|
|
|
if ( promoted_e != e )
|
|
Unref(el.replace(i, promoted_e.release()));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
std::optional<std::vector<ValPtr>> eval_list(Frame* f, const ListExpr* l)
|
|
{
|
|
const ExprPList& e = l->Exprs();
|
|
auto rval = std::make_optional<std::vector<ValPtr>>();
|
|
rval->reserve(e.length());
|
|
|
|
for ( const auto& expr : e )
|
|
{
|
|
auto ev = expr->Eval(f);
|
|
|
|
if ( ! ev )
|
|
return {};
|
|
|
|
rval->emplace_back(std::move(ev));
|
|
}
|
|
|
|
return rval;
|
|
}
|
|
|
|
bool expr_greater(const Expr* e1, const Expr* e2)
|
|
{
|
|
return e1->Tag() > e2->Tag();
|
|
}
|
|
|
|
} // namespace zeek::detail
|