factoring of ZAM operation specifications into separate files

This commit is contained in:
Vern Paxson 2024-08-05 17:57:43 +01:00 committed by Arne Welzel
parent 5fc2c601b4
commit e94764982d
19 changed files with 3139 additions and 3370 deletions

@ -1 +1 @@
Subproject commit 610cf8527dad7033b971595a1d556c2c95294f2b Subproject commit cfc0c7b9de63f44419c2a57040ae6b7081a66a33

View file

@ -162,7 +162,23 @@ list(APPEND BINPAC_OUTPUTS "${BINPAC_OUTPUT_CC}")
include(Gen-ZAM) include(Gen-ZAM)
set(GEN_ZAM_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/script_opt/ZAM/OPs) set(GEN_ZAM_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/script_opt/ZAM/OPs)
set(GEN_ZAM_SRC ${GEN_ZAM_SRC_DIR}/ZAM.op) set(ZAM_OP_SRCS
${GEN_ZAM_SRC_DIR}/aggr-assignments.op
${GEN_ZAM_SRC_DIR}/binary-exprs.op
${GEN_ZAM_SRC_DIR}/calls.op
${GEN_ZAM_SRC_DIR}/coercions.op
${GEN_ZAM_SRC_DIR}/constructors.op
${GEN_ZAM_SRC_DIR}/indexing.op
${GEN_ZAM_SRC_DIR}/internal.op
${GEN_ZAM_SRC_DIR}/iterations.op
${GEN_ZAM_SRC_DIR}/macros.op
${GEN_ZAM_SRC_DIR}/non-uniform.op
${GEN_ZAM_SRC_DIR}/rel-exprs.op
${GEN_ZAM_SRC_DIR}/script-idioms.op
${GEN_ZAM_SRC_DIR}/stmts.op
${GEN_ZAM_SRC_DIR}/unary-exprs.op
${GEN_ZAM_SRC_DIR}/ZBI.op)
set(GEN_ZAM_SRC ${ZAM_OP_SRCS})
gen_zam_target(${GEN_ZAM_SRC_DIR}) gen_zam_target(${GEN_ZAM_SRC_DIR})

View file

@ -0,0 +1,286 @@
# See the file "COPYING" in the main distribution directory for copyright.
# This directory contains templates used to generate virtual functions, opcodes,
# and evaluation code for compiled code. Each template describes a ZAM
# "operation", which generally corresponds to a set of concrete ZAM
# "instructions". (See ZInst.h for the layout of ZAM instructions.) Often
# a single ZAM operation gives rise to a family of instructions that differ
# in either the nature of the instruction's operands (typically, whether
# they are variables residing on the ZAM execution frame, or constants)
# and/or the Zeek type of the operands (e.g., "count" or "double" or "addr").
#
# The Gen-ZAM utility processes this file to generate numerous C++ inclusion
# files that are then compiled into Zeek. These files span the range of (1)
# hooks that enable run-time generation of ZAM code to execute ASTs (which
# have first been transformed to "reduced" form), (2) specifications of the
# properties of the different instructions, (3) code to evaluate (execute)
# each instruction, and (4) macros (C++ #define's) to aid in writing that
# code. See Gen-ZAM.h for a list of the different inclusion files.
#
# Operation templates are declarative, other than the imperative C++ snippets
# they include for instruction evaluation/execution. You specify a template
# using lines of text for which, for the most part, the first word on the
# line designates an "attribute" associated with the template, and the
# remainder of the line provides specifiers/arguments for that attribute.
# A blank line (or end of file) ends the template. By convention, for
# templates that include C++ evaluation snippets, those are specified as the
# last attribute. Comments begin with '#' at the start of the line (no
# leading whitespace allowed), and can be intermingled with a template's
# attributes.
#
# Each ZAM instruction includes up to 4 integer values and one constant
# (specified as a ZVal). Often, the integer values are interpreted as offsets
# ("slots") into the ZAM execution "frame", though sometimes they have other
# meanings, such as the offset of a particular field in a record, or an index
# into the ZAM code for a branch instruction. Most instructions compute
# some sort of result (expressed as a ZVal) that is stored into the frame
# slot specified by the instruction's first integer value. We refer to this
# target as the "assignment slot", and to the other 3 integer values as
# "operands". Thus, for example, an instruction with two operands used the
# first 3 integer values, the first as the assignment slot and the other two
# for computing the result to put in that slot.
#
# Instruction templates have one or more "type"s associated with them (as
# discussed below) specifying the types of operands (variables corresponding
# to slots, or constants) associated with the instruction. In the evaluation
# code for an instruction, these are referred to with $-parameters, such as
# $1 for the first operand. The special parameter $$ refers to the *assignment
# target* of the instruction, if applicable. These parameters always come
# first when specifying an instruction's type. For example, a type of "VVC"
# specifies an instruction with two variables and one constant associated
# with it. If the instruction assigns a value, then in the evaluation these
# will be specified as $$, $1 and $2, respectively. If it does not (usually
# reflected by the template having the "op1-read" attribute) then they
# are specified as $1, $2 and $3, respectively. See "eval" below.
#
# The first attribute of each template states the type of operation specified
# in the template, along with the name of the operation. The possible types
# are:
#
# op an operation that generally corresponds to a single ZAM
# instruction, and is fully specified
#
# expr-op an operation corresponding to an AST expression node
# (some sort of Expr object). Gen-ZAM generates code for
# automatically converting Expr objects to ZAM instructions.
# The name of the operation must match that used in the AST
# tag, so for example for "expr-op Foo" there must be a
# corresponding "EXPR_FOO" tag.
#
# unary-expr-op an expr-op for a unary Expr object
# binary-expr-op an expr-op for a binary Expr object
# rel-expr-op an expr-op for a (binary) Expr object that
# represents a relational operation
#
# assign-op directly assigning either a ZVal or a record field
# to either a frame slot or a record field
#
# unary-op an operation with one operand that requires special
# treatment that doesn't fit with how unary-expr-op's
# are expressed
#
# direct-unary-op an operation with one operand that corresponds to
# a specific ZAMCompiler method for generating its
# instruction
#
# internal-op similar to "op", but for ZAM instructions only used
# internally, and thus not having any AST counterpart
# internal-assignment-op the same, for operations that assign ZVals
# produced by loading interpreter variables
# or calling functions
#
# After specifying the type of operation, you list additional attributes to
# fill out the template, ending by convention with the C++ evaluation snippet
# (if appropriate). The most significant (and complex) of these are:
#
# class specifies how to interpret the operation in terms of ZAM
# instruction slots (and constant). The specification is
# in terms of single-letter mnemonics for the different
# possible classes:
#
# F special value designating a record field being
# assigned to
# H event handler
# L list of values
# O opaque value (here, "opaque" refers to ZAM
# internals, not OpaqueVal)
# R record
# V variable (frame slot)
# X used to indicate an empty specifier
# b branch target
# f iteration information associated with table "for" loop
# g access to a global
# i integer constant, often a record field offset
# s iteration information associated with stepping
# through a vector or string
#
# The full specification consists of concatenating mnemonics
# with the order left-to-right corresponding to each of the
# instruction's 4 integer values (stopping with the last one
# used). If the operation includes a constant, then it is
# listed at the point reflecting where the constant is used as
# an operand. For example, a class of "VVCV" means that the
# first integer is used as a frame variable (i.e., the usual
# "assignment slot"), the second integer (first "operand") is
# also a frame variable, the second operand is the instruction's
# constant, and the third operand is the instruction's third
# integer value, with the fourth integer value not being used.
#
# classes specifies a number of "class" values to instantiate over.
# Cannot be combined with "class", nor used for expressions.
#
# op-type for some form of expr-op, specifies to which Zeek scripting
# types the expression applies:
#
# A addr
# D double
# F file
# I int
# N subnet
# P pattern
# R record
# S string
# T table
# U count
# V vector
#
# along with two special types: 'X' indicates that Gen-ZAM
# should not iterate over any possible values, and '*'
# indicates that Gen-ZAM should additionally iterate over
# all of possible values not explicitly listed (used in
# conjunction with eval-type - see below)
#
# op-types similar to op-type, but lists a type for each operand
# (including assignment target), so for example "A N A"
# would correspond to a 3-operand instruction for which
# the first operand (or assignment target) is an "addr",
# the second a "subnet", and the third another "addr".
#
# Note that these types collectively apply to each instance of
# an operation, whereas listing multiple "op-type" types
# iterates through those one-at-a-time in turn (and generally
# the point is that the each type applies to *all* operands,
# rather than a per-operand list). Given that, the two are
# incompatible.
#
# For operands corresponding to 'i' or any of the internal types,
# such as 'b', 'f', 'g', and 's', the corresponding type to
# list is 'I', used for integer access.
#
# eval specifies a block of C++ code used to evaluation the
# execution of the instruction. The block begins with the
# remainder of the "eval" line and continues until either a
# blank line or a line that starts with non-whitespace.
#
# Blocks can include special '$' parameters that Gen-ZAM
# automatically expands. "$1" refers to an operation's first
# operand, "$2" to its second, etc. "$$" refers to the
# operation's assignment target.
#
# For simple expr-op's you can express the block as simply
# the C++ expression to compute. For example, for multiplication
# (named "Times"), the "eval" block is simply "$1 * $2",
# rather than "$$ = $1 * $2"; Gen-ZAM knows to expand it
# accordingly.
#
# Finally, to help with avoiding duplicate code, you can
# define macros that expand to code snippets you want to use
# in multiple places. You specify these using a "macro"
# keyword followed by the name of the macro and an evaluation
# block. Macros behave identically to C++ #define's, except
# you don't use "\" to continue them across line breaks, but
# instead just indent the lines you want included, ending
# (as with "eval" blocks) with an empty line or a line that
# starts with non-whitespace.
#
# We list the remaining types of attributes alphabetically. Note that some
# only apply to certain types of operations.
#
# assign-val for an assignment operation, the name of the
# C++ variable that holds the value to assign
#
# custom-method a ZAMCompiler method that Gen-ZAM should use for
# this operation, rather than generating one
#
# eval-mixed an expression "eval" block that applies to two
# different op-type's
#
# eval-type evaluation code associated with one specific op-type
#
# explicit-result-type the operation's evaluation yields a ZVal
# rather than a low-level C++ type
#
# field-op the operation is a direct assignment to a record field
#
# includes-field-op the operation should include a version
# that assigns to a record field as well as a
# version for assigning to a frame variable
#
# indirect-call the operation represents an indirect call (through
# a global variable, rather than directly). Only
# meaningful if num-call-args is also specified.
#
# indirect-local-call same, but via a local variable rather than
# global
#
# method-post C++ code to add to the end of the method that
# dynamically generates ZAM code
#
# no-const do not generate a version of the unary-expr-op
# where the operand is a constant
#
# no-eval this operation does not have an "eval" block
# (because it will be translated instead into internal
# operations)
#
# num-call-args indicates that the operation is a function call,
# and specifies how many arguments the call takes.
# A specification of 'n' means "build a ZAM instruction
# for calling with an arbitrary number of arguments".
#
# op1-internal states that the operation's treatment of the
# instruction's first integer value is for internal
# purposes; the value does not correspond to a frame
# variable
#
# op1-read the operation treats the instruction's first integer
# value as a frame variable, but only reads the value.
# (The default is that the frame variable is written
# to but not read.)
#
# op1-read-write the operation treats the instruction's first integer
# value as a frame variable, and both reads and
# writes the value.
#
# precheck a test conducted before evaluating an expression,
# which is skipped if the test is true. Must be used
# in conjunction with precheck-action.
#
# precheck-action code to execute if a precheck is true, instead
# of evaluating the expression. Must be used in
# conjunction with precheck.
#
# set-type the instruction's primary type comes from either the
# assignment target ("$$"), the first operand ("$1"),
# or the second operand ("$2")
#
# set-type2 the same as set-type but for the instruction's
# secondary type
#
# side-effects the operation has side-effects, so even if its
# assignment target winds up being "dead" (the value is
# no longer used), the operation should still occur.
# Optionally, this attribute can include two arguments
# specifying the ZAM opcode to use if the assignment
# is dead, and the internal "type" of that opcode.
#
# For example, "side-effects OP_CALL1_V OP_V" means
# "this operation has side-effects; if eliminating
# its assignment, change the ZAM op-code to OP_CALL1_V,
# which has an internal type of OP_V".
#
# vector generate a version of the operation that takes
# vectors as operands
#
# Finally, a note concernning comments: due to internal use of C++ #define
# macros, comments in C++ code should use /* ... */ rather than // delimiters.

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,627 @@
# Operations corresponding to ZAM BuiltIn Functions.
internal-op Remove-Teredo
op1-read
class V
op-types R
eval auto teredo = zeek::packet_mgr->GetAnalyzer("Teredo");
if ( teredo )
{
zeek::detail::ConnKey conn_key($1);
static_cast<zeek::packet_analysis::teredo::TeredoAnalyzer*>(teredo.get())->RemoveConnection(conn_key);
}
internal-op Remove-Teredo
side-effects OP_REMOVE_TEREDO_V OP_V
class VV
op-types I R
eval auto teredo = zeek::packet_mgr->GetAnalyzer("Teredo");
if ( teredo )
{
zeek::detail::ConnKey conn_key($1);
static_cast<zeek::packet_analysis::teredo::TeredoAnalyzer*>(teredo.get())->RemoveConnection(conn_key);
}
$$ = 1;
internal-op Remove-GTPv1
op1-read
class V
op-types R
eval auto gtpv1 = zeek::packet_mgr->GetAnalyzer("GTPv1");
if ( gtpv1 )
{
zeek::detail::ConnKey conn_key($1);
static_cast<zeek::packet_analysis::gtpv1::GTPv1_Analyzer*>(gtpv1.get())->RemoveConnection(conn_key);
}
internal-op Remove-GTPv1
side-effects OP_REMOVE_GTPV1_V OP_V
class VV
op-types I R
eval auto gtpv1 = zeek::packet_mgr->GetAnalyzer("GTPv1");
if ( gtpv1 )
{
zeek::detail::ConnKey conn_key($1);
static_cast<zeek::packet_analysis::gtpv1::GTPv1_Analyzer*>(gtpv1.get())->RemoveConnection(conn_key);
}
$$ = 1;
internal-op Set-File-Handle
op1-read
class V
op-types S
eval auto handle = $1;
auto bytes = reinterpret_cast<const char*>(handle->Bytes());
auto h = std::string(bytes, handle->Len());
zeek::file_mgr->SetHandle(h);
internal-op Subnet-To-Addr
class VV
op-types X N
eval auto addr_v = make_intrusive<AddrVal>($1->Prefix());
ZVal::DeleteManagedType($$);
$$ = ZVal(std::move(addr_v));
macro EvalSubBytes(lhs, arg1, arg2, arg3)
{
auto sv = ZAM_sub_bytes(arg1, arg2, arg3);
Unref(lhs);
lhs = sv;
}
internal-op Sub-Bytes
classes VVVV VVVC VVCV VCVV VVCi VCVi
op-types S S U I
eval EvalSubBytes($$, $1, $2, $3)
# Use a distinct name because due to the convention when constructing
# instructions, frame slots are always positioned earlier than non-frame
# slots, i.e. we can't construct "VCiV", which is why the arguments are
# in a different order than above.
internal-op Sub-Bytes2
class VCVi
op-types S S I U
eval EvalSubBytes($$, $1, $3, $2)
internal-op Time-To-Double
class VV
op-types D D
eval $$ = $1;
internal-op To-Lower
class VV
op-types S S
eval auto sv = ZAM_to_lower($1);
Unref($$);
$$ = sv;
# A ZAM version of Log::__write. In calls to it, the first argument
# is generally a constant (enum) *if we inlined*, but otherwise a
# parameter, so we support both VVV ad VVC.
#
# It's actually the case that the return value is pretty much always
# ignored ... plus optimization can elide it away. See the second
# pair of built-ins for versions that discard the return value.
#
# Could speed things up further by modifying the Write method to just
# take the raw enum value, as it appears that that's all that's ever
# actually used.
macro LogWritePre(id_val, columns_val)
auto id = id_val;
auto columns = columns_val;
macro LogWriteResPost(lhs)
bool result = log_mgr->Write(id->AsEnumVal(), columns->AsRecordVal());
lhs = result;
internal-op Log-Write
side-effects OP_LOG_WRITE_VV OP_VV
class VVV
op-types I X R
eval LogWritePre(LogEnum($1), $2)
LogWriteResPost($$)
### Check that invoked correctly
internal-op Log-WriteC
side-effects OP_LOG_WRITE_CV OP_VC
class VCV
op-types I X R
eval LogWritePre(LogEnum($1), $2)
LogWriteResPost($$)
# Versions that discard the return value.
internal-op Log-Write
side-effects
op1-read
classes VV CV
op-types X R
eval LogWritePre(LogEnum($1), $2)
(void) log_mgr->Write(id->AsEnumVal(), columns->AsRecordVal());
internal-op Broker-Flush-Logs
side-effects OP_BROKER_FLUSH_LOGS_X OP_X
class V
op-types U
eval $$ = broker_mgr->FlushLogBuffers();
internal-op Broker-Flush-Logs
side-effects
class X
eval (void) broker_mgr->FlushLogBuffers();
internal-op Get-Port-Transport-Proto
class VV
op-types U U
eval auto mask = $1 & PORT_SPACE_MASK;
auto v = 0; /* TRANSPORT_UNKNOWN */
if ( mask == TCP_PORT_MASK )
v = 1;
else if ( mask == UDP_PORT_MASK )
v = 2;
else if ( mask == ICMP_PORT_MASK )
v = 3;
$$ = v;
predicate-op Conn-Exists
class V
op-types R
eval session_mgr->FindConnection($1) != nullptr
internal-op Lookup-Conn
class VV
op-types X R
eval auto cid = $1;
Connection* conn = session_mgr->FindConnection(cid);
ValPtr res;
if ( conn )
res = conn->GetVal();
else
{
ERROR2("connection ID not a known connection", cid);
res = build_dummy_conn_record();
}
AssignTarget($$, ZVal(res, res->GetType()));
predicate-op Is-ICMP-Port
class V
op-types U
eval ($1 & PORT_SPACE_MASK) == ICMP_PORT_MASK
predicate-op Is-TCP-Port
class V
op-types U
eval ($1 & PORT_SPACE_MASK) == TCP_PORT_MASK
predicate-op Is-UDP-Port
class V
op-types U
eval ($1 & PORT_SPACE_MASK) == UDP_PORT_MASK
predicate-op Is-V4-Addr
class V
op-types A
eval $1->AsAddr().GetFamily() == IPv4
predicate-op Is-V6-Addr
class V
op-types A
eval $1->AsAddr().GetFamily() == IPv6
internal-op Network-Time
class V
op-types D
eval $$ = run_state::network_time;
internal-op Current-Time
class V
op-types D
eval $$ = util::current_time();
predicate-op Reading-Live-Traffic
class X
eval run_state::reading_live
predicate-op Reading-Traces
class X
eval run_state::reading_traces
internal-op Sort
op1-read
class V
op-types V
eval if ( $1->Size() > 1 )
$1->Sort();
internal-op Sort
class VV
op-types V V
eval auto vv = $1;
if ( vv->Size() > 1 )
vv->Sort();
zeek::Ref(vv);
Unref($$);
$$ = vv;
internal-op Sort-With-Cmp
op1-read
class VV
op-types V F
eval if ( $1->Size() > 1 )
$1->Sort($2);
internal-op Sort-With-Cmp
class VVV
op-types V V F
eval auto vv = $1;
if ( vv->Size() > 1 )
vv->Sort($2);
zeek::Ref(vv);
Unref($$);
$$ = vv;
internal-op Starts-With
classes VVV VCV VVC
op-types I S S
eval auto str = $1;
auto sub = $2;
auto str_n = str->Len();
auto sub_n = sub->Len();
if ( str_n < sub_n )
$$ = 0;
else
{
auto str_b = str->Bytes();
auto sub_b = sub->Bytes();
int i;
for ( i = 0; i < sub_n; ++i )
if ( str_b[i] != sub_b[i] )
break;
$$ = i == sub_n;
}
internal-op StrCmp
classes VVV VCV VVC
op-types I S S
eval auto s1 = $1;
auto s2 = $2;
$$ = Bstr_cmp(s1->AsString(), s2->AsString());
internal-op StrStr
classes VVV VCV VVC
op-types I S S
eval auto big = $1;
auto little = $2;
$$ = 1 + big->AsString()->FindSubstring(little->AsString());
macro Cat1Op(lhs, val)
auto& v1 = lhs;
ZVal::DeleteManagedType(v1);
v1 = val;
internal-op Cat1
classes VV VC
eval Cat1Op($$, $1)
zeek::Ref(v1.AsString());
internal-op Cat1Full
classes VV VC
eval auto formatted_val = ZVal(ZAM_val_cat($1.ToVal(Z_TYPE)));
Cat1Op($$, formatted_val)
internal-op CatN
class V
eval CatNPre()
int n = aux->n;
size_t max_size = 0;
for ( int i = 0; i < n; ++i )
max_size += ca[i]->MaxSize(aux->elems[i].ToDirectZVal(frame));
auto res = new char[max_size + /* slop */ n + 1];
auto res_p = res;
for ( int i = 0; i < n; ++i )
ca[i]->RenderInto(aux->elems[i].ToDirectZVal(frame), res_p);
*res_p = '\0';
auto s = new String(true, reinterpret_cast<byte_vec>(res), res_p - res);
Cat1Op($$, ZVal(new StringVal(s)))
macro CatNPre()
auto aux = Z_AUX;
auto& ca = aux->cat_args;
macro CatNMid()
auto res = new char[max_size + /* slop */ 10];
auto res_p = res;
macro CatNPost(lhs)
*res_p = '\0';
auto s = new String(true, reinterpret_cast<byte_vec>(res), res_p - res);
Cat1Op(lhs, ZVal(new StringVal(s)))
internal-op Cat2
class V
eval CatNPre()
size_t max_size = ca[0]->MaxSize(aux->elems[0].ToDirectZVal(frame));
max_size += ca[1]->MaxSize(aux->elems[1].ToDirectZVal(frame));
CatNMid()
ca[0]->RenderInto(aux->elems[0].ToDirectZVal(frame), res_p);
ca[1]->RenderInto(aux->elems[1].ToDirectZVal(frame), res_p);
CatNPost($$)
internal-op Cat3
class V
eval CatNPre()
size_t max_size = ca[0]->MaxSize(aux->elems[0].ToDirectZVal(frame));
max_size += ca[1]->MaxSize(aux->elems[1].ToDirectZVal(frame));
max_size += ca[2]->MaxSize(aux->elems[2].ToDirectZVal(frame));
CatNMid()
ca[0]->RenderInto(aux->elems[0].ToDirectZVal(frame), res_p);
ca[1]->RenderInto(aux->elems[1].ToDirectZVal(frame), res_p);
ca[2]->RenderInto(aux->elems[2].ToDirectZVal(frame), res_p);
CatNPost($$)
internal-op Cat4
class V
eval CatNPre()
size_t max_size = ca[0]->MaxSize(aux->elems[0].ToDirectZVal(frame));
max_size += ca[1]->MaxSize(aux->elems[1].ToDirectZVal(frame));
max_size += ca[2]->MaxSize(aux->elems[2].ToDirectZVal(frame));
max_size += ca[3]->MaxSize(aux->elems[3].ToDirectZVal(frame));
CatNMid()
ca[0]->RenderInto(aux->elems[0].ToDirectZVal(frame), res_p);
ca[1]->RenderInto(aux->elems[1].ToDirectZVal(frame), res_p);
ca[2]->RenderInto(aux->elems[2].ToDirectZVal(frame), res_p);
ca[3]->RenderInto(aux->elems[3].ToDirectZVal(frame), res_p);
CatNPost($$)
internal-op Cat5
class V
eval CatNPre()
size_t max_size = ca[0]->MaxSize(aux->elems[0].ToDirectZVal(frame));
max_size += ca[1]->MaxSize(aux->elems[1].ToDirectZVal(frame));
max_size += ca[2]->MaxSize(aux->elems[2].ToDirectZVal(frame));
max_size += ca[3]->MaxSize(aux->elems[3].ToDirectZVal(frame));
max_size += ca[4]->MaxSize(aux->elems[4].ToDirectZVal(frame));
CatNMid()
ca[0]->RenderInto(aux->elems[0].ToDirectZVal(frame), res_p);
ca[1]->RenderInto(aux->elems[1].ToDirectZVal(frame), res_p);
ca[2]->RenderInto(aux->elems[2].ToDirectZVal(frame), res_p);
ca[3]->RenderInto(aux->elems[3].ToDirectZVal(frame), res_p);
ca[4]->RenderInto(aux->elems[4].ToDirectZVal(frame), res_p);
CatNPost($$)
internal-op Cat6
class V
eval CatNPre()
size_t max_size = ca[0]->MaxSize(aux->elems[0].ToDirectZVal(frame));
max_size += ca[1]->MaxSize(aux->elems[1].ToDirectZVal(frame));
max_size += ca[2]->MaxSize(aux->elems[2].ToDirectZVal(frame));
max_size += ca[3]->MaxSize(aux->elems[3].ToDirectZVal(frame));
max_size += ca[4]->MaxSize(aux->elems[4].ToDirectZVal(frame));
max_size += ca[5]->MaxSize(aux->elems[5].ToDirectZVal(frame));
CatNMid()
ca[0]->RenderInto(aux->elems[0].ToDirectZVal(frame), res_p);
ca[1]->RenderInto(aux->elems[1].ToDirectZVal(frame), res_p);
ca[2]->RenderInto(aux->elems[2].ToDirectZVal(frame), res_p);
ca[3]->RenderInto(aux->elems[3].ToDirectZVal(frame), res_p);
ca[4]->RenderInto(aux->elems[4].ToDirectZVal(frame), res_p);
ca[5]->RenderInto(aux->elems[5].ToDirectZVal(frame), res_p);
CatNPost($$)
internal-op Cat7
class V
eval CatNPre()
size_t max_size = ca[0]->MaxSize(aux->elems[0].ToDirectZVal(frame));
max_size += ca[1]->MaxSize(aux->elems[1].ToDirectZVal(frame));
max_size += ca[2]->MaxSize(aux->elems[2].ToDirectZVal(frame));
max_size += ca[3]->MaxSize(aux->elems[3].ToDirectZVal(frame));
max_size += ca[4]->MaxSize(aux->elems[4].ToDirectZVal(frame));
max_size += ca[5]->MaxSize(aux->elems[5].ToDirectZVal(frame));
max_size += ca[6]->MaxSize(aux->elems[6].ToDirectZVal(frame));
CatNMid()
ca[0]->RenderInto(aux->elems[0].ToDirectZVal(frame), res_p);
ca[1]->RenderInto(aux->elems[1].ToDirectZVal(frame), res_p);
ca[2]->RenderInto(aux->elems[2].ToDirectZVal(frame), res_p);
ca[3]->RenderInto(aux->elems[3].ToDirectZVal(frame), res_p);
ca[4]->RenderInto(aux->elems[4].ToDirectZVal(frame), res_p);
ca[5]->RenderInto(aux->elems[5].ToDirectZVal(frame), res_p);
ca[6]->RenderInto(aux->elems[6].ToDirectZVal(frame), res_p);
CatNPost($$)
internal-op Cat8
class V
eval CatNPre()
size_t max_size = ca[0]->MaxSize(aux->elems[0].ToDirectZVal(frame));
max_size += ca[1]->MaxSize(aux->elems[1].ToDirectZVal(frame));
max_size += ca[2]->MaxSize(aux->elems[2].ToDirectZVal(frame));
max_size += ca[3]->MaxSize(aux->elems[3].ToDirectZVal(frame));
max_size += ca[4]->MaxSize(aux->elems[4].ToDirectZVal(frame));
max_size += ca[5]->MaxSize(aux->elems[5].ToDirectZVal(frame));
max_size += ca[6]->MaxSize(aux->elems[6].ToDirectZVal(frame));
max_size += ca[7]->MaxSize(aux->elems[7].ToDirectZVal(frame));
CatNMid()
ca[0]->RenderInto(aux->elems[0].ToDirectZVal(frame), res_p);
ca[1]->RenderInto(aux->elems[1].ToDirectZVal(frame), res_p);
ca[2]->RenderInto(aux->elems[2].ToDirectZVal(frame), res_p);
ca[3]->RenderInto(aux->elems[3].ToDirectZVal(frame), res_p);
ca[4]->RenderInto(aux->elems[4].ToDirectZVal(frame), res_p);
ca[5]->RenderInto(aux->elems[5].ToDirectZVal(frame), res_p);
ca[6]->RenderInto(aux->elems[6].ToDirectZVal(frame), res_p);
ca[7]->RenderInto(aux->elems[7].ToDirectZVal(frame), res_p);
CatNPost($$)
internal-op Analyzer-Name
classes VV VC
op-types S X
eval auto atype = $1.ToVal(Z_TYPE);
auto val = atype->AsEnumVal();
Unref($$);
plugin::Component* component = zeek::analyzer_mgr->Lookup(val);
if ( ! component )
component = zeek::packet_mgr->Lookup(val);
if ( ! component )
component = zeek::file_mgr->Lookup(val);
if ( component )
$$ = new StringVal(component->CanonicalName());
else
$$ = new StringVal("<error>");
macro FilesAddOrRemoveAnalyzer(file_id_val, tag, args_val, METHOD)
auto file_id = file_id_val;
using zeek::BifType::Record::Files::AnalyzerArgs;
auto rv = args_val->CoerceTo(AnalyzerArgs);
bool result = zeek::file_mgr->METHOD(
file_id->CheckString(),
zeek::file_mgr->GetComponentTag(tag.ToVal(Z_TYPE).get()),
std::move(rv));
macro FilesAddAnalyzer(file_id_val, tag, args_val)
FilesAddOrRemoveAnalyzer(file_id_val, tag, args_val, AddAnalyzer)
internal-op Files-Add-Analyzer
op1-read
classes VVV VCV
op-types S X R
eval FilesAddAnalyzer($1, $2, $3)
internal-op Files-Add-Analyzer
class VVVV
side-effects OP_FILES_ADD_ANALYZER_VVV OP_VVV
op-types I S X R
eval FilesAddAnalyzer($1, $2, $3)
$$ = result;
internal-op Files-Add-Analyzer
class VVCV
op-types I S X R
side-effects OP_FILES_ADD_ANALYZER_VCV OP_VVC
eval FilesAddAnalyzer($1, $2, $3)
$$ = result;
macro FilesRemoveAnalyzer(file_id_val, tag, args_slot)
FilesAddOrRemoveAnalyzer(file_id_val, tag, args_slot, RemoveAnalyzer)
internal-op Files-Remove-Analyzer
op1-read
classes VVV VCV
op-types S X R
eval FilesRemoveAnalyzer($1, $2, $3)
internal-op Files-Remove-Analyzer
class VVVV
op-types I S X R
side-effects OP_FILES_REMOVE_ANALYZER_VVV OP_VVV
eval FilesRemoveAnalyzer($1, $2, $3)
$$ = result;
internal-op Files-Remove-Analyzer
class VVCV
op-types I S X R
side-effects OP_FILES_REMOVE_ANALYZER_VCV OP_VVC
eval FilesRemoveAnalyzer($1, $2, $3)
$$ = result;
internal-op Analyzer-Enabled
classes VV VC
op-types I X
eval auto atype = $1.ToVal(Z_TYPE);
auto c = zeek::file_mgr->Lookup(atype->AsEnumVal());
$$ = c && c->Enabled();
internal-op File-Analyzer-Name
classes VV VC
eval auto atype = $1.ToVal(Z_TYPE);
Unref($$.AsString());
$$ = ZVal(file_mgr->GetComponentNameVal({NewRef{}, atype->AsEnumVal()}));
internal-op Is-Protocol-Analyzer
classes VV VC
op-types I X
eval auto atype = $1.ToVal(Z_TYPE);
$$ = analyzer_mgr->Lookup(atype->AsEnumVal()) != nullptr;
internal-op Clear-Table
op1-read
class V
op-types T
eval $1->RemoveAll();
internal-op Files-Enable-Reassembly
op1-read
class V
op-types S
eval auto f = $1->CheckString();
file_mgr->EnableReassembly(f);
internal-op Files-Set-Reassembly-Buffer
op1-read
classes VV Vi
op-types S U
eval auto f = $1->CheckString();
file_mgr->SetReassemblyBuffer(f, $2);
internal-op Files-Set-Reassembly-Buffer
class VVV
op-types I S U
side-effects OP_FILES_SET_REASSEMBLY_BUFFER_VV OP_VV
eval auto f = $1->CheckString();
$$ = file_mgr->SetReassemblyBuffer(f, $2);
internal-op Files-Set-Reassembly-Buffer
class VVi
op-types I S U
side-effects OP_FILES_SET_REASSEMBLY_BUFFER_Vi OP_VV_I2
eval auto f = $1->CheckString();
$$ = file_mgr->SetReassemblyBuffer(f, $2);
internal-op Get-Bytes-Thresh
classes VVV VVC
op-types U R I
eval auto a = analyzer::conn_size::GetConnsizeAnalyzer($1);
auto res = 0U;
if ( a )
res = static_cast<analyzer::conn_size::ConnSize_Analyzer*>(a)->GetByteAndPacketThreshold(true, $2);
$$ = res;
macro SetBytesThresh(cid, threshold, is_orig)
bool res = false;
auto a = analyzer::conn_size::GetConnsizeAnalyzer(cid);
if ( a )
{
static_cast<analyzer::conn_size::ConnSize_Analyzer*>(a)->SetByteAndPacketThreshold(threshold, true, is_orig);
res = true;
}
internal-op Set-Bytes-Thresh
op1-read
classes VVV VVC VCV VCi
op-types R U I
eval SetBytesThresh($1, $2, $3)
internal-op Set-Bytes-Thresh
class VVVV
op-types I R U I
side-effects OP_SET_BYTES_THRESH_VVV OP_VVV
eval SetBytesThresh($1, $2, $3)
$$ = res;
internal-op Set-Bytes-Thresh
class VVVC
op-types I R U I
side-effects OP_SET_BYTES_THRESH_VVC OP_VVC
eval SetBytesThresh($1, $2, $3)
$$ = res;
internal-op Set-Bytes-Thresh
class VVCV
op-types I R U I
side-effects OP_SET_BYTES_THRESH_VCV OP_VVC
eval SetBytesThresh($1, $2, $3)
$$ = res;
internal-op Set-Bytes-Thresh
class VVCi
op-types I R U I
side-effects OP_SET_BYTES_THRESH_VCi OP_VVC_I2
eval SetBytesThresh($1, $2, $3)
$$ = res;

View file

@ -0,0 +1,89 @@
# Operations corresponding to assigning to elements of aggregates.
macro VectorElemAssignPre(vec, index)
auto ind = index.AsCount();
auto vv = vec.AsVector();
macro EvalVectorElemAssign(vec, index, val_setup, assign_op)
VectorElemAssignPre(vec, index)
val_setup
if ( ! assign_op )
ERROR("value used but not set");
op Vector-Elem-Assign
op1-read
set-type $1
class VVV
eval EvalVectorElemAssign($1, $2,, copy_vec_elem(vv, ind, $3, Z_TYPE))
op Any-Vector-Elem-Assign
op1-read
set-type $1
classes VVV VVC
eval EvalVectorElemAssign($1, $2,, vv->Assign(ind, $3.ToVal(Z_TYPE)))
op Vector-Elem-Assign-Any
op1-read
class VVV
op-types X X a
eval EvalVectorElemAssign($1, $2, auto any_v = $3;, vv->Assign(ind, {NewRef{}, any_v}))
op Vector-Elem-Assign
op1-read
set-type $2
class VVC
eval VectorElemAssignPre($1, $2)
(void) copy_vec_elem(vv, ind, $3, Z_TYPE);
# These versions are used when the constant is the index, not the new value.
op Vector-Elem-Assign
op1-read
set-type $1
class VVi
op-types V X U
eval auto vv = $1;
if ( ! copy_vec_elem(vv, $3, $2, Z_TYPE) )
ERROR("value used but not set");
op Any-Vector-Elem-Assign
op1-read
set-type $1
class VVi
op-types V X I
eval auto vv = $1;
if ( ! vv->Assign($3, $2.ToVal(Z_TYPE)) )
ERROR("value used but not set");
op Vector-Elem-Assign-Any
op1-read
class VVi
op-types V a I
eval auto vv = $1;
auto any_v = $2;
vv->Assign($3, {NewRef{}, any_v});
internal-op Vector-Slice-Assign
op1-read
class VV
op-types V V
eval ValPtr vec = {NewRef{}, $1};
auto indices = Z_AUX->ToListVal(frame);
ValPtr vals = {NewRef{}, $2};
bool iterators_invalidated;
auto error = assign_to_index(std::move(vec), std::move(indices), std::move(vals), iterators_invalidated);
if ( error )
ERROR(error);
if ( iterators_invalidated )
WARN("possible loop/iterator invalidation");
internal-op Table-Elem-Assign
op1-read
classes VV VC
op-types T X
eval auto indices = Z_AUX->ToListVal(frame);
auto val = $2.ToVal(Z_TYPE);
bool iterators_invalidated = false;
$1->Assign(std::move(indices), std::move(val), true, &iterators_invalidated);
if ( iterators_invalidated )
WARN("possible loop/iterator invalidation");

View file

@ -0,0 +1,104 @@
# Operations corresponding to binary expressions.
binary-expr-op Add
op-type I U D S
vector
eval $1 + $2
eval-type S vector<const String*> strings;
strings.push_back($1->AsString());
strings.push_back($2->AsString());
auto res = new StringVal(concatenate(strings));
$$ = res;
binary-expr-op Sub
op-type I U D T
vector
eval $1 - $2
#
eval-type T auto v = $1->Clone();
auto s = v.release()->AsTableVal();
$2->RemoveFrom(s);
$$ = s;
binary-expr-op Times
op-type I U D
vector
eval $1 * $2
binary-expr-op Divide
op-type I U D
vector
#
precheck $2 == 0
precheck-action ERROR("division by zero");
eval $1 / $2
binary-expr-op Mask
# Signal that this expression only has mixed-type evaluation.
op-type X
explicit-result-type
eval-mixed A I auto mask = static_cast<uint32_t>($2);
auto a = $1->AsAddr();
if ( a.GetFamily() == IPv4 && mask > 32 )
ERROR(util::fmt("bad IPv4 subnet prefix length: %" PRIu32, mask));
if ( a.GetFamily() == IPv6 && mask > 128 )
ERROR(util::fmt("bad IPv6 subnet prefix length: %" PRIu32, mask));
auto v = make_intrusive<SubNetVal>(a, mask);
Unref($$.AsSubNet());
$$.AsSubNetRef() = v.release();
binary-expr-op Mod
op-type I U
vector
precheck $2 == 0
precheck-action ERROR("modulo by zero");
eval $1 % $2
binary-expr-op And-And
op-type I
vector
eval zeek_int_t($1 && $2)
binary-expr-op Or-Or
op-type I
vector
eval zeek_int_t($1 || $2)
binary-expr-op And
op-type U P T
vector
eval $1 & $2
#
eval-type P $$ = new PatternVal(RE_Matcher_conjunction($1->AsPattern(), $2->AsPattern()));
#
eval-type T $$ = $1->Intersection(*$2).release();
binary-expr-op Or
op-type U P T
vector
eval $1 | $2
#
eval-type P $$ = new PatternVal(RE_Matcher_disjunction($1->AsPattern(), $2->AsPattern()));
#
eval-type T auto v = $1->Clone();
auto s = v.release()->AsTableVal();
(void) $2->AddTo(s, false, false);
$$ = s;
binary-expr-op Xor
op-type U
vector
eval $1 ^ $2
binary-expr-op Lshift
op-type I U
vector
eval-type I if ( $1 < 0 )
ERROR("left shifting a negative number is undefined");
$$ = $1 << $2;
eval $1 << $2
binary-expr-op Rshift
op-type I U
vector
eval $1 >> $2

View file

@ -0,0 +1,180 @@
# Operations corresponding to function calls.
# A call with no arguments and no return value.
internal-op Call0
op1-read
class X
side-effects
num-call-args 0
# A call with no arguments and a return value.
internal-assignment-op Call0
class V
side-effects OP_CALL0_X OP_X
assign-val v
num-call-args 0
# Calls with 1 argument and no return value.
internal-op Call1
op1-read
classes V C
side-effects
num-call-args 1
# Same but with a return value.
internal-assignment-op Call1
class VV
side-effects OP_CALL1_V OP_V
assign-val v
num-call-args 1
internal-assignment-op Call1
class VC
side-effects OP_CALL1_C OP_C
assign-val v
num-call-args 1
# Calls with 2-5 arguments and no return value.
internal-op Call2
class X
side-effects
num-call-args 2
# Same with a return value.
internal-assignment-op Call2
class V
side-effects OP_CALL2_X OP_X
assign-val v
num-call-args 2
internal-op Call3
class X
side-effects
num-call-args 3
# Same with a return value.
internal-assignment-op Call3
class V
side-effects OP_CALL3_X OP_X
assign-val v
num-call-args 3
internal-op Call4
class X
side-effects
num-call-args 4
# Same with a return value.
internal-assignment-op Call4
class V
side-effects OP_CALL4_X OP_X
assign-val v
num-call-args 4
internal-op Call5
class X
side-effects
num-call-args 5
# Same with a return value.
internal-assignment-op Call5
class V
side-effects OP_CALL5_X OP_X
assign-val v
num-call-args 5
# ... and with an arbitrary number of arguments.
internal-op CallN
class X
side-effects
num-call-args n
# Same with a return value.
internal-assignment-op CallN
class V
side-effects OP_CALLN_X OP_X
assign-val v
num-call-args n
# Same, but for indirect calls via a global variable.
internal-op IndCallN
class X
side-effects
indirect-call
num-call-args n
# Same with a return value.
internal-assignment-op IndCallN
class V
side-effects OP_INDCALLN_X OP_X
assign-val v
indirect-call
num-call-args n
# And versions with a local variable rather than a global.
internal-op Local-IndCallN
op1-read
class V
side-effects
indirect-local-call
num-call-args n
internal-assignment-op Local-IndCallN
class VV
side-effects OP_LOCAL_INDCALLN_V OP_V
assign-val v
indirect-local-call
num-call-args n
# A call made in a "when" context. These always have assignment targets.
# To keep things simple, we just use one generic flavor (for N arguments,
# doing a less-streamlined-but-simpler Val-based assignment).
macro WhenCall(lhs, func)
if ( ! func )
throw ZAMDelayedCallException();
auto trigger = Z_FRAME->GetTrigger();
Val* v = trigger ? trigger->Lookup(Z_AUX->call_expr.get()) : nullptr;
ValPtr vp;
if ( v )
vp = {NewRef{}, v};
else
{
auto aux = Z_AUX;
auto current_assoc = Z_FRAME->GetTriggerAssoc();
auto n = aux->n;
std::vector<ValPtr> args;
for ( auto i = 0; i < n; ++i )
args.push_back(aux->ToVal(frame, i));
Z_FRAME->SetCall(Z_AUX->call_expr.get());
/* It's possible that this function will call another that
* itself returns null because *it* is the actual blocker.
* That will set ZAM_error, which we need to ignore.
*/
auto hold_ZAM_error = ZAM_error;
vp = func->Invoke(&args, Z_FRAME);
ZAM_error = hold_ZAM_error;
Z_FRAME->SetTriggerAssoc(current_assoc);
if ( ! vp )
throw ZAMDelayedCallException();
}
if ( Z_IS_MANAGED )
ZVal::DeleteManagedType(lhs);
lhs = ZVal(vp, Z_TYPE);
internal-op WhenCallN
class V
side-effects
eval WhenCall($$, Z_AUX_FUNC)
internal-op WhenIndCallN
class VV
op-types X F
side-effects
eval WhenCall($$, $1)
# Form for when we need to look up the function value at run-time.
internal-op When-ID-IndCallN
class V
side-effects
eval WhenCall($$, Z_AUX_ID->GetVal()->AsFunc())

View file

@ -0,0 +1,151 @@
# Operations corresponding to type coercions.
direct-unary-op Arith-Coerce ArithCoerce
internal-op Coerce-UI
class VV
op-types U I
eval auto v = $1;
if ( v < 0 )
ERROR("underflow converting int to count");
else
$$ = zeek_uint_t(v);
internal-op Coerce-UD
class VV
op-types U D
eval auto v = $1;
if ( v < 0.0 )
ERROR("underflow converting double to count");
else if ( v > static_cast<double>(UINT64_MAX) )
ERROR("overflow converting double to count");
else
$$ = zeek_uint_t(v);
internal-op Coerce-IU
class VV
op-types I U
eval auto v = $1;
if ( v > INT64_MAX )
ERROR("overflow converting count to int");
else
$$ = zeek_int_t(v);
internal-op Coerce-ID
class VV
op-types I D
eval auto v = $1;
if ( v < static_cast<double>(INT64_MIN) )
ERROR("underflow converting double to int");
else if ( v > static_cast<double>(INT64_MAX) )
ERROR("overflow converting double to int");
else
$$ = zeek_int_t(v);
internal-op Coerce-DI
class VV
op-types D I
eval $$ = double($1);
internal-op Coerce-DU
class VV
op-types D U
eval $$ = double($1);
macro EvalCoerceVec(lhs, rhs, coercer)
auto old_v1 = lhs.AsVector();
lhs.AsVectorRef() = coercer(rhs.AsVector(), Z_LOC);
Unref(old_v1); /* delayed to allow for same value on both sides */
internal-op Coerce-UI-Vec
class VV
eval EvalCoerceVec($$, $1, vec_coerce_UI)
internal-op Coerce-UD-Vec
class VV
eval EvalCoerceVec($$, $1, vec_coerce_UD)
internal-op Coerce-IU-Vec
class VV
eval EvalCoerceVec($$, $1, vec_coerce_IU)
internal-op Coerce-ID-Vec
class VV
eval EvalCoerceVec($$, $1, vec_coerce_ID)
internal-op Coerce-DI-Vec
class VV
eval EvalCoerceVec($$, $1, vec_coerce_DI)
internal-op Coerce-DU-Vec
class VV
eval EvalCoerceVec($$, $1, vec_coerce_DU)
direct-unary-op Record-Coerce RecordCoerce
internal-op Record-Coerce
class VV
op-types R R
eval auto rt = cast_intrusive<RecordType>(Z_TYPE);
auto v = $1;
auto to_r = coerce_to_record(std::move(rt), v, Z_AUX_MAP);
Unref($$);
$$ = to_r.release();
direct-unary-op Table-Coerce TableCoerce
internal-op Table-Coerce
class VV
op-types T T
eval auto tv = $1;
if ( tv->Size() > 0 )
ERROR("coercion of non-empty table/set");
else
{
auto tt = cast_intrusive<TableType>(Z_TYPE);
AttributesPtr attrs = tv->GetAttrs();
auto t = make_intrusive<TableVal>(tt, attrs);
Unref($$);
$$ = t.release();
}
direct-unary-op Vector-Coerce VectorCoerce
internal-op Vector-Coerce
class VV
op-types V V
eval if ( $1->Size() > 0 )
ERROR("coercion of non-empty vector");
else
{
auto vv = new VectorVal(cast_intrusive<VectorType>(Z_TYPE));
Unref($$);
$$ = vv;
}
unary-expr-op To-Any-Coerce
op-type X
set-type $1
eval AssignTarget($$, ZVal($1.ToVal(Z_TYPE), ZAM::any_base_type))
unary-expr-op From-Any-Coerce
no-const
op-type X
set-type $$
eval auto v = $1.AsAny();
AssignTarget($$, ZVal({NewRef{}, v}, Z_TYPE))
unary-expr-op From-Any-Vec-Coerce
no-const
op-type X
set-type $$
eval auto vv = $1.AsVector();
if ( ! vv->Concretize(Z_TYPE->Yield()) )
ERROR("incompatible vector-of-any");
else
{
zeek::Ref(vv);
AssignTarget($$, ZVal(vv))
}

View file

@ -0,0 +1,251 @@
# Operations corresponding to aggregated constructors.
# Table construction requires atypical evaluation of list elements
# using information from their expression specifics.
direct-unary-op Table-Constructor ConstructTable
macro ConstructTableOrSetPre(width)
auto tt = cast_intrusive<TableType>(Z_TYPE);
auto new_t = new TableVal(tt, Z_AUX_ATTRS);
auto aux = Z_AUX;
auto n = aux->n;
auto ind_width = width;
macro ConstructTableOrSetPost(lhs)
auto& t = lhs.AsTableRef();
Unref(t);
t = new_t;
internal-op Construct-Table
class Vi
eval ConstructTableOrSetPre($1)
for ( auto i = 0; i < n; ++i )
{
auto indices = aux->ToIndices(frame, i, ind_width);
auto v = aux->ToVal(frame, i + ind_width);
new_t->Assign(indices, v);
i += ind_width;
}
ConstructTableOrSetPost($$)
# When tables are constructed, if their &default is a lambda with captures
# then we need to explicitly set up the default.
internal-op Set-Table-Default-Lambda
op1-read
class VV
op-types T X
eval auto tbl = $1;
auto lambda = $2.ToVal(Z_TYPE);
tbl->InitDefaultVal(std::move(lambda));
direct-unary-op Set-Constructor ConstructSet
internal-op Construct-Set
class Vi
eval ConstructTableOrSetPre($1)
for ( auto i = 0; i < n; i += ind_width )
{
auto indices = aux->ToIndices(frame, i, ind_width);
new_t->Assign(indices, nullptr);
}
ConstructTableOrSetPost($$)
direct-unary-op Record-Constructor ConstructRecord
direct-unary-op Rec-Construct-With-Rec ConstructRecordFromRecord
macro ConstructRecordPost(lhs)
auto& r = lhs.AsRecordRef();
Unref(r);
r = new RecordVal(cast_intrusive<RecordType>(Z_TYPE), std::move(init_vals));
op Construct-Direct-Record
class V
eval auto init_vals = Z_AUX->ToZValVec(frame);
ConstructRecordPost($$)
op Construct-Known-Record
class V
eval auto init_vals = Z_AUX->ToZValVecWithMap(frame);
ConstructRecordPost($$)
macro AssignFromRec(lhs_full, rhs_full)
/* The following is defined below, for use by Rec-Assign-Fields */
SetUpRecFieldOps(lhs_full, rhs_full, lhs_map)
auto is_managed = Z_AUX->is_managed;
for ( size_t i = 0U; i < n; ++i )
{
auto rhs_i = rhs->RawField(rhs_map[i]);
if ( is_managed[i] )
zeek::Ref(rhs_i.ManagedVal());
init_vals[lhs_map[i]] = rhs_i;
}
op Construct-Known-Record-From
class VV
eval auto init_vals = Z_AUX->ToZValVecWithMap(frame);
AssignFromRec($$, $1)
ConstructRecordPost($$)
macro DoNetworkTimeInit(slot)
init_vals[slot] = ZVal(run_state::network_time);
op Construct-Known-Record-With-NT
class Vi
eval auto init_vals = Z_AUX->ToZValVecWithMap(frame);
DoNetworkTimeInit($1)
ConstructRecordPost($$)
op Construct-Known-Record-With-NT-From
class VVi
eval auto init_vals = Z_AUX->ToZValVecWithMap(frame);
DoNetworkTimeInit($2)
AssignFromRec($$, $1)
ConstructRecordPost($$)
macro GenInits()
auto init_vals = Z_AUX->ToZValVecWithMap(frame);
for ( auto& fi : *z.aux->field_inits )
init_vals[fi.first] = fi.second->Generate();
op Construct-Known-Record-With-Inits
class V
eval GenInits()
ConstructRecordPost($$)
op Construct-Known-Record-With-Inits-From
class VV
eval GenInits()
AssignFromRec($$, $1)
ConstructRecordPost($$)
op Construct-Known-Record-With-Inits-And-NT
class Vi
eval GenInits()
DoNetworkTimeInit($1)
ConstructRecordPost($$)
op Construct-Known-Record-With-Inits-And-NT-From
class VVi
eval GenInits()
DoNetworkTimeInit($2)
AssignFromRec($$, $1)
ConstructRecordPost($$)
macro SetUpRecFieldOps(lhs_full, rhs_full, which_lhs_map)
auto lhs = lhs_full.record_val;
auto rhs = rhs_full.record_val;
auto& lhs_map = Z_AUX->which_lhs_map;
auto& rhs_map = Z_AUX->rhs_map;
auto n = rhs_map.size();
op Rec-Assign-Fields
op1-read
class VV
eval SetUpRecFieldOps($1, $2, map)
for ( size_t i = 0U; i < n; ++i )
lhs->RawOptField(lhs_map[i]) = rhs->RawField(rhs_map[i]);
macro DoManagedRecAssign()
auto is_managed = Z_AUX->is_managed;
for ( size_t i = 0U; i < n; ++i )
if ( is_managed[i] )
{
auto& lhs_i = lhs->RawOptField(lhs_map[i]);
auto rhs_i = rhs->RawField(rhs_map[i]);
zeek::Ref(rhs_i.ManagedVal());
if ( lhs_i )
ZVal::DeleteManagedType(*lhs_i);
lhs_i = rhs_i;
}
else
lhs->RawOptField(lhs_map[i]) = rhs->RawField(rhs_map[i]);
op Rec-Assign-Fields-Managed
op1-read
class VV
eval SetUpRecFieldOps($1, $2, map)
DoManagedRecAssign()
op Rec-Assign-Fields-All-Managed
op1-read
class VV
eval SetUpRecFieldOps($1, $2, map)
for ( size_t i = 0U; i < n; ++i )
{
auto& lhs_i = lhs->RawOptField(lhs_map[i]);
auto rhs_i = rhs->RawField(rhs_map[i]);
zeek::Ref(rhs_i.ManagedVal());
if ( lhs_i )
ZVal::DeleteManagedType(*lhs_i);
lhs_i = rhs_i;
}
op Rec-Add-Int-Fields
op1-read
class VV
eval SetUpRecFieldOps($1, $2, map)
for ( size_t i = 0U; i < n; ++i )
lhs->RawField(lhs_map[i]).int_val += rhs->RawField(rhs_map[i]).int_val;
op Rec-Add-Double-Fields
op1-read
class VV
eval SetUpRecFieldOps($1, $2, map)
for ( size_t i = 0U; i < n; ++i )
lhs->RawField(lhs_map[i]).double_val += rhs->RawField(rhs_map[i]).double_val;
op Rec-Add-Fields
op1-read
class VV
eval SetUpRecFieldOps($1, $2, map)
auto& types = Z_AUX->types;
for ( size_t i = 0U; i < n; ++i )
{
auto& lhs_i = lhs->RawField(lhs_map[i]);
auto rhs_i = rhs->RawField(rhs_map[i]);
auto tag = types[i]->Tag();
if ( tag == TYPE_INT )
lhs_i.int_val += rhs_i.int_val;
else if ( tag == TYPE_COUNT )
lhs_i.uint_val += rhs_i.uint_val;
else
lhs_i.double_val += rhs_i.double_val;
}
# Special instruction for concretizing vectors that are fields in a
# newly-constructed record. "aux" holds which fields in the record to
# inspect.
op Concretize-Vector-Fields
op1-read
class V
op-types R
eval auto rt = cast_intrusive<RecordType>(Z_TYPE);
auto r = $1;
auto aux = Z_AUX;
auto n = aux->n;
for ( auto i = 0; i < n; ++i )
{
auto ind = aux->elems[i].IntVal();
auto v_i = r->GetField(ind);
ASSERT(v_i);
if ( v_i->GetType<VectorType>()->IsUnspecifiedVector() )
{
const auto& t_i = rt->GetFieldType(ind);
v_i->AsVectorVal()->Concretize(t_i->Yield());
}
}
direct-unary-op Vector-Constructor ConstructVector
internal-op Construct-Vector
class V
op-types V
eval auto new_vv = new VectorVal(cast_intrusive<VectorType>(Z_TYPE));
auto aux = Z_AUX;
auto n = aux->n;
for ( auto i = 0; i < n; ++i )
new_vv->Assign(i, aux->ToVal(frame, i));
auto& vv = $$;
Unref(vv);
vv = new_vv;

View file

@ -0,0 +1,212 @@
# Operations corresponding to indexing of tables, vectors, strings,
# and "any" values.
op IndexVecBoolSelect
classes VVV VCV
op-types V V V
set-type $$
eval if ( $1->Size() != $2->Size() )
ERROR("size mismatch, boolean index and vector");
else
{
auto vt = cast_intrusive<VectorType>(Z_TYPE);
auto v2 = $1;
auto v3 = $2;
auto v = vector_bool_select(std::move(vt), v2, v3);
Unref($$);
$$ = v.release();
}
op IndexVecIntSelect
classes VVV VCV
op-types V V V
set-type $$
eval auto vt = cast_intrusive<VectorType>(Z_TYPE);
auto v2 = $1;
auto v3 = $2;
auto v = vector_int_select(std::move(vt), v2, v3);
Unref($$);
$$ = v.release();
op Index
class VVL
custom-method return CompileIndex(n1, n2, l, false);
no-eval
op Index
class VCL
custom-method return CompileIndex(n, c, l, false);
no-eval
op WhenIndex
class VVL
custom-method return CompileIndex(n1, n2, l, true);
no-eval
op WhenIndex
class VCL
custom-method return CompileIndex(n, c, l, true);
no-eval
internal-op Index-Vec
class VVV
op-types X V I
eval EvalIndexVec($$, $1, $2)
macro EvalIndexVec(lhs, rhs_vec, index)
auto& vv = rhs_vec->RawVec();
zeek_int_t ind = index;
if ( ind < 0 )
ind += vv.size();
if ( ind < 0 || ind >= int(vv.size()) )
ERROR("no such index");
AssignTarget(lhs, CopyVal(*(vv[ind])))
internal-op Index-VecC
class VVi
op-types X V I
eval EvalIndexVec($$, $1, $2)
internal-op Index-Any-Vec
class VVV
op-types X V I
eval EvalIndexAnyVec($$, $1, $2)
macro EvalIndexAnyVec(lhs, vec, index)
auto vv = vec;
zeek_int_t ind = index;
if ( ind < 0 )
ind += vv->Size();
if ( ind < 0 || ind >= int(vv->Size()) )
ERROR("no such index");
AssignTarget(lhs, ZVal(vv->ValAt(ind).release()))
internal-op Index-Any-VecC
class VVi
op-types X V I
eval EvalIndexAnyVec($$, $1, $2)
macro WhenIndexResCheck(vec)
if ( vec && IndexExprWhen::evaluating > 0 )
IndexExprWhen::results.push_back({NewRef{}, vec});
internal-op When-Index-Vec
class VVV
op-types X V I
eval EvalIndexAnyVec($$, $1, $2)
WhenIndexResCheck($$.AsVector())
internal-op When-Index-VecC
class VVi
op-types X V I
eval EvalIndexAnyVec($$, $1, $2)
WhenIndexResCheck($$.AsVector())
macro EvalVecSlice(lhs, vv)
auto vec = vv;
auto v = index_slice(vec, indices.get());
Unref(lhs);
lhs = v.release();
internal-op Index-Vec-Slice
class VV
op-types V V
eval auto indices = Z_AUX->ToListVal(frame);
EvalVecSlice($$, $1)
internal-op When-Index-Vec-Slice
class VV
op-types V V
eval auto indices = Z_AUX->ToListVal(frame);
EvalVecSlice($$, $1)
WhenIndexResCheck($$)
internal-op Table-Index
class VV
eval auto indices = Z_AUX->ToListVal(frame);
EvalTableIndex($1, indices)
if ( v )
AssignTarget($$, BuildVal(v, Z_TYPE))
internal-op Table-PatStr-Index
classes VVV VVC
op-types X T S
eval auto vec = ZVal($1->LookupPattern({NewRef{}, $2}));
ZVal::DeleteManagedType($$);
$$ = vec;
internal-op When-Table-Index
class VV
eval auto indices = Z_AUX->ToListVal(frame);
EvalTableIndex($1, indices)
if ( v )
{
if ( IndexExprWhen::evaluating > 0 )
IndexExprWhen::results.emplace_back(v);
AssignTarget($$, BuildVal(v, Z_TYPE))
}
macro EvalTableIndex(tbl, index)
auto v = tbl.AsTable()->FindOrDefault(index);
if ( ! v )
ERROR("no such index");
internal-op When-PatStr-Index
class VV
op-types X T
eval auto indices = Z_AUX->ToListVal(frame);
auto arg0 = indices->Idx(0);
auto v = $1->LookupPattern({NewRef{}, arg0->AsStringVal()});
if ( IndexExprWhen::evaluating > 0 )
IndexExprWhen::results.emplace_back(v);
AssignTarget($$, BuildVal(v, Z_TYPE))
internal-assignment-op Table-Index1
classes VVV VVC
assign-val v
eval EvalTableIndex($1, $2.ToVal(Z_TYPE))
# No AssignTarget needed, as this is an assignment-op
# This version is for a variable v3.
internal-op Index-String
class VVV
op-types S S I
eval EvalIndexString($$, $1, $2)
macro EvalIndexString(lhs, s, index)
auto str = s->AsString();
auto len = str->Len();
auto idx = index;
if ( idx < 0 )
idx += len;
auto v = str->GetSubstring(idx, 1);
Unref(lhs);
lhs = new StringVal(v ? v : new String(""));
# This version is for a constant v3.
internal-op Index-StringC
class VVi
op-types S S I
eval EvalIndexString($$, $1, $2)
internal-op Index-String-Slice
class VV
op-types S S
eval auto str = $1->AsString();
auto indices = Z_AUX->ToListVal(frame);
auto slice = index_string(str, indices.get());
Unref($$);
$$ = new StringVal(slice->ToStdString());
op AnyIndex
class VVi
op-types X a I
set-type $$
eval auto lv = $1->AsListVal();
if ( $2 < 0 || $2 >= lv->Length() )
reporter->InternalError("bad \"any\" element index");
ValPtr elem = lv->Idx($2);
if ( CheckAnyType(elem->GetType(), Z_TYPE, Z_LOC) )
AssignTarget($$, BuildVal(elem, Z_TYPE))
else
ZAM_error = true;

View file

@ -0,0 +1,124 @@
# Internal operations not directly driven off of AST elements.
# These two are only needed for type-based switch statements. Could think
# about replacing them using CoerceFromAnyExpr.
op Assign-Any
classes VV VC
set-type $1
op-types a X
eval auto v = $1.ToVal(Z_TYPE);
$$ = v.release();
# Lazy way to assign without having to track the specific type of
# a constant.
internal-op Assign-Const
class VC
eval AssignTarget($$, BuildVal($1.ToVal(Z_TYPE), Z_TYPE))
internal-assignment-op Load-Val
class Vi
assign-val v
eval auto& v = Z_FRAME->GetElement($1);
internal-assignment-op Load-Global
# We don't use GlobalVal() for the assignment because we want to leverage
# the bookkeeping that assign-val gives us in terms of memory management.
class Vg
assign-val v
eval auto& v = GlobalID($1)->GetVal();
if ( ! v )
ERROR2("value used but not set", Z_AUX_ID.get());
# We need a special form here for loading global types, as they don't
# fit the usual template.
internal-op Load-Global-Type
class Vg
op-types t I
eval auto& v = $$;
Unref(v);
auto& t = GlobalID($1)->GetType();
v = new TypeVal(t, true);
internal-op Load-Capture
class Vi
eval $$ = Z_FRAME->GetFunction()->GetCapturesVec()[$1];
internal-op Load-Managed-Capture
class Vi
eval auto& lhs = $$;
auto& rhs = Z_FRAME->GetFunction()->GetCapturesVec()[$1];
zeek::Ref(rhs.ManagedVal());
ZVal::DeleteManagedType(lhs);
lhs = rhs;
internal-op Store-Global
op1-internal
class g
eval GlobalID($1)->SetVal(GlobalVal($1).ToVal(Z_TYPE));
# Both of these have the LHS as v2 not v1, to keep with existing
# conventions of OP_VV_I2 op type (as opposed to OP_VV_I1_V2, which doesn't
# currently exist, and would be a pain to add).
internal-op Store-Capture
op1-read
class Vi
eval Z_FRAME->GetFunction()->GetCapturesVec()[$2] = $1;
internal-op Store-Managed-Capture
op1-read
class Vi
eval auto& lhs = Z_FRAME->GetFunction()->GetCapturesVec()[$2];
auto& rhs = $1;
zeek::Ref(rhs.ManagedVal());
ZVal::DeleteManagedType(lhs);
lhs = rhs;
internal-op Copy-To
class VC
set-type $1
eval AssignTarget($$, CopyVal($1))
internal-op GoTo
class b
eval $1
internal-op Hook-Break
class X
eval flow = FLOW_BREAK;
pc = end_pc;
DO_ZAM_PROFILE
continue;
# Slot 2 gives frame size.
internal-op Lambda
class Vi
op-types F I
eval auto& primary_func = Z_AUX_PRIMARY_FUNC;
auto& body = primary_func->GetBodies()[0].stmts;
ASSERT(body->Tag() == STMT_ZAM);
auto lamb = make_intrusive<ScriptFunc>(Z_AUX_ID);
lamb->AddBody(body, $1);
lamb->SetName(Z_AUX_LAMBDA_NAME.c_str());
auto& aux = Z_AUX;
if ( aux->n > 0 )
{
auto captures = std::make_unique<std::vector<ZVal>>();
for ( auto i = 0; i < aux->n; ++i )
{
auto slot = aux->elems[i].Slot();
if ( slot >= 0 )
{
auto& cp = frame[slot];
if ( aux->elems[i].IsManaged() )
zeek::Ref(cp.ManagedVal());
captures->push_back(cp);
}
else
/* Used for when-locals. */
captures->push_back(ZVal());
}
lamb->CreateCaptures(std::move(captures));
}
Unref($$);
$$ = lamb.release();

View file

@ -0,0 +1,124 @@
# Operations corresponding to iterations.
internal-op Init-Table-Loop
op1-read
class Vf
op-types T I
eval $2.BeginLoop({NewRef{}, $1}, frame, Z_AUX);
internal-op Next-Table-Iter
op1-read
class fb
eval NextTableIterPre($1, $2)
$1.NextIter();
macro NextTableIterPre(iter, BRANCH)
if ( iter.IsDoneIterating() )
BRANCH
internal-op Next-Table-Iter-No-Vars
op1-read
class fb
eval NextTableIterPre($1, $2)
$1.IterFinished();
internal-op Next-Table-Iter-Val-Var
# v1 = slot of the "ValueVar"
class Vfb
eval NextTableIterPre($1, $2)
AssignTarget($$, $1.IterValue());
$1.NextIter();
internal-op Next-Table-Iter-Val-Var-No-Vars
# v1 = slot of the "ValueVar"
class Vfb
eval NextTableIterPre($1, $2)
AssignTarget($$, $1.IterValue());
$1.IterFinished();
internal-op Init-Vector-Loop
op1-read
class Vs
op-types V I
eval auto& vv = $1->RawVec();
$2.InitLoop(&vv);
macro NextVectorIterCore(info, BRANCH)
if ( info.IsDoneIterating() )
BRANCH
const auto& vv = *info.vv;
if ( ! vv[info.iter] )
{ /* Account for vector hole. Re-execute for next position. */
info.IterFinished();
REDO
}
internal-op Next-Vector-Iter
# v1 = iteration variable
class Vsb
op-types U I I
eval NextVectorIterCore($1, $2)
$$ = $1.iter;
$1.IterFinished();
internal-op Next-Vector-Blank-Iter
op1-internal
class sb
eval NextVectorIterCore($1, $2)
$1.IterFinished();
internal-op Next-Vector-Iter-Val-Var
# v1 = iteration variable
# v2 = value variable
op1-read-write
class VVsb
op-types U X I I
eval NextVectorIterCore($2, $3)
$$ = $2.iter;
if ( Z_IS_MANAGED )
$1 = BuildVal(vv[$2.iter]->ToVal(Z_TYPE), Z_TYPE);
else
$1 = *vv[$2.iter];
$2.IterFinished();
internal-op Next-Vector-Blank-Iter-Val-Var
# v1 = value variable
class Vsb
eval NextVectorIterCore($1, $2)
if ( Z_IS_MANAGED )
$$ = BuildVal(vv[$1.iter]->ToVal(Z_TYPE), Z_TYPE);
else
$$ = *vv[$1.iter];
$1.IterFinished();
internal-op Init-String-Loop
op1-read
classes Vs Cs
op-types S I
eval $2.InitLoop($1->AsString());
internal-op Next-String-Iter
# v1 = iteration variable
class Vsb
op-types S I I
eval if ( $1.IsDoneIterating() )
$2
auto bytes = (const char*) $1.s->Bytes() + $1.iter;
auto sv = new StringVal(1, bytes);
Unref($$);
$$ = sv;
$1.IterFinished();
internal-op Next-String-Blank-Iter
op1-internal
class sb
eval if ( $1.IsDoneIterating() )
$2
$1.IterFinished();
internal-op End-Table-Loop
op1-internal
class f
eval $1.Clear();

View file

@ -0,0 +1,74 @@
# General-purpose macros. Those that are specific to a group of instructions
# are defined with those templates rather than appearing here.
# Macros for information associated with the current instruction.
# The Val frame used to pass in arguments.
macro Z_FRAME f
# The main type.
macro Z_TYPE z.GetType()
# Whether it's managed.
macro Z_IS_MANAGED *(z.is_managed)
# Secondary type.
macro Z_TYPE2 z.GetType2()
# Auxiliary information.
macro Z_AUX z.aux
macro Z_AUX_ID z.aux->id_val
macro Z_AUX_FUNC z.aux->func
macro Z_AUX_MAP z.aux->map
macro Z_AUX_ATTRS z.aux->attrs
macro Z_AUX_WHEN_INFO z.aux->wi
macro Z_AUX_EVENT_HANDLER z.aux->event_handler
macro Z_AUX_PRIMARY_FUNC z.aux->lambda->PrimaryFunc()
macro Z_AUX_LAMBDA_NAME z.aux->lambda->Name()
# Location in the original script.
macro Z_LOC z.loc
macro SET_RET_TYPE(type) ret_type = type;
macro INDEX_LIST zam_index_val_list
macro ERROR(msg) ZAM_run_time_error(Z_LOC, msg)
macro ERROR2(msg, obj) ZAM_run_time_error(Z_LOC, msg, obj)
macro WARN(msg) ZAM_run_time_warning(Z_LOC, msg)
# The following abstracts the process of creating a frame-assignable value.
macro BuildVal(v, t) ZVal(v, t)
# Returns a memory-managed-if-necessary copy of an existing value.
macro CopyVal(v) (Z_IS_MANAGED ? BuildVal((v).ToVal(Z_TYPE), Z_TYPE) : (v))
# Managed assignments to the given target.
macro AssignTarget(target, v) {
if ( Z_IS_MANAGED )
{
/* It's important to hold a reference to v here prior
to the deletion in case target points to v. */
auto v2 = v;
ZVal::DeleteManagedType(target);
target = v2;
}
else
target = v;
}
macro Branch(target) { DO_ZAM_PROFILE; pc = target; continue; }
macro REDO { --pc; /* so we then increment to here again */ break; }
macro GlobalID(g) globals[g].id
macro GlobalVal(g) frame[globals[g].slot]
macro StepIter(slot) step_iters[slot]
macro TableIter(slot) (*tiv_ptr)[slot]
macro DirectField(r, f) r->RawField(f)
macro DirectOptField(r, f) r->RawOptField(f)
macro LogEnum(v) v.ToVal(ZAM::log_ID_enum_type)

View file

@ -0,0 +1,267 @@
# Operations corresponding to non-uniform expressions.
assign-op Field
class R
field-op
assign-val v
eval auto r = $1.AsRecord();
auto& rv = DirectOptField(r, $2);
ZVal v;
if ( ! rv )
{
auto def = r->GetType<RecordType>()->FieldDefault($2);
if ( def )
v = ZVal(def, Z_TYPE);
else
ERROR(util::fmt("field value missing: $%s", r->GetType()->AsRecordType()->FieldName($2)));
}
else
v = *rv;
expr-op Has-Field
class VRi
includes-field-op
no-eval
internal-op Has-Field
class VRi
op-types I R I
eval $$ = $1->HasField($2);
internal-op Has-Field
class VRii
op-types R R I I
eval DirectOptField($$, $2) = ZVal(zeek_int_t($1->HasField($3)));
# The following generates an assignment version of Has-Field that we
# don't use (because we need the one above that uses "includes-field-op")
# but lets us compress the two conditionals.
predicate-op Has-Field
class Vi
op-types R I
eval $1->HasField($2)
predicate-op Table-Has-Elements
class V
op-types T
eval $1->Size() > 0
predicate-op Vector-Has-Elements
class V
op-types V
eval $1->Size() > 0
expr-op In
class VVV
custom-method return CompileInExpr(n1, n2, n3);
no-eval
expr-op In
class VCV
custom-method return CompileInExpr(n1, c, n2);
no-eval
expr-op In
class VVC
custom-method return CompileInExpr(n1, n2, c);
no-eval
internal-op P-In-S
classes VVV VCV VVC
op-types I P S
eval $$ = $1->MatchAnywhere($2->AsString()) != 0;
internal-op Str-In-Pat-Tbl
classes VVV VCV
op-types I S T
eval $$ = $2->MatchPattern({NewRef{}, $1});
internal-op S-In-S
classes VVV VCV VVC
op-types I S S
eval auto sc = reinterpret_cast<const unsigned char*>($1->CheckString());
auto cmp = util::strstr_n($2->Len(), $2->Bytes(), $1->Len(), sc);
$$ = cmp != -1;
internal-op A-In-S
classes VVV VCV VVC
op-types I A N
eval $$ = $2->Contains($1->AsAddr());
# Handled differently because of the unusual middle argument.
op L-In-T
class VLV
custom-method return CompileInExpr(n1, l, n2);
no-eval
op L-In-T
class VLC
custom-method return CompileInExpr(n, l, c);
no-eval
op L-In-Vec
class VLV
custom-method return CompileInExpr(n1, l, n2);
no-eval
op L-In-Vec
class VLC
custom-method return CompileInExpr(n, l, c);
no-eval
predicate-op Val-Is-In-Table
class VV
op-types X T
eval $2->Find($1.ToVal(Z_TYPE)) != nullptr
# Variants for indexing two values, one of which might be a constant.
# We set the instructions's *second* type to be that of the first variable
# index. We get the type of the second variable (if any) by digging it
# out of the table's type. For a constant in either position, we use
# the main instruction type, as always.
macro EvalVal2InTableCore(op1, op2)
INDEX_LIST->Clear();
INDEX_LIST->Append(op1);
INDEX_LIST->Append(op2);
macro EvalVal2InTableAssignCore(lhs, tbl)
lhs.AsIntRef() = tbl.AsTable()->Find(INDEX_LIST) != nullptr;
macro EvalVal2InTablePre(op1, op2, tbl)
auto& tt_ind = tbl.AsTable()->GetType()->AsTableType()->GetIndexTypes();
EvalVal2InTableCore(op1.ToVal(Z_TYPE2), op2.ToVal(tt_ind[1]))
internal-op Val2-Is-In-Table
class VVVV
eval EvalVal2InTablePre($1,$2,$3)
EvalVal2InTableAssignCore($$, $3)
internal-op Val2-Is-In-Table-Cond
op1-read
class VVVb
eval EvalVal2InTablePre($1,$2,$3)
EvalVal2InTableCond($3, INDEX_LIST, $4, !)
macro EvalVal2InTableCond(tbl, op, BRANCH, negate)
if ( negate tbl.AsTable()->Find(op) )
BRANCH
internal-op Val2-Is-Not-In-Table-Cond
op1-read
class VVVb
eval EvalVal2InTablePre($1,$2,$3)
EvalVal2InTableCond($3, INDEX_LIST, $4,)
internal-op Val2-Is-In-Table
class VVVC
eval EvalVal2InTableCore($1.ToVal(Z_TYPE2), $3.ToVal(Z_TYPE))
EvalVal2InTableAssignCore($$, $2)
internal-op Val2-Is-In-Table
class VVCV
eval EvalVal2InTableCore($2.ToVal(Z_TYPE), $1.ToVal(Z_TYPE2))
EvalVal2InTableAssignCore($$, $3)
internal-op Val2-Is-In-Table-Cond
op1-read
class VVbC
eval EvalVal2InTableCore($1.ToVal(Z_TYPE2), $4.ToVal(Z_TYPE))
EvalVal2InTableCond($2, INDEX_LIST, $3, !)
internal-op Val2-Is-In-Table-Cond
op1-read
class VVCb
eval EvalVal2InTableCore($3.ToVal(Z_TYPE), $1.ToVal(Z_TYPE2))
EvalVal2InTableCond($2, INDEX_LIST, $4, !)
internal-op Val2-Is-Not-In-Table-Cond
op1-read
class VVbC
eval EvalVal2InTableCore($1.ToVal(Z_TYPE2), $4.ToVal(Z_TYPE))
EvalVal2InTableCond($2, INDEX_LIST, $3, )
internal-op Val2-Is-Not-In-Table-Cond
op1-read
class VVCb
eval EvalVal2InTableCore($3.ToVal(Z_TYPE), $1.ToVal(Z_TYPE2))
EvalVal2InTableCond($2, INDEX_LIST, $4, )
predicate-op Const-Is-In-Table
class VC
op-types T X
eval $1->Find($2.ToVal(Z_TYPE)) != nullptr
internal-op List-Is-In-Table
classes VV VC
op-types I T
eval auto indices = Z_AUX->ToListVal(frame);
$$ = $1->Find(std::move(indices)) != nullptr;
internal-op Val-Is-In-Vector
class VVV
op-types I I V
eval auto vec = $2;
auto ind = $1;
$$ = vec->Has(ind);
internal-op Const-Is-In-Vector
class VCV
op-types I I V
eval auto vec = $2;
auto ind = $1;
$$ = vec->Has(ind);
expr-op Cond
class VVVV
op-types X I X X
set-type $2
eval AssignTarget($$, $1 ? CopyVal($2) : CopyVal($3))
expr-op Cond
class VVVC
op-types X I X X
set-type $2
eval AssignTarget($$, $1 ? CopyVal($2) : CopyVal($3))
expr-op Cond
class VVCV
op-types X I X X
set-type $2
eval AssignTarget($$, $1 ? CopyVal($2) : CopyVal($3))
op Bool-Vec-Cond
class VVVV
op-types V V V V
set-type $2
eval auto& vsel = $1->RawVec();
auto& v1 = $2->RawVec();
auto& v2 = $3->RawVec();
auto n = v1.size();
auto res = new vector<std::optional<ZVal>>(n);
for ( auto i = 0U; i < n; ++i )
if ( vsel[i] )
(*res)[i] = vsel[i]->AsInt() ? v1[i] : v2[i];
auto& full_res = $$;
Unref(full_res);
full_res = new VectorVal(cast_intrusive<VectorType>(Z_TYPE), res);
# Our instruction format doesn't accommodate two constants, so for
# the singular case of a V ? C1 : C2 conditional, we split it into
# two operations, V ? C1 and !V ? C2.
op CondC1
class VVC
op-types X I X
set-type $$
eval if ( $1 )
AssignTarget($$, CopyVal($2))
op CondC2
class VVC
op-types X I X
set-type $$
eval if ( ! $1 )
AssignTarget($$, CopyVal($2))

View file

@ -0,0 +1,55 @@
# Operations corresponding to relational expressions.
rel-expr-op LT
op-type I U D S T A
vector
eval $1 < $2
eval-type S Bstr_cmp($1->AsString(), $2->AsString()) < 0
eval-type T $1->IsSubsetOf(*$2) && $1->Size() < $2->Size()
eval-type A $1->AsAddr() < $2->AsAddr()
rel-expr-op LE
op-type I U D S T A
vector
eval $1 <= $2
eval-type S Bstr_cmp($1->AsString(), $2->AsString()) <= 0
eval-type T $1->IsSubsetOf(*$2)
eval-type A $1->AsAddr() < $2->AsAddr() || $1->AsAddr() == $2->AsAddr()
rel-expr-op EQ
op-type I U D S T A N F
vector
eval $1 == $2
eval-type S Bstr_cmp($1->AsString(), $2->AsString()) == 0
eval-type T $1->EqualTo(*$2)
eval-type A $1->AsAddr() == $2->AsAddr()
eval-type N $1->AsSubNet() == $2->AsSubNet()
eval-type F util::streq($1->Name(), $2->Name())
eval-mixed P S $1->MatchExactly($2->AsString())
rel-expr-op NE
op-type I U D S T A N F
vector
eval $1 != $2
eval-type S Bstr_cmp($1->AsString(), $2->AsString()) != 0
eval-type T ! $1->EqualTo(*$2)
eval-type A $1->AsAddr() != $2->AsAddr()
eval-type N $1->AsSubNet() != $2->AsSubNet()
eval-type F ! util::streq($1->Name(), $2->Name())
eval-mixed P S ! $1->MatchExactly($2->AsString())
# Note, canonicalization means that GE and GT shouldn't occur
# for Sets (type T).
rel-expr-op GE
op-type I U D S A
vector
eval $1 >= $2
eval-type S Bstr_cmp($1->AsString(), $2->AsString()) >= 0
eval-type A ! ($1->AsAddr() < $2->AsAddr())
rel-expr-op GT
op-type I U D S A
vector
eval $1 > $2
eval-type S Bstr_cmp($1->AsString(), $2->AsString()) > 0
eval-type A ! ($1->AsAddr() < $2->AsAddr()) && $1->AsAddr() != $2->AsAddr()

View file

@ -0,0 +1,57 @@
# Operations corresponding to scripting idioms / known script functions.
internal-op MinU
classes VVV VVC
op-types U U U
eval $$ = std::min($1, $2);
internal-op MinI
classes VVV VVC
op-types I I I
eval $$ = std::min($1, $2);
internal-op MinD
classes VVV VVC
op-types D D D
eval $$ = std::min($1, $2);
internal-op MaxU
classes VVV VVC
op-types U U U
eval $$ = std::max($1, $2);
internal-op MaxI
classes VVV VVC
op-types I I I
eval $$ = std::max($1, $2);
internal-op MaxD
classes VVV VVC
op-types D D D
eval $$ = std::max($1, $2);
internal-op Func-Id-String
class VV
op-types S R
eval auto id_rec = $1;
auto orig_h = DirectField(id_rec, 0).AsAddr()->AsAddr().AsString();
auto resp_h = DirectField(id_rec, 2).AsAddr()->AsAddr().AsString();
auto orig_p = static_cast<uint32_t>(DirectField(id_rec, 1).AsCount()) & ~PORT_SPACE_MASK;
auto resp_p = static_cast<uint32_t>(DirectField(id_rec, 3).AsCount()) & ~PORT_SPACE_MASK;
/* Maximum address size is for IPv6 with no compression. Each
* 8 16-bit hex elements plus 7 colons between them plus the two []'s
* = 8*4 + 7 + 2 = 41 characters.
*
* Maximum port size is 5.
*
* Two of these = 2*41 + 2*5 = 92.
* Other delimiters: two ':', one ' < ' for 5 more.
*
* TOTAL: 97 characters.
*
* We use considerably more for safety.
*/
char buf[128];
snprintf(buf, sizeof buf, "%s:%u > %s:%u", orig_h.c_str(), orig_p, resp_h.c_str(), resp_p);
Unref($$);
$$ = new StringVal(buf);

View file

@ -0,0 +1,339 @@
# Operations corresponding to statements, other than iterations.
macro EvalScheduleArgs(time, is_delta, build_args)
if ( ! run_state::terminating )
{
double dt = time;
if ( is_delta )
dt += run_state::network_time;
auto handler = EventHandlerPtr(Z_AUX_EVENT_HANDLER);
ValVec args;
build_args
auto timer = new ScheduleTimer(handler, std::move(args), dt);
timer_mgr->Add(timer);
}
macro EvalSchedule(time, is_delta)
EvalScheduleArgs(time, is_delta, Z_AUX->FillValVec(args, frame);)
op Schedule
class ViHL
op-types D I X X
op1-read
custom-method return CompileSchedule(n, nullptr, i, h, l);
eval EvalSchedule($1, $2)
op Schedule
class CiHL
op-types D I X X
op1-read
custom-method return CompileSchedule(nullptr, c, i, h, l);
eval EvalSchedule($1, $2)
internal-op Schedule0
classes ViH CiH
op-types D I X
op1-read
eval EvalScheduleArgs($1, $2,)
macro QueueEvent(eh, args)
if ( *eh )
event_mgr.Enqueue(eh, std::move(args));
op Event
class HL
op1-read
custom-method return CompileEvent(h, l);
eval ValVec args;
Z_AUX->FillValVec(args, frame);
QueueEvent(Z_AUX_EVENT_HANDLER, args);
internal-op Event0
class X
eval ValVec args(0);
QueueEvent(Z_AUX_EVENT_HANDLER, args);
internal-op Event1
class V
op1-read
eval ValVec args(1);
args[0] = $1.ToVal(Z_TYPE);
QueueEvent(Z_AUX_EVENT_HANDLER, args);
internal-op Event2
class VV
op1-read
eval ValVec args(2);
args[0] = $1.ToVal(Z_TYPE);
args[1] = $2.ToVal(Z_TYPE2);
QueueEvent(Z_AUX_EVENT_HANDLER, args);
internal-op Event3
class VVV
op1-read
eval ValVec args(3);
auto& aux = Z_AUX;
args[0] = $1.ToVal(Z_TYPE);
args[1] = $2.ToVal(Z_TYPE2);
args[2] = $3.ToVal(aux->elems[2].GetType());
QueueEvent(Z_AUX_EVENT_HANDLER, args);
internal-op Event4
class VVVV
op1-read
eval ValVec args(4);
auto& aux = Z_AUX;
args[0] = $1.ToVal(Z_TYPE);
args[1] = $2.ToVal(Z_TYPE2);
args[2] = $3.ToVal(aux->elems[2].GetType());
args[3] = $4.ToVal(aux->elems[3].GetType());
QueueEvent(Z_AUX_EVENT_HANDLER, args);
op Return
class X
eval EvalReturn(nullptr,)
macro EvalReturn(val, type)
ret_u = val;
type
DO_ZAM_PROFILE
pc = end_pc;
continue;
op Return
op1-read
classes V C
set-type $$
eval EvalReturn(&$$, SET_RET_TYPE(Z_TYPE))
op When-Return
class X
eval static auto any_val = ZVal();
EvalReturn(&any_val,);
# Branch on the value of v1 using switch table v2, with default branch to v3
macro EvalSwitchBody(index, branch, cases, postscript)
{
auto t = cases[index];
if ( t.find(v) == t.end() )
pc = branch;
else
pc = t[v];
postscript
DO_ZAM_PROFILE
continue;
}
internal-op SwitchI
op1-read
class Vii
op-types I I I
eval auto v = $1;
EvalSwitchBody($2, $3, int_cases,)
internal-op SwitchU
op1-read
class Vii
op-types U I I
eval auto v = $1;
EvalSwitchBody($2, $3, uint_cases,)
internal-op SwitchD
op1-read
class Vii
op-types D I I
eval auto v = $1;
EvalSwitchBody($2, $3, double_cases,)
internal-op SwitchS
op1-read
class Vii
op-types S I I
eval auto vs = $1->AsString()->Render();
std::string v(vs);
EvalSwitchBody($2, $3, str_cases,delete[] vs;)
internal-op SwitchA
op1-read
class Vii
op-types A I I
eval auto v = $1->AsAddr().AsString();
EvalSwitchBody($2, $3, str_cases,)
internal-op SwitchN
op1-read
class Vii
op-types N I I
eval auto v = $1->AsSubNet().AsString();
EvalSwitchBody($2, $3, str_cases,)
internal-op Determine-Type-Match
class VV
op-types I a
eval auto& aux = Z_AUX;
int match = -1;
for ( int i = 0; i < aux->n; ++i )
{
auto& el = aux->elems[i];
auto& et = el.GetType();
if ( can_cast_value_to_type($1, et.get()) )
{
match = i;
if ( el.Slot() >= 0 )
{
auto& tv = frame[el.Slot()];
if ( el.IsManaged() )
Unref(tv.ManagedVal());
tv = ZVal(cast_value_to_type($1, et.get()), et);
}
break;
}
}
$$ = match;
op CheckAnyLen
op1-read
class Vi
op-types L U
eval auto v = $1;
if ( v->Vals().size() != $2 )
ERROR("mismatch in list lengths");
op Print
class O
eval do_print_stmt(Z_AUX->ToValVec(frame));
method-post z.aux = v->aux;
op Print1
op1-read
classes V C
set-type $$
eval std::vector<ValPtr> vals;
vals.push_back($$.ToVal(Z_TYPE));
do_print_stmt(vals);
internal-op If-Else
op1-read
class Vb
op-types I I
eval if ( ! $1 ) $2
internal-op If
op1-read
class Vb
op-types I I
eval if ( ! $1 ) $2
internal-op If-Not
op1-read
class Vb
op-types I I
eval if ( $1 ) $2
op AddStmt
op1-read
class VO
eval auto indices = Z_AUX->ToListVal(frame);
EvalAddStmt($1, indices)
method-post z.aux = v->aux;
macro EvalAddStmt(lhs, ind)
auto index = ind;
bool iterators_invalidated = false;
lhs.AsTable()->Assign(std::move(index), nullptr, true, &iterators_invalidated);
if ( iterators_invalidated )
WARN("possible loop/iterator invalidation");
op AddStmt1
op1-read
set-type $1
classes VV VC
eval EvalAddStmt($1, $2.ToVal(Z_TYPE))
op ClearTable
op1-read
class V
op-types T
eval $1->RemoveAll();
op ClearVector
op1-read
class V
op-types V
eval $1->Resize(0);
op DelTable
op1-read
class VO
op-types T X
eval auto indices = Z_AUX->ToListVal(frame);
bool iterators_invalidated = false;
$1->Remove(*indices, true, &iterators_invalidated);
if ( iterators_invalidated )
WARN("possible loop/iterator invalidation");
method-post z.aux = v->aux;
op DelField
op1-read
class Vi
op-types R I
eval $1->Remove($2);
internal-op Init-Record
class V
op-types R
eval auto r = new RecordVal(cast_intrusive<RecordType>(Z_TYPE));
Unref($$);
$$ = r;
internal-op Init-Vector
class V
op-types V
eval auto vt = cast_intrusive<VectorType>(Z_TYPE);
auto vec = new VectorVal(std::move(vt));
Unref($$);
$$ = vec;
internal-op Init-Table
class V
op-types T
eval auto tt = cast_intrusive<TableType>(Z_TYPE);
auto t = new TableVal(tt, Z_AUX_ATTRS);
Unref($$);
$$ = t;
op When
class V
op1-read
op-types F
eval BuildWhen($1, -1.0)
op When-Timeout
classes VV VC
op1-read
op-types F D
eval BuildWhen($1, $2)
macro BuildWhen(zf, timeout)
auto& aux = Z_AUX;
auto wi = Z_AUX_WHEN_INFO;
FuncPtr func{NewRef{}, zf};
auto lambda = make_intrusive<FuncVal>(func);
wi->Instantiate(std::move(lambda));
std::vector<ValPtr> local_aggrs;
for ( int i = 0; i < aux->n; ++i )
{
auto v = aux->ToVal(frame, i);
if ( v )
local_aggrs.push_back(v);
}
(void)make_intrusive<trigger::Trigger>(wi, wi->WhenExprGlobals(), local_aggrs, timeout, Z_FRAME, Z_LOC->Loc());

View file

@ -0,0 +1,181 @@
# Operations corresponding to unary expressions.
# Direct assignment of an existing value.
assign-op Assign
class V
# The same, but where the assignment target (LHS) is a record field.
assign-op Field-LHS-Assign
op1-read
class F
unary-expr-op Clone
no-const
op-type X
set-type $$
set-type2 $1
eval auto v = $1.ToVal(Z_TYPE2)->Clone();
AssignTarget($$, BuildVal(v, Z_TYPE))
unary-expr-op Size
no-const
op-type I U D A N S T V *
explicit-result-type
set-type $$
set-type2 $1
eval-type I $$ = ZVal(zeek_int_t($1 < 0 ? -$1 : $1));
eval-type U $$ = ZVal($1);
eval-type D $$ = ZVal($1 < 0 ? -$1 : $1);
eval-type A $$ = ZVal(zeek_uint_t($1->AsAddr().GetFamily() == IPv4 ? 32 : 128));
eval-type N $$ = ZVal(pow(2.0, double(128 - $1->AsSubNet().LengthIPv6())));
eval-type S $$ = ZVal(zeek_uint_t($1->Len()));
eval-type T $$ = ZVal(zeek_uint_t($1->Size()));
eval-type V $$ = ZVal(zeek_uint_t($1->Size()));
eval auto v = $1.ToVal(Z_TYPE2)->SizeVal();
$$ = BuildVal(v, Z_TYPE);
unary-expr-op Not
op-type I
eval ! $1
unary-expr-op Complement
op-type U
eval ~ $1
unary-expr-op Positive
op-type I U D
vector
eval $1
unary-expr-op Negate
op-type I U D
vector
eval -$1
op IncrI
op1-read-write
class V
op-types I
eval ++$$;
op IncrU
op1-read-write
class V
op-types U
eval ++$$;
op DecrI
op1-read-write
class V
op-types I
eval --$$;
op DecrU
op1-read-write
class V
op-types U
eval auto& u = $$;
if ( u == 0 )
WARN("count underflow");
--u;
unary-op AppendTo
# Note, even though it feels like appending both reads and modifies
# its first operand, for our purposes it just reads it (to get the
# aggregate), and then modifies its *content* but not the operand's
# value itself.
op1-read
set-type $1
eval auto vv = $1.AsVector();
if ( vv->Size() == 0 )
/* Use the slightly more expensive Assign(), since it
* knows how to deal with empty vectors that do not yet
* have concrete types.
*/
vv->Assign(0, $2.ToVal(Z_TYPE));
else
{
vv->RawVec().push_back(CopyVal($2));
vv->Modified();
}
# For vectors-of-any, we always go through the Assign() interface because
# it's needed for tracking the potentially differing types.
unary-op AppendToAnyVec
op1-read
set-type $1
eval auto vv = $1.AsVector();
vv->Assign(vv->Size(), $2.ToVal(Z_TYPE));
internal-op AddPatternToField
classes VVi VCi
op1-read
op-types R P I
eval auto r = $$;
auto fpat = r->GetField($2)->AsPatternVal();
if ( fpat )
{
$1->AddTo(fpat, false);
r->Modified();
}
else
ERROR(util::fmt("field value missing: $%s", r->GetType()->AsRecordType()->FieldName($2)));
unary-op ExtendPattern
op1-read
eval $1.AsPattern()->AddTo($$.AsPattern(), false);
unary-op AddVecToVec
op1-read
eval if ( ! $1.AsVector()->AddTo($$.AsVector(), false) )
ERROR("incompatible vector element assignment");
unary-op AddTableToTable
op1-read
eval auto t = $$.AsTable();
auto v = $1.AsTable();
if ( v->Size() > 0 )
{
v->AddTo(t, false);
t->Modified();
}
unary-op RemoveTableFromTable
op1-read
eval auto t = $$.AsTable();
auto v = $1.AsTable();
if ( v->Size() > 0 )
{
v->RemoveFrom(t);
t->Modified();
}
unary-expr-op Cast
op-type X
set-type $$
set-type2 $1
eval EvalCast($$, $1.ToVal(Z_TYPE2))
macro EvalCast(lhs, rhs)
std::string error;
auto res = cast_value(rhs, Z_TYPE, error);
if ( res )
AssignTarget(lhs, BuildVal(res, Z_TYPE))
else
ERROR(error.c_str());
# Cast an "any" type to the given type. Only needed for type-based switch
# statements.
internal-op Cast-Any
class VV
op-types X a
eval ValPtr rhs = {NewRef{}, $1};
EvalCast($$, rhs)
direct-unary-op Is Is
internal-op Is
class VV
op-types I X
eval auto rhs = $1.ToVal(Z_TYPE2).get();
$$ = can_cast_value_to_type(rhs, Z_TYPE.get());