From e94764982d02406faa35240cc03f308eee2f9de9 Mon Sep 17 00:00:00 2001 From: Vern Paxson Date: Mon, 5 Aug 2024 17:57:43 +0100 Subject: [PATCH] factoring of ZAM operation specifications into separate files --- auxil/gen-zam | 2 +- src/CMakeLists.txt | 18 +- src/script_opt/ZAM/OPs/README.txt | 286 ++ src/script_opt/ZAM/OPs/ZAM.op | 3368 -------------------- src/script_opt/ZAM/OPs/ZBI.op | 627 ++++ src/script_opt/ZAM/OPs/aggr-assignments.op | 89 + src/script_opt/ZAM/OPs/binary-exprs.op | 104 + src/script_opt/ZAM/OPs/calls.op | 180 ++ src/script_opt/ZAM/OPs/coercions.op | 151 + src/script_opt/ZAM/OPs/constructors.op | 251 ++ src/script_opt/ZAM/OPs/indexing.op | 212 ++ src/script_opt/ZAM/OPs/internal.op | 124 + src/script_opt/ZAM/OPs/iterations.op | 124 + src/script_opt/ZAM/OPs/macros.op | 74 + src/script_opt/ZAM/OPs/non-uniform.op | 267 ++ src/script_opt/ZAM/OPs/rel-exprs.op | 55 + src/script_opt/ZAM/OPs/script-idioms.op | 57 + src/script_opt/ZAM/OPs/stmts.op | 339 ++ src/script_opt/ZAM/OPs/unary-exprs.op | 181 ++ 19 files changed, 3139 insertions(+), 3370 deletions(-) create mode 100644 src/script_opt/ZAM/OPs/README.txt delete mode 100644 src/script_opt/ZAM/OPs/ZAM.op create mode 100644 src/script_opt/ZAM/OPs/ZBI.op create mode 100644 src/script_opt/ZAM/OPs/aggr-assignments.op create mode 100644 src/script_opt/ZAM/OPs/binary-exprs.op create mode 100644 src/script_opt/ZAM/OPs/calls.op create mode 100644 src/script_opt/ZAM/OPs/coercions.op create mode 100644 src/script_opt/ZAM/OPs/constructors.op create mode 100644 src/script_opt/ZAM/OPs/indexing.op create mode 100644 src/script_opt/ZAM/OPs/internal.op create mode 100644 src/script_opt/ZAM/OPs/iterations.op create mode 100644 src/script_opt/ZAM/OPs/macros.op create mode 100644 src/script_opt/ZAM/OPs/non-uniform.op create mode 100644 src/script_opt/ZAM/OPs/rel-exprs.op create mode 100644 src/script_opt/ZAM/OPs/script-idioms.op create mode 100644 src/script_opt/ZAM/OPs/stmts.op create mode 100644 src/script_opt/ZAM/OPs/unary-exprs.op diff --git a/auxil/gen-zam b/auxil/gen-zam index 610cf8527d..cfc0c7b9de 160000 --- a/auxil/gen-zam +++ b/auxil/gen-zam @@ -1 +1 @@ -Subproject commit 610cf8527dad7033b971595a1d556c2c95294f2b +Subproject commit cfc0c7b9de63f44419c2a57040ae6b7081a66a33 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c7ae4f183c..5a623745f6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -162,7 +162,23 @@ list(APPEND BINPAC_OUTPUTS "${BINPAC_OUTPUT_CC}") include(Gen-ZAM) 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}) diff --git a/src/script_opt/ZAM/OPs/README.txt b/src/script_opt/ZAM/OPs/README.txt new file mode 100644 index 0000000000..c8174b5c2c --- /dev/null +++ b/src/script_opt/ZAM/OPs/README.txt @@ -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. diff --git a/src/script_opt/ZAM/OPs/ZAM.op b/src/script_opt/ZAM/OPs/ZAM.op deleted file mode 100644 index 6b18b4156e..0000000000 --- a/src/script_opt/ZAM/OPs/ZAM.op +++ /dev/null @@ -1,3368 +0,0 @@ -# See the file "COPYING" in the main distribution directory for copyright. - -# This file 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. -# -# 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-binary-op the same, for operations that take two operands -# 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: -# -# type 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 types: -# -# 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 -# i integer constant, often a record field offset -# -# 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 type 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. -# -# 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 -# 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) -# -# 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-pre code to add to the beginning of the "eval" block. -# This can be required for operations where Gen-ZAM -# generates elements of the C++ (such as for expr-op'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". -# -# op-accessor tells Gen-ZAM what ZVal accessor to use to get to -# the underlying values of the operand(s) -# -# op1-accessor the same as op-accessor except only for the first -# operand -# -# 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. -# -# op2-accessor the same as op-accessor except only for the second -# operand -# -# 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 - - -# 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) (ZVal::IsManagedType(z.t) ? BuildVal((v).ToVal(z.t), z.t) : (v)) - -# Managed assignments to frame[s.v1]. -macro AssignV1T(v, t) { - if ( z.is_managed ) - { - /* It's important to hold a reference to v here prior - to the deletion in case frame[z.v1] points to v. */ - auto v2 = v; - ZVal::DeleteManagedType(frame[z.v1]); - frame[z.v1] = v2; - } - else - frame[z.v1] = v; - } -# Convenience macro for when the value of the assigned type comes from -# the instruction. -macro AssignV1(v) AssignV1T(v, z.t) - -macro BRANCH(target_slot) { DO_ZAM_PROFILE; pc = z.target_slot; continue; } - -########## Unary Ops ########## - -# Direct assignment of an existing value. -assign-op Assign -type V - -# The same, but where the assignment target (LHS) is a record field. -assign-op Field-LHS-Assign -op1-read -type F - -unary-expr-op Clone -no-const -op-type X -set-type $$ -set-type2 $1 -eval auto v = frame[z.v2].ToVal(z.t2)->Clone(); - AssignV1(BuildVal(v, z.t)) - -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 = frame[z.v2].ToVal(z.t2)->SizeVal(); - $$ = BuildVal(v, z.t); - -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 -type V -eval ++frame[z.v1].int_val; - -op IncrU -op1-read-write -type V -eval ++frame[z.v1].uint_val; - -op DecrI -op1-read-write -type V -eval --frame[z.v1].int_val; - -op DecrU -op1-read-write -type V -eval auto& u = frame[z.v1].uint_val; - if ( u == 0 ) - ZAM_run_time_warning(z.loc, "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 = frame[z.v1].vector_val; - 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, $1.ToVal(z.t)); - else - { - vv->RawVec().push_back(CopyVal($1)); - 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 = frame[z.v1].vector_val; - vv->Assign(vv->Size(), $1.ToVal(z.t)); - -internal-op AddPatternToField -type VVi -op1-read -eval EvalAddPatternToField(frame[z.v2], v3) - -macro EvalAddPatternToField(v, f) - auto fpat = frame[z.v1].record_val->GetField(z.f)->AsPatternVal(); - if ( fpat ) - { - v.re_val->AddTo(fpat, false); - frame[z.v1].record_val->Modified(); - } - else - ZAM_run_time_error(z.loc, util::fmt("field value missing: $%s", frame[z.v1].record_val->GetType()->AsRecordType()->FieldName(z.f))); - -internal-op AddPatternToField -type VCi -op1-read -eval EvalAddPatternToField(z.c, v2) - -unary-op ExtendPattern -op1-read -eval $1.re_val->AddTo(frame[z.v1].re_val, false); - -unary-op AddVecToVec -op1-read -eval if ( ! $1.vector_val->AddTo(frame[z.v1].vector_val, false) ) - ZAM_run_time_error(z.loc, "incompatible vector element assignment"); - -unary-op AddTableToTable -op1-read -eval auto t = frame[z.v1].table_val; - auto v = $1.table_val; - if ( v->Size() > 0 ) - { - v->AddTo(t, false); - t->Modified(); - } - -unary-op RemoveTableFromTable -op1-read -eval auto t = frame[z.v1].table_val; - auto v = $1.table_val; - if ( v->Size() > 0 ) - { - v->RemoveFrom(t); - t->Modified(); - } - -unary-expr-op Cast -op-type X -set-type $$ -set-type2 $1 -eval EvalCast(frame[z.v2].ToVal(z.t2)) - -macro EvalCast(rhs) - std::string error; - auto res = cast_value(rhs, z.t, error); - if ( res ) - AssignV1(BuildVal(res, z.t)) - else - ZAM_run_time_error(z.loc, error.c_str()); - -# Cast an "any" type to the given type. Only needed for type-based switch -# statements. -internal-op Cast-Any -type VV -eval ValPtr rhs = {NewRef{}, frame[z.v2].any_val}; - EvalCast(rhs) - -direct-unary-op Is Is - -internal-op Is -type VV -eval auto rhs = frame[z.v2].ToVal(z.t2).get(); - frame[z.v1].int_val = can_cast_value_to_type(rhs, z.t.get()); - -########## Binary Ops ########## - -binary-expr-op Add -op-type I U D S -vector -eval $1 + $2 -eval-type S vector 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 -# -eval-pre if ( $2 == 0 ) - { - ZAM_run_time_error(z.loc, "division by zero"); - break; - } -eval $1 / $2 - -binary-expr-op Mask -op-type I -vector -### Note that this first "eval" is a dummy - we'll never generate code -### that uses it because "Mask" expressions don't have LHS operands of -### type "int". We could omit this if we modified Gen-ZAM to understand -### that an op-type of 'X' for a binary-expr-op means "skip the usual case -### of two operands of the same type". -eval $1 / $2 -eval-mixed A I auto mask = static_cast($2); - auto a = $1->AsAddr(); - if ( a.GetFamily() == IPv4 && mask > 32 ) - ZAM_run_time_error(z.loc, util::fmt("bad IPv4 subnet prefix length: %" PRIu32, mask)); - if ( a.GetFamily() == IPv6 && mask > 128 ) - ZAM_run_time_error(z.loc, util::fmt("bad IPv6 subnet prefix length: %" PRIu32, mask)); - auto v = make_intrusive(a, mask); - Unref(frame[z.v1].subnet_val); - frame[z.v1].subnet_val = v.release(); - -binary-expr-op Mod -op-type I U -vector -eval-pre if ( $2 == 0 ) - { - ZAM_run_time_error(z.loc, "modulo by zero"); - break; - } -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 ) - ZAM_run_time_error(z.loc, "left shifting a negative number is undefined"); - $$ = $1 << $2; -eval $1 << $2 - -binary-expr-op Rshift -op-type I U -vector -eval $1 >> $2 - -########## Relationals ########## - -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() - -########## Nonuniform Expressions ########## - -assign-op Field -type R -field-op -assign-val v -eval auto r = frame[z.v2].record_val; - auto& rv = r->RawOptField(z.v3); - if ( ! rv ) - { - auto def = r->GetType()->FieldDefault(z.v3); - if ( def ) - rv = ZVal(def, z.t); - else - { - ZAM_run_time_error(z.loc, util::fmt("field value missing: $%s", r->GetType()->AsRecordType()->FieldName(z.v3))); - break; - } - } - auto v = *rv; - -expr-op Has-Field -type VRi -includes-field-op -eval frame[z.v1].int_val = frame[z.v2].record_val->HasField(z.v3); - -internal-op Has-Field-Cond -op1-read -type VVV -eval if ( ! frame[z.v1].record_val->HasField(z.v2) ) - BRANCH(v3) - -internal-op Not-Has-Field-Cond -op1-read -type VVV -eval if ( frame[z.v1].record_val->HasField(z.v2) ) - BRANCH(v3) - -internal-op Table-Has-Elements -type VV -eval frame[z.v1].int_val = frame[z.v2].table_val->Size() > 0; - -internal-op Table-Has-Elements-Cond -op1-read -type VV -eval if ( frame[z.v1].table_val->Size() == 0 ) - BRANCH(v2) - -internal-op Not-Table-Has-Elements-Cond -op1-read -type VV -eval if ( frame[z.v1].table_val->Size() > 0 ) - BRANCH(v2) - -internal-op Vector-Has-Elements -type VV -eval frame[z.v1].int_val = frame[z.v2].vector_val->Size() > 0; - -internal-op Vector-Has-Elements-Cond -op1-read -type VV -eval if ( frame[z.v1].vector_val->Size() == 0 ) - BRANCH(v2) - -internal-op Not-Vector-Has-Elements-Cond -op1-read -type VV -eval if ( frame[z.v1].vector_val->Size() > 0 ) - BRANCH(v2) - -expr-op In -type VVV -custom-method return CompileInExpr(n1, n2, n3); -no-eval - -expr-op In -type VCV -custom-method return CompileInExpr(n1, c, n2); -no-eval - -expr-op In -type VVC -custom-method return CompileInExpr(n1, n2, c); -no-eval - -macro EvalPInS(op1, op2) - frame[z.v1].int_val = op1.re_val->MatchAnywhere(op2.string_val->AsString()) != 0; - -internal-op P-In-S -type VVV -eval EvalPInS(frame[z.v2], frame[z.v3]) - -internal-op P-In-S -type VCV -eval EvalPInS(z.c, frame[z.v2]) - -internal-op P-In-S -type VVC -eval EvalPInS(frame[z.v2], z.c) - -macro EvalStrInPatTbl(op1, op2) - frame[z.v1].int_val = op2.table_val->MatchPattern({NewRef{}, op1.string_val}); - -internal-op Str-In-Pat-Tbl -type VVV -eval EvalStrInPatTbl(frame[z.v2], frame[z.v3]) - -internal-op Str-In-Pat-Tbl -type VCV -eval EvalStrInPatTbl(z.c, frame[z.v2]) - -internal-binary-op S-In-S -op-accessor string_val -op-type I -eval auto sc = reinterpret_cast(op1->CheckString()); - auto cmp = util::strstr_n(op2->Len(), op2->Bytes(), op1->Len(), sc); - $$ = cmp != -1; - -internal-binary-op A-In-S -op1-accessor addr_val -op2-accessor subnet_val -op-type I -eval $$ = op2->Contains(op1->AsAddr()); - - -# Handled differently because of the unusual middle argument. -op L-In-T -type VLV -custom-method return CompileInExpr(n1, l, n2); -no-eval - -op L-In-T -type VLC -custom-method return CompileInExpr(n, l, c); -no-eval - -op L-In-Vec -type VLV -custom-method return CompileInExpr(n1, l, n2); -no-eval - -op L-In-Vec -type VLC -custom-method return CompileInExpr(n, l, c); -no-eval - - -internal-op Val-Is-In-Table -type VVV -# No set-type as these are internal ops. -eval auto op1 = frame[z.v2].ToVal(z.t); - frame[z.v1].int_val = frame[z.v3].table_val->Find(op1) != nullptr; - -internal-op Val-Is-In-Table-Cond -op1-read -type VVV -eval auto op1 = frame[z.v1].ToVal(z.t); - if ( ! frame[z.v2].table_val->Find(op1) ) - BRANCH(v3) - -internal-op Val-Is-Not-In-Table-Cond -op1-read -type VVV -eval auto op1 = frame[z.v1].ToVal(z.t); - if ( frame[z.v2].table_val->Find(op1) ) - BRANCH(v3) - -# 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) - auto lvp = zeek::make_intrusive(TYPE_ANY); - lvp->Append(op1); - lvp->Append(op2); - -macro EvalVal2InTableAssignCore(slot) - frame[z.v1].int_val = frame[z.slot].table_val->Find(std::move(lvp)) != nullptr; - -macro EvalVal2InTablePre(op1, op2, op3) - auto& tt_ind = frame[z.op3].table_val->GetType()->AsTableType()->GetIndexTypes(); - EvalVal2InTableCore(frame[z.op1].ToVal(z.t2), frame[z.op2].ToVal(tt_ind[1])) - -internal-op Val2-Is-In-Table -type VVVV -eval EvalVal2InTablePre(v2,v3,v4) - EvalVal2InTableAssignCore(v4) - -internal-op Val2-Is-In-Table-Cond -op1-read -type VVVV -eval EvalVal2InTablePre(v1,v2,v3) - EvalVal2InTableCond(v3, lvp, v4, !) - -macro EvalVal2InTableCond(cond, op, target, negate) - if ( negate frame[z.cond].table_val->Find(op) ) - BRANCH(target) - -internal-op Val2-Is-Not-In-Table-Cond -op1-read -type VVVV -eval EvalVal2InTablePre(v1,v2,v3) - EvalVal2InTableCond(v3, lvp, v4,) - if ( frame[z.v3].table_val->Find(lvp) ) - BRANCH(v4) - -internal-op Val2-Is-In-Table -type VVVC -eval EvalVal2InTableCore(frame[z.v2].ToVal(z.t2), z.c.ToVal(z.t)) - EvalVal2InTableAssignCore(v3) - -internal-op Val2-Is-In-Table-Cond -op1-read -type VVVC -eval EvalVal2InTableCore(frame[z.v1].ToVal(z.t2), z.c.ToVal(z.t)) - EvalVal2InTableCond(v2, lvp, v3, !) - -internal-op Val2-Is-Not-In-Table-Cond -op1-read -type VVVC -eval EvalVal2InTableCore(frame[z.v1].ToVal(z.t2), z.c.ToVal(z.t)) - EvalVal2InTableCond(v2, lvp, v3, ) - -internal-op Val2-Is-In-Table -type VVCV -eval EvalVal2InTableCore(z.c.ToVal(z.t), frame[z.v2].ToVal(z.t2)) - EvalVal2InTableAssignCore(v3) - -internal-op Val2-Is-In-Table-Cond -op1-read -type VVCV -eval EvalVal2InTableCore(z.c.ToVal(z.t), frame[z.v1].ToVal(z.t2)) - EvalVal2InTableCond(v2, lvp, v3, !) - -internal-op Val2-Is-Not-In-Table-Cond -op1-read -type VVCV -eval EvalVal2InTableCore(z.c.ToVal(z.t), frame[z.v1].ToVal(z.t2)) - EvalVal2InTableCond(v2, lvp, v3, ) - - -internal-op Const-Is-In-Table -type VCV -eval auto op1 = z.c.ToVal(z.t); - frame[z.v1].int_val = frame[z.v2].table_val->Find(op1) != nullptr; - -internal-op Const-Is-In-Table-Cond -op1-read -type VVC -eval auto op1 = z.c.ToVal(z.t); - if ( ! frame[z.v1].table_val->Find(op1) ) - BRANCH(v2) - -internal-op Const-Is-Not-In-Table-Cond -op1-read -type VVC -eval auto op1 = z.c.ToVal(z.t); - if ( frame[z.v1].table_val->Find(op1) ) - BRANCH(v2) - -internal-op List-Is-In-Table -type VV -eval auto op1 = z.aux->ToListVal(frame); - frame[z.v1].int_val = frame[z.v2].table_val->Find(std::move(op1)) != nullptr; - -internal-op List-Is-In-Table -type VC -eval auto op1 = z.aux->ToListVal(frame); - frame[z.v1].int_val = z.c.table_val->Find(std::move(op1)) != nullptr; - -internal-op Val-Is-In-Vector -type VVV -eval auto& vec = frame[z.v3].vector_val; - auto ind = frame[z.v2].int_val; - frame[z.v1].int_val = vec->Has(ind); - -internal-op Const-Is-In-Vector -type VCV -eval auto& vec = frame[z.v2].vector_val; - auto ind = z.c.int_val; - frame[z.v1].int_val = vec->Has(ind); - -expr-op Cond -type VVVV -set-type $2 -eval AssignV1(frame[z.v2].int_val ? CopyVal(frame[z.v3]) : CopyVal(frame[z.v4])) - -expr-op Cond -type VVVC -set-type $2 -eval AssignV1(frame[z.v2].int_val ? CopyVal(frame[z.v3]) : CopyVal(z.c)) - -expr-op Cond -type VVCV -set-type $2 -eval AssignV1(frame[z.v2].int_val ? CopyVal(z.c) : CopyVal(frame[z.v3])) - -op Bool-Vec-Cond -type VVVV -set-type $2 -eval auto& vsel = frame[z.v2].vector_val->RawVec(); - auto& v1 = frame[z.v3].vector_val->RawVec(); - auto& v2 = frame[z.v4].vector_val->RawVec(); - auto n = v1.size(); - auto res = new vector>(n); - for ( auto i = 0U; i < n; ++i ) - if ( vsel[i] ) - (*res)[i] = vsel[i]->int_val ? v1[i] : v2[i]; - auto& full_res = frame[z.v1].vector_val; - Unref(full_res); - full_res = new VectorVal(cast_intrusive(z.t), 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 -type VVC -set-type $$ -eval if ( frame[z.v2].int_val ) - AssignV1(CopyVal(z.c)) - -op CondC2 -set-type $$ -type VVC -eval if ( ! frame[z.v2].int_val ) - AssignV1(CopyVal(z.c)) - -########## Index Expressions ########## - -op IndexVecBoolSelect -type VVV -set-type $$ -eval EvalIndexVecBoolSelect(frame[z.v2], frame[z.v3]) - -macro EvalIndexVecBoolSelect(op1, op2) - if ( op1.vector_val->Size() != op2.vector_val->Size() ) - { - ZAM_run_time_error(z.loc, "size mismatch, boolean index and vector"); - break; - } - auto vt = cast_intrusive(z.t); - auto v2 = op1.vector_val; - auto v3 = op2.vector_val; - auto v = vector_bool_select(std::move(vt), v2, v3); - Unref(frame[z.v1].vector_val); - frame[z.v1].vector_val = v.release(); - -op IndexVecBoolSelect -type VCV -set-type $$ -eval EvalIndexVecBoolSelect(z.c, frame[z.v2]) - -op IndexVecIntSelect -type VVV -set-type $$ -eval EvalIndexVecIntSelect(frame[z.v2], frame[z.v3]) - -macro EvalIndexVecIntSelect(op1, op2) - auto vt = cast_intrusive(z.t); - auto v2 = op1.vector_val; - auto v3 = op2.vector_val; - auto v = vector_int_select(std::move(vt), v2, v3); - Unref(frame[z.v1].vector_val); - frame[z.v1].vector_val = v.release(); - -op IndexVecIntSelect -type VCV -set-type $$ -eval EvalIndexVecIntSelect(z.c, frame[z.v2]) - -op Index -type VVL -custom-method return CompileIndex(n1, n2, l, false); - -op Index -type VCL -custom-method return CompileIndex(n, c, l, false); - -op WhenIndex -type VVL -custom-method return CompileIndex(n1, n2, l, true); - -op WhenIndex -type VCL -custom-method return CompileIndex(n, c, l, true); - -internal-op Index-Vec -type VVV -eval EvalIndexVec(frame[z.v3].uint_val) - -macro EvalIndexVec(index) - auto& vv = frame[z.v2].vector_val->RawVec(); - const auto& vec = vv; - zeek_int_t ind = index; - if ( ind < 0 ) - ind += vv.size(); - if ( ind < 0 || ind >= int(vv.size()) ) - ZAM_run_time_error(z.loc, "no such index"); - AssignV1(CopyVal(*vec[ind])) - -internal-op Index-VecC -type VVV -eval EvalIndexVec(z.v3) - -internal-op Index-Any-Vec -type VVV -eval EvalIndexAnyVec(frame[z.v3].uint_val) - -macro EvalIndexAnyVec(index) - auto vv = frame[z.v2].vector_val; - zeek_int_t ind = index; - if ( ind < 0 ) - ind += vv->Size(); - if ( ind < 0 || ind >= int(vv->Size()) ) - ZAM_run_time_error(z.loc, "no such index"); - AssignV1(ZVal(vv->ValAt(ind).release())) - -internal-op Index-Any-VecC -type VVV -eval EvalIndexAnyVec(z.v3) - -macro WhenIndexResCheck() - auto& res = frame[z.v1].vector_val; - if ( res && IndexExprWhen::evaluating > 0 ) - IndexExprWhen::results.push_back({NewRef{}, res}); - -internal-op When-Index-Vec -type VVV -eval EvalIndexAnyVec(frame[z.v3].uint_val) - WhenIndexResCheck() - -internal-op When-Index-VecC -type VVV -eval EvalIndexAnyVec(z.v3) - WhenIndexResCheck() - -macro EvalVecSlice() - auto vec = frame[z.v2].vector_val; - auto lv = z.aux->ToListVal(frame); - auto v = index_slice(vec, lv.get()); - Unref(frame[z.v1].vector_val); - frame[z.v1].vector_val = v.release(); - -internal-op Index-Vec-Slice -type VV -eval EvalVecSlice() - -internal-op When-Index-Vec-Slice -type VV -eval EvalVecSlice() - WhenIndexResCheck() - -internal-op Table-Index -type VV -eval EvalTableIndex(z.aux->ToListVal(frame)) - AssignV1(BuildVal(v, z.t)) - -macro EvalTablePatStr(index) - auto& lhs = frame[z.v1]; - auto vec = ZVal(frame[z.v2].table_val->LookupPattern({NewRef{}, index.string_val})); - ZVal::DeleteManagedType(lhs); - lhs = vec; - -internal-op Table-PatStr-Index -type VVV -eval EvalTablePatStr(frame[z.v3]) - -internal-op Table-PatStr-Index -type VVC -eval EvalTablePatStr(z.c) - -internal-op When-Table-Index -type VV -eval EvalTableIndex(z.aux->ToListVal(frame)) - if ( IndexExprWhen::evaluating > 0 ) - IndexExprWhen::results.emplace_back(v); - AssignV1(BuildVal(v, z.t)) - -macro EvalTableIndex(index) - auto v = frame[z.v2].table_val->FindOrDefault(index); - if ( ! v ) - { - ZAM_run_time_error(z.loc, "no such index"); - break; - } - -internal-op When-PatStr-Index -type VV -eval auto args = z.aux->ToListVal(frame); - auto arg0 = args->Idx(0); - auto v = frame[z.v2].table_val->LookupPattern({NewRef{}, arg0->AsStringVal()}); - if ( IndexExprWhen::evaluating > 0 ) - IndexExprWhen::results.emplace_back(v); - AssignV1(BuildVal(v, z.t)) - -internal-assignment-op Table-Index1 -type VVV -assign-val v -eval EvalTableIndex(frame[z.v3].ToVal(z.t)) -# No AssignV1 needed, as this is an assignment-op - -internal-assignment-op Table-Index1 -type VVC -assign-val v -eval EvalTableIndex(z.c.ToVal(z.t)) - -# This version is for a variable v3. -internal-op Index-String -type VVV -eval EvalIndexString(frame[z.v3].int_val) - -macro EvalIndexString(index) - auto str = frame[z.v2].string_val->AsString(); - auto len = str->Len(); - auto idx = index; - if ( idx < 0 ) - idx += len; - auto v = str->GetSubstring(idx, 1); - Unref(frame[z.v1].string_val); - frame[z.v1].string_val = new StringVal(v ? v : new String("")); - -# This version is for a constant v3. -internal-op Index-StringC -type VVV -eval EvalIndexString(z.v3) - -internal-op Index-String-Slice -type VV -eval auto str = frame[z.v2].string_val->AsString(); - auto lv = z.aux->ToListVal(frame); - auto slice = index_string(str, lv.get()); - Unref(frame[z.v1].string_val); - frame[z.v1].string_val = new StringVal(slice->ToStdString()); - -op AnyIndex -type VVi -set-type $$ -eval auto lv = frame[z.v2].any_val->AsListVal(); - if ( z.v3 < 0 || z.v3 >= lv->Length() ) - reporter->InternalError("bad \"any\" element index"); - ValPtr elem = lv->Idx(z.v3); - if ( CheckAnyType(elem->GetType(), z.t, z.loc) ) - AssignV1(BuildVal(elem, z.t)) - else - ZAM_error = true; - - -########## Constructors ########## - -# Table construction requires atypical evaluation of list elements -# using information from their expression specifics. -direct-unary-op Table-Constructor ConstructTable - -macro ConstructTableOrSetPre() - auto tt = cast_intrusive(z.t); - auto new_t = new TableVal(tt, z.aux->attrs); - auto aux = z.aux; - auto n = aux->n; - auto ind_width = z.v2; - -macro ConstructTableOrSetPost() - auto& t = frame[z.v1].table_val; - Unref(t); - t = new_t; - -internal-op Construct-Table -type VV -eval ConstructTableOrSetPre() - 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 -type VV -op1-read -eval auto& tbl = frame[z.v1].table_val; - auto lambda = frame[z.v2].ToVal(z.t); - tbl->InitDefaultVal(std::move(lambda)); - -direct-unary-op Set-Constructor ConstructSet - -internal-op Construct-Set -type VV -eval ConstructTableOrSetPre() - 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() - auto& r = frame[z.v1].record_val; - Unref(r); - r = new RecordVal(cast_intrusive(z.t), std::move(init_vals)); - -op Construct-Direct-Record -type V -eval auto init_vals = z.aux->ToZValVec(frame); - ConstructRecordPost() - -op Construct-Known-Record -type V -eval auto init_vals = z.aux->ToZValVecWithMap(frame); - ConstructRecordPost() - -macro AssignFromRec() - /* The following is defined below, for use by Rec-Assign-Fields */ - SetUpRecFieldOps(lhs_map) - auto is_managed = 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 -type VV -eval auto init_vals = z.aux->ToZValVecWithMap(frame); - AssignFromRec() - ConstructRecordPost() - -macro DoNetworkTimeInit(slot) - init_vals[slot] = ZVal(run_state::network_time); - -op Construct-Known-Record-With-NT -type VV -eval auto init_vals = z.aux->ToZValVecWithMap(frame); - DoNetworkTimeInit(z.v2) - ConstructRecordPost() - -op Construct-Known-Record-With-NT-From -type VVV -eval auto init_vals = z.aux->ToZValVecWithMap(frame); - DoNetworkTimeInit(z.v3) - AssignFromRec() - 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 -type V -eval GenInits() - ConstructRecordPost() - -op Construct-Known-Record-With-Inits-From -type VV -eval GenInits() - AssignFromRec() - ConstructRecordPost() - -op Construct-Known-Record-With-Inits-And-NT -type VV -eval GenInits() - DoNetworkTimeInit(z.v2) - ConstructRecordPost() - -op Construct-Known-Record-With-Inits-And-NT-From -type VVV -eval GenInits() - DoNetworkTimeInit(z.v3) - AssignFromRec() - ConstructRecordPost() - -macro SetUpRecFieldOps(which_lhs_map) - auto lhs = frame[z.v1].record_val; - auto rhs = frame[z.v2].record_val; - auto aux = z.aux; - auto& lhs_map = aux->which_lhs_map; - auto& rhs_map = aux->rhs_map; - auto n = rhs_map.size(); - -op Rec-Assign-Fields -op1-read -type VV -eval SetUpRecFieldOps(map) - for ( size_t i = 0U; i < n; ++i ) - lhs->RawOptField(lhs_map[i]) = rhs->RawField(rhs_map[i]); - -macro DoManagedRecAssign() - auto is_managed = 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 -type VV -eval SetUpRecFieldOps(map) - DoManagedRecAssign() - -op Rec-Assign-Fields-All-Managed -op1-read -type VV -eval SetUpRecFieldOps(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 -type VV -eval SetUpRecFieldOps(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 -type VV -eval SetUpRecFieldOps(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 -type VV -eval SetUpRecFieldOps(map) - auto& types = 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 -type V -eval auto rt = cast_intrusive(z.t); - auto r = frame[z.v1].record_val; - 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()->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 -type V -eval auto new_vv = new VectorVal(cast_intrusive(z.t)); - 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 = frame[z.v1].vector_val; - Unref(vv); - vv = new_vv; - -########## Coercions ########## - -direct-unary-op Arith-Coerce ArithCoerce - -internal-op Coerce-UI -type VV -eval auto v = frame[z.v2].int_val; - if ( v < 0 ) - { - ZAM_run_time_error(z.loc, "underflow converting int to count"); - break; - } - frame[z.v1].uint_val = zeek_uint_t(v); - -internal-op Coerce-UD -type VV -eval auto v = frame[z.v2].double_val; - if ( v < 0.0 ) - { - ZAM_run_time_error(z.loc, "underflow converting double to count"); - break; - } - if ( v > static_cast(UINT64_MAX) ) - { - ZAM_run_time_error(z.loc, "overflow converting double to count"); - break; - } - frame[z.v1].uint_val = zeek_uint_t(v); - -internal-op Coerce-IU -type VV -eval auto v = frame[z.v2].uint_val; - if ( v > INT64_MAX ) - { - ZAM_run_time_error(z.loc, "overflow converting count to int"); - break; - } - frame[z.v1].int_val = zeek_int_t(v); - -internal-op Coerce-ID -type VV -eval auto v = frame[z.v2].double_val; - if ( v < static_cast(INT64_MIN) ) - { - ZAM_run_time_error(z.loc, "underflow converting double to int"); - break; - } - if ( v > static_cast(INT64_MAX) ) - { - ZAM_run_time_error(z.loc, "overflow converting double to int"); - break; - } - frame[z.v1].int_val = zeek_int_t(v); - -internal-op Coerce-DI -type VV -eval frame[z.v1].double_val = double(frame[z.v2].int_val); - -internal-op Coerce-DU -type VV -eval frame[z.v1].double_val = double(frame[z.v2].uint_val); - - -macro EvalCoerceVec(coercer) - auto old_v1 = frame[z.v1].vector_val; - frame[z.v1].vector_val = coercer(frame[z.v2].vector_val, z); - Unref(old_v1); // delayed to allow for same value on both sides - -internal-op Coerce-UI-Vec -type VV -eval EvalCoerceVec(vec_coerce_UI) - -internal-op Coerce-UD-Vec -type VV -eval EvalCoerceVec(vec_coerce_UD) - -internal-op Coerce-IU-Vec -type VV -eval EvalCoerceVec(vec_coerce_IU) - -internal-op Coerce-ID-Vec -type VV -eval EvalCoerceVec(vec_coerce_ID) - -internal-op Coerce-DI-Vec -type VV -eval EvalCoerceVec(vec_coerce_DI) - -internal-op Coerce-DU-Vec -type VV -eval EvalCoerceVec(vec_coerce_DU) - - -direct-unary-op Record-Coerce RecordCoerce - -internal-op Record-Coerce -type VV -eval auto rt = cast_intrusive(z.t); - auto v = frame[z.v2].record_val; - auto to_r = coerce_to_record(std::move(rt), v, z.aux->map); - Unref(frame[z.v1].record_val); - frame[z.v1].record_val = to_r.release(); - -direct-unary-op Table-Coerce TableCoerce - -internal-op Table-Coerce -type VV -eval auto tv = frame[z.v2].table_val; - if ( tv->Size() > 0 ) - { - ZAM_run_time_error(z.loc, "coercion of non-empty table/set"); - break; - } - auto tt = cast_intrusive(z.t); - AttributesPtr attrs = tv->GetAttrs(); - auto t = make_intrusive(tt, attrs); - Unref(frame[z.v1].table_val); - frame[z.v1].table_val = t.release(); - -direct-unary-op Vector-Coerce VectorCoerce - -internal-op Vector-Coerce -type VV -eval if ( frame[z.v2].vector_val->Size() > 0 ) - { - ZAM_run_time_error(z.loc, "coercion of non-empty vector"); - break; - } - auto vv = new VectorVal(cast_intrusive(z.t)); - Unref(frame[z.v1].vector_val); - frame[z.v1].vector_val = vv; - -unary-expr-op To-Any-Coerce -op-type X -set-type $1 -eval AssignV1(ZVal(frame[z.v2].ToVal(z.t), ZAM::any_base_type)) - -unary-expr-op From-Any-Coerce -op-type X -set-type $$ -eval auto v = frame[z.v2].any_val; - AssignV1(ZVal({NewRef{}, v}, z.t)) - -unary-expr-op From-Any-Vec-Coerce -op-type X -set-type $$ -eval auto vv = frame[z.v2].vector_val; - if ( ! vv->Concretize(z.t->Yield()) ) - { - ZAM_run_time_error(z.loc, "incompatible vector-of-any"); - break; - } - zeek::Ref(vv); - AssignV1(ZVal(vv)) - - -########## Aggregate Assignments ########## - -macro VectorElemAssignPre() - auto ind = frame[z.v2].uint_val; - auto vv = frame[z.v1].vector_val; - -macro EvalVectorElemAssign(val_setup, assign_op) - VectorElemAssignPre() - val_setup - if ( ! assign_op ) - ZAM_run_time_error(z.loc, "value used but not set"); - -op Vector-Elem-Assign -op1-read -set-type $1 -type VVV -eval EvalVectorElemAssign(, copy_vec_elem(vv, ind, frame[z.v3], z.t)) - -op Any-Vector-Elem-Assign -op1-read -set-type $1 -type VVV -eval EvalVectorElemAssign(, vv->Assign(ind, frame[z.v3].ToVal(z.t))) - -op Vector-Elem-Assign-Any -op1-read -type VVV -eval EvalVectorElemAssign(auto any_v = frame[z.v3].any_val;, vv->Assign(ind, {NewRef{}, any_v})) - -op Vector-Elem-Assign -op1-read -set-type $2 -type VVC -eval VectorElemAssignPre() - (void) copy_vec_elem(vv, ind, z.c, z.t); - -op Any-Vector-Elem-Assign -op1-read -set-type $1 -type VVC -eval VectorElemAssignPre() - if ( ! vv->Assign(ind, z.c.ToVal(z.t)) ) - ZAM_run_time_error(z.loc, "vector index assignment failed for invalid type"); - -# These versions are used when the constant is the index, not the new value. -op Vector-Elem-Assign -op1-read -set-type $1 -type VVi -eval auto vv = frame[z.v1].vector_val; - if ( ! copy_vec_elem(vv, z.v3, frame[z.v2], z.t) ) - ZAM_run_time_error(z.loc, "value used but not set"); - -op Any-Vector-Elem-Assign -op1-read -set-type $1 -type VVi -eval auto vv = frame[z.v1].vector_val; - if ( ! vv->Assign(z.v3, frame[z.v2].ToVal(z.t)) ) - ZAM_run_time_error(z.loc, "value used but not set"); - -op Vector-Elem-Assign-Any -op1-read -type VVi -eval auto vv = frame[z.v1].vector_val; - auto any_v = frame[z.v2].any_val; - vv->Assign(z.v3, {NewRef{}, any_v}); - -internal-op Vector-Slice-Assign -op1-read -type VV -eval ValPtr vec = {NewRef{}, frame[z.v1].vector_val}; - auto slice = z.aux->ToListVal(frame); - ValPtr vals = {NewRef{}, frame[z.v2].vector_val}; - bool iterators_invalidated; - auto error = assign_to_index(std::move(vec), std::move(slice), std::move(vals), iterators_invalidated); - if ( error ) - ZAM_run_time_error(z.loc, error); - if ( iterators_invalidated ) - ZAM_run_time_warning(z.loc, "possible loop/iterator invalidation"); - - -internal-op Table-Elem-Assign -op1-read -type VV -eval EvalTableElemAssign(frame[z.v2]) - -macro EvalTableElemAssign(value) - auto indices = z.aux->ToListVal(frame); - auto val = value.ToVal(z.t); - bool iterators_invalidated = false; - frame[z.v1].table_val->Assign(std::move(indices), std::move(val), true, &iterators_invalidated); - if ( iterators_invalidated ) - ZAM_run_time_warning(z.loc, "possible loop/iterator invalidation"); - -internal-op Table-Elem-Assign -op1-read -type VC -eval EvalTableElemAssign(z.c) - - -########## Function Calls ########## - -# A call with no arguments and no return value. -internal-op Call0 -op1-read -type X -side-effects -num-call-args 0 - -# A call with no arguments and a return value. -internal-assignment-op Call0 -type 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 -type V -side-effects -num-call-args 1 - -internal-op Call1 -op1-read -type C -side-effects -num-call-args 1 - -# Same but with a return value. -internal-assignment-op Call1 -type VV -side-effects OP_CALL1_V OP_V -assign-val v -num-call-args 1 - -internal-assignment-op Call1 -type 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 -type X -side-effects -num-call-args 2 - -# Same with a return value. -internal-assignment-op Call2 -type V -side-effects OP_CALL2_X OP_X -assign-val v -num-call-args 2 - -internal-op Call3 -type X -side-effects -num-call-args 3 - -# Same with a return value. -internal-assignment-op Call3 -type V -side-effects OP_CALL3_X OP_X -assign-val v -num-call-args 3 - -internal-op Call4 -type X -side-effects -num-call-args 4 - -# Same with a return value. -internal-assignment-op Call4 -type V -side-effects OP_CALL4_X OP_X -assign-val v -num-call-args 4 - -internal-op Call5 -type X -side-effects -num-call-args 5 - -# Same with a return value. -internal-assignment-op Call5 -type 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 -type X -side-effects -num-call-args n - -# Same with a return value. -internal-assignment-op CallN -type 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 -type X -side-effects -indirect-call -num-call-args n - -# Same with a return value. -internal-assignment-op IndCallN -type 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 -type V -side-effects -indirect-local-call -num-call-args n - -internal-assignment-op Local-IndCallN -type 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(func) - if ( ! func ) - throw ZAMDelayedCallException(); - auto& lhs = frame[z.v1]; - auto trigger = f->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 = f->GetTriggerAssoc(); - auto n = aux->n; - std::vector args; - for ( auto i = 0; i < n; ++i ) - args.push_back(aux->ToVal(frame, i)); - f->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, f); - ZAM_error = hold_ZAM_error; - f->SetTriggerAssoc(current_assoc); - if ( ! vp ) - throw ZAMDelayedCallException(); - } - if ( z.is_managed ) - ZVal::DeleteManagedType(lhs); - lhs = ZVal(vp, z.t); - -internal-op WhenCallN -type V -side-effects -eval WhenCall(z.aux->func) - -internal-op WhenIndCallN -type VV -side-effects -eval auto sel = z.v2; - auto func = (sel < 0) ? z.aux->id_val->GetVal()->AsFunc() : frame[sel].AsFunc(); - WhenCall(func) - - -########## Statements ########## - -macro EvalScheduleArgs(time, is_delta, build_args) - if ( run_state::terminating ) - break; - double dt = time.double_val; - 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 -type ViHL -op1-read -custom-method return CompileSchedule(n, nullptr, i, h, l); -eval EvalSchedule(frame[z.v1], z.v2) - -op Schedule -type CiHL -op1-read -custom-method return CompileSchedule(nullptr, c, i, h, l); -eval EvalSchedule(z.c, z.v1) - -internal-op Schedule0 -type ViH -op1-read -eval EvalScheduleArgs(frame[z.v1], z.v2,) - -internal-op Schedule0 -type CiH -op1-read -eval EvalScheduleArgs(z.c, z.v1,) - -macro QueueEvent(eh, args) - if ( *eh ) - event_mgr.Enqueue(eh, std::move(args)); - -op Event -type 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 -type X -eval ValVec args(0); - QueueEvent(z.aux->event_handler, args); - -internal-op Event1 -type V -op1-read -eval ValVec args(1); - args[0] = frame[z.v1].ToVal(z.t); - QueueEvent(z.aux->event_handler, args); - -internal-op Event2 -type VV -op1-read -eval ValVec args(2); - args[0] = frame[z.v1].ToVal(z.t); - args[1] = frame[z.v2].ToVal(z.t2); - QueueEvent(z.aux->event_handler, args); - -internal-op Event3 -type VVV -op1-read -eval ValVec args(3); - auto& aux = z.aux; - args[0] = frame[z.v1].ToVal(z.t); - args[1] = frame[z.v2].ToVal(z.t2); - args[2] = frame[z.v3].ToVal(aux->elems[2].GetType()); - QueueEvent(z.aux->event_handler, args); - -internal-op Event4 -type VVVV -op1-read -eval ValVec args(4); - auto& aux = z.aux; - args[0] = frame[z.v1].ToVal(z.t); - args[1] = frame[z.v2].ToVal(z.t2); - args[2] = frame[z.v3].ToVal(aux->elems[2].GetType()); - args[3] = frame[z.v4].ToVal(aux->elems[3].GetType()); - QueueEvent(z.aux->event_handler, args); - - -op Return -type X -eval EvalReturn(nullptr,) - -macro EvalReturn(val, type) - ret_u = val; - type - DO_ZAM_PROFILE - pc = end_pc; - continue; - -op Return -op1-read -type V -set-type $$ -eval EvalReturn(&frame[z.v1], ret_type = z.t;) - -op Return -type C -eval EvalReturn(&z.c, ret_type = z.t;) - - -# Branch on the value of v1 using switch table v2, with default branch to v3 - -macro EvalSwitchBody(cases, postscript) - { - auto t = cases[z.v2]; - if ( t.find(v) == t.end() ) - pc = z.v3; - else - pc = t[v]; - postscript - DO_ZAM_PROFILE - continue; - } - -internal-op SwitchI -type VVV -op1-read -eval auto v = frame[z.v1].int_val; - EvalSwitchBody(int_cases,) - -internal-op SwitchU -op1-read -type VVV -eval auto v = frame[z.v1].uint_val; - EvalSwitchBody(uint_cases,) - -internal-op SwitchD -op1-read -type VVV -eval auto v = frame[z.v1].double_val; - EvalSwitchBody(double_cases,) - -internal-op SwitchS -op1-read -type VVV -eval auto vs = frame[z.v1].string_val->AsString()->Render(); - std::string v(vs); - EvalSwitchBody(str_cases,delete[] vs;) - -internal-op SwitchA -op1-read -type VVV -eval auto v = frame[z.v1].addr_val->AsAddr().AsString(); - EvalSwitchBody(str_cases,) - -internal-op SwitchN -op1-read -type VVV -eval auto v = frame[z.v1].subnet_val->AsSubNet().AsString(); - EvalSwitchBody(str_cases,) - - -internal-op Branch-If-Not-Type -op1-read -type VV -eval auto v = frame[z.v1].any_val; - if ( ! can_cast_value_to_type(v, z.t.get()) ) - BRANCH(v2) - - -internal-op Init-Table-Loop -type VV -op1-read -eval auto& ti = (*tiv_ptr)[z.v2]; - ti.BeginLoop({NewRef{}, frame[z.v1].table_val}, z.aux); - -internal-op Next-Table-Iter -op1-read -# v1 = iteration info -# v2 = branch target if loop done -type VV -eval NextTableIterPre(v1, v2) - ti.NextIter(frame); - -macro NextTableIterPre(iter, branch) - auto& ti = (*tiv_ptr)[z.iter]; - if ( ti.IsDoneIterating() ) - BRANCH(branch) - -internal-op Next-Table-Iter-No-Vars -op1-read -# v1 = iteration info -# v2 = branch target if loop done -type VV -eval NextTableIterPre(v1, v2) - ti.IterFinished(); - -internal-op Next-Table-Iter-Val-Var -# v1 = slot of the "ValueVar" -# v2 = iteration info -# v3 = branch target if loop done -type VVV -eval NextTableIterPre(v2, v3) - AssignV1(ti.IterValue()); - ti.NextIter(frame); - -internal-op Next-Table-Iter-Val-Var-No-Vars -# v1 = slot of the "ValueVar" -# v2 = iteration info -# v3 = branch target if loop done -type VVV -eval NextTableIterPre(v2, v3) - AssignV1(ti.IterValue()); - ti.IterFinished(); - - -internal-op Init-Vector-Loop -type VV -op1-read -eval auto& vv = frame[z.v1].vector_val->RawVec(); - step_iters[z.v2].InitLoop(&vv); - -macro NextVectorIterCore(info, branch) - auto& si = step_iters[info]; - if ( si.IsDoneIterating() ) - BRANCH(branch) - const auto& vv = *si.vv; - if ( ! vv[si.iter] ) - { /* Account for vector hole. Re-execute for next position. */ - si.IterFinished(); - --pc; /* so we then increment to here again */ - break; - } - -internal-op Next-Vector-Iter -# v1 = iteration variable -# v2 = iteration info -# v3 = branch target if loop done -type VVV -eval NextVectorIterCore(z.v2, v3) - frame[z.v1].uint_val = si.iter; - si.IterFinished(); - -internal-op Next-Vector-Blank-Iter -# v1 = iteration info -# v2 = branch target if loop done -op1-internal -type VV -eval NextVectorIterCore(z.v1, v2) - si.IterFinished(); - -internal-op Next-Vector-Iter-Val-Var -# v1 = iteration variable -# v2 = value variable -# v3 = iteration info -# v4 = branch target if loop done -op1-read-write -type VVVV -eval NextVectorIterCore(z.v3, v4) - frame[z.v1].uint_val = si.iter; - if ( z.is_managed ) - frame[z.v2] = BuildVal(vv[si.iter]->ToVal(z.t), z.t); - else - frame[z.v2] = *vv[si.iter]; - si.IterFinished(); - -internal-op Next-Vector-Blank-Iter-Val-Var -# v1 = value variable -# v2 = iteration info -# v3 = branch target if loop done -type VVV -eval NextVectorIterCore(z.v2, v3) - if ( z.is_managed ) - frame[z.v1] = BuildVal(vv[si.iter]->ToVal(z.t), z.t); - else - frame[z.v1] = *vv[si.iter]; - si.IterFinished(); - - -internal-op Init-String-Loop -type VV -op1-read -eval step_iters[z.v2].InitLoop(frame[z.v1].string_val->AsString()); - -internal-op Init-String-Loop -type VC -eval step_iters[z.v1].InitLoop(z.c.string_val->AsString()); - -internal-op Next-String-Iter -# v1 = iteration variable -# v2 = iteration info -# v3 = branch target if loop done -type VVV -eval auto& si = step_iters[z.v2]; - if ( si.IsDoneIterating() ) - BRANCH(v3) - auto bytes = (const char*) si.s->Bytes() + si.iter; - auto sv = new StringVal(1, bytes); - Unref(frame[z.v1].string_val); - frame[z.v1].string_val = sv; - si.IterFinished(); - -internal-op Next-String-Blank-Iter -# v1 = iteration info -# v2 = branch target if loop done -op1-internal -type VV -eval auto& si = step_iters[z.v1]; - if ( si.IsDoneIterating() ) - BRANCH(v2) - si.IterFinished(); - -internal-op End-Table-Loop -op1-internal -type V -eval (*tiv_ptr)[z.v1].Clear(); - - -op CheckAnyLen -op1-read -type Vi -eval auto v = frame[z.v1].list_val; - if ( v->Vals().size() != static_cast(z.v2) ) - ZAM_run_time_error(z.loc, "mismatch in list lengths"); - -op Print -type O -eval do_print_stmt(z.aux->ToValVec(frame)); -method-post z.aux = v->aux; - -op Print1 -op1-read -type V -set-type $$ -eval EvalPrint1(frame[z.v1]) - -macro EvalPrint1(value) - std::vector vals; - vals.push_back(value.ToVal(z.t)); - do_print_stmt(vals); - -op Print1 -op1-read -type C -set-type $$ -eval EvalPrint1(z.c) - - -internal-op If-Else -op1-read -type VV -eval if ( ! frame[z.v1].int_val ) BRANCH(v2) - -internal-op If -op1-read -type VV -eval if ( ! frame[z.v1].int_val ) BRANCH(v2) - -internal-op If-Not -op1-read -type VV -eval if ( frame[z.v1].int_val ) BRANCH(v2) - - -op AddStmt -op1-read -type VO -eval EvalAddStmt(z.aux->ToListVal(frame)) -method-post z.aux = v->aux; - -macro EvalAddStmt(ind) - auto index = ind; - bool iterators_invalidated = false; - frame[z.v1].table_val->Assign(std::move(index), nullptr, true, &iterators_invalidated); - if ( iterators_invalidated ) - ZAM_run_time_warning(z.loc, "possible loop/iterator invalidation"); - -op AddStmt1 -op1-read -set-type $1 -type VV -eval EvalAddStmt(frame[z.v2].ToVal(z.t)) - -op AddStmt1 -op1-read -type VC -eval EvalAddStmt(z.c.ToVal(z.t)) - - -op ClearTable -op1-read -type V -eval frame[z.v1].table_val->RemoveAll(); - -op ClearVector -op1-read -type V -eval frame[z.v1].vector_val->Resize(0); - - -op DelTable -op1-read -type VO -eval auto index = z.aux->ToListVal(frame); - bool iterators_invalidated = false; - frame[z.v1].table_val->Remove(*index, true, &iterators_invalidated); - if ( iterators_invalidated ) - ZAM_run_time_warning(z.loc, "possible loop/iterator invalidation"); -method-post z.aux = v->aux; - -op DelField -op1-read -type Vi -eval frame[z.v1].record_val->Remove(z.v2); - - -internal-op Init-Record -type V -eval auto r = new RecordVal(cast_intrusive(z.t)); - Unref(frame[z.v1].record_val); - frame[z.v1].record_val = r; - -internal-op Init-Vector -type V -eval auto vt = cast_intrusive(z.t); - auto vec = new VectorVal(std::move(vt)); - Unref(frame[z.v1].vector_val); - frame[z.v1].vector_val = vec; - -internal-op Init-Table -type V -eval auto tt = cast_intrusive(z.t); - auto t = new TableVal(tt, z.aux->attrs); - Unref(frame[z.v1].table_val); - frame[z.v1].table_val = t; - -op When -type V -op1-read -eval BuildWhen(-1.0) - -op When-Timeout -type VV -op1-read -eval BuildWhen(frame[z.v2].double_val) - -op When-Timeout -type VC -op1-read -eval BuildWhen(z.c.double_val) - -macro BuildWhen(timeout) - auto& aux = z.aux; - auto wi = aux->wi; - FuncPtr func{NewRef{}, frame[z.v1].func_val}; - auto lambda = make_intrusive(func); - wi->Instantiate(std::move(lambda)); - std::vector 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(wi, wi->WhenExprGlobals(), local_aggrs, timeout, f, z.loc->Loc()); - -######################################## -# Internal -######################################## - -# These two are only needed for type-based switch statements. Could think -# about replacing them using CoerceFromAnyExpr. -op Assign-Any -type VV -set-type $1 -eval EvalAssignAny(frame[z.v2]) - -macro EvalAssignAny(value) - auto v = value.ToVal(z.t); - frame[z.v1].any_val = v.release(); - -op Assign-Any -type VC -set-type $1 -eval EvalAssignAny(z.c) - -# Lazy way to assign without having to track the specific type of -# a constant. -internal-op Assign-Const -type VC -eval AssignV1(BuildVal(z.c.ToVal(z.t), z.t)) - -internal-assignment-op Load-Val -type VV -assign-val v -eval auto& v = f->GetElement(z.v2); - -internal-assignment-op Load-Global -type VV -assign-val v -eval auto& v = globals[z.v2].id->GetVal(); - if ( ! v ) - { - ZAM_run_time_error(z.loc, "value used but not set", z.aux->id_val.get()); - break; - } - -# We need a special form here for loading global types, as they don't -# fit the usual template. -internal-op Load-Global-Type -type VV -eval auto& v = frame[z.v1].type_val; - Unref(v); - auto& t = globals[z.v2].id->GetType(); - v = new TypeVal(t, true); - -internal-op Load-Capture -type VV -eval frame[z.v1] = f->GetFunction()->GetCapturesVec()[z.v2]; - -internal-op Load-Managed-Capture -type VV -eval auto& lhs = frame[z.v1]; - auto& rhs = f->GetFunction()->GetCapturesVec()[z.v2]; - zeek::Ref(rhs.ManagedVal()); - ZVal::DeleteManagedType(lhs); - lhs = rhs; - -internal-op Store-Global -op1-internal -type V -eval auto& g = globals[z.v1]; - g.id->SetVal(frame[g.slot].ToVal(z.t)); - -# 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 -type VV -eval f->GetFunction()->GetCapturesVec()[z.v2] = frame[z.v1]; - -internal-op Store-Managed-Capture -op1-read -type VV -eval auto& lhs = f->GetFunction()->GetCapturesVec()[z.v2]; - auto& rhs = frame[z.v1]; - zeek::Ref(rhs.ManagedVal()); - ZVal::DeleteManagedType(lhs); - lhs = rhs; - - -internal-op Copy-To -type VC -set-type $1 -eval AssignV1(CopyVal(z.c)) - -internal-op GoTo -type V -eval BRANCH(v1) - -internal-op Hook-Break -type X -eval flow = FLOW_BREAK; - pc = end_pc; - DO_ZAM_PROFILE - continue; - -# Slot 2 gives frame size. -internal-op Lambda -type VV -eval auto& aux = z.aux; - auto& primary_func = aux->primary_func; - auto& body = primary_func->GetBodies()[0].stmts; - ASSERT(body->Tag() == STMT_ZAM); - auto lamb = make_intrusive(aux->id_val); - lamb->AddBody(body, z.v2); - lamb->SetName(aux->lambda_name.c_str()); - if ( aux->n > 0 ) - { - auto captures = std::make_unique>(); - 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)); - } - ZVal::DeleteManagedType(frame[z.v1]); - frame[z.v1].func_val = lamb.release(); - -######################################## -# Built-in Functions -######################################## - -macro EvalSubBytes(arg1, arg2, arg3) - { - auto sv = ZAM_sub_bytes(arg1.AsString(), arg2, arg3); - Unref(frame[z.v1].AsString()); - frame[z.v1].string_val = sv; - } - -internal-op Remove-Teredo -op1-read -type V -eval auto teredo = zeek::packet_mgr->GetAnalyzer("Teredo"); - if ( teredo ) - { - zeek::detail::ConnKey conn_key(frame[z.v1].record_val); - static_cast(teredo.get())->RemoveConnection(conn_key); - } - -internal-op Remove-Teredo -side-effects OP_REMOVE_TEREDO_V OP_V -type VV -eval auto teredo = zeek::packet_mgr->GetAnalyzer("Teredo"); - if ( teredo ) - { - zeek::detail::ConnKey conn_key(frame[z.v2].record_val); - static_cast(teredo.get())->RemoveConnection(conn_key); - } - frame[z.v1].int_val = 1; - -internal-op Remove-GTPv1 -op1-read -type V -eval auto gtpv1 = zeek::packet_mgr->GetAnalyzer("GTPv1"); - if ( gtpv1 ) - { - zeek::detail::ConnKey conn_key(frame[z.v1].record_val); - static_cast(gtpv1.get())->RemoveConnection(conn_key); - } - -internal-op Remove-GTPv1 -side-effects OP_REMOVE_GTPV1_V OP_V -type VV -eval auto gtpv1 = zeek::packet_mgr->GetAnalyzer("GTPv1"); - if ( gtpv1 ) - { - zeek::detail::ConnKey conn_key(frame[z.v2].record_val); - static_cast(gtpv1.get())->RemoveConnection(conn_key); - } - frame[z.v1].int_val = 1; - -internal-op Set-File-Handle -op1-read -type V -eval auto handle = frame[z.v1].string_val; - auto bytes = reinterpret_cast(handle->Bytes()); - auto h = std::string(bytes, handle->Len()); - zeek::file_mgr->SetHandle(h); - -internal-op Subnet-To-Addr -type VV -eval auto addr_v = make_intrusive(frame[z.v2].subnet_val->Prefix()); - Unref(frame[z.v1].addr_val); - frame[z.v1] = ZVal(std::move(addr_v)); - -internal-op Sub-Bytes -type VVVV -eval EvalSubBytes(frame[z.v2], frame[z.v3].uint_val, frame[z.v4].int_val) - -internal-op Sub-Bytes -type VVVi -eval EvalSubBytes(frame[z.v2], frame[z.v3].uint_val, z.c.int_val) - -internal-op Sub-Bytes -type VViV -eval EvalSubBytes(frame[z.v2], z.c.uint_val, frame[z.v3].int_val) - -internal-op Sub-Bytes -type VVii -eval EvalSubBytes(frame[z.v2], z.c.uint_val, z.v3) - -internal-op Sub-Bytes -type VVVC -eval EvalSubBytes(z.c, frame[z.v2].uint_val, frame[z.v3].uint_val) - -internal-op Sub-Bytes -type VViC -eval EvalSubBytes(z.c, frame[z.v2].uint_val, z.v3) - -internal-op Sub-Bytes -type ViVC -eval EvalSubBytes(z.c, zeek_uint_t(z.v3), frame[z.v2].uint_val) - -internal-op Time-To-Double -type VV -eval frame[z.v1] = frame[z.v2]; - - -internal-op To-Lower -type VV -eval auto sv = ZAM_to_lower(frame[z.v2].string_val); - Unref(frame[z.v1].string_val); - frame[z.v1].string_val = 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_slot) - auto id = id_val.ToVal(ZAM::log_ID_enum_type); - auto columns = frame[z.columns_slot].record_val; - -macro LogWriteResPost() - bool result = log_mgr->Write(id->AsEnumVal(), columns->AsRecordVal()); - frame[z.v1].int_val = result; - -macro LogWriteNoResPost() - (void) log_mgr->Write(id->AsEnumVal(), columns->AsRecordVal()); - -internal-op Log-Write -side-effects OP_LOG_WRITE_VV OP_VV -type VVV -eval LogWritePre(frame[z.v2], v3) - LogWriteResPost() - -internal-op Log-WriteC -side-effects OP_LOG_WRITEC_V OP_V -type VV -eval LogWritePre(z.c, v2) - LogWriteResPost() - -# Versions that discard the return value. -internal-op Log-Write -side-effects -op1-read -type VV -eval LogWritePre(frame[z.v1], v2) - LogWriteNoResPost() - -internal-op Log-WriteC -side-effects -op1-read -type V -eval LogWritePre(z.c, v1) - LogWriteNoResPost() - -internal-op Broker-Flush-Logs -side-effects OP_BROKER_FLUSH_LOGS_X OP_X -type V -eval frame[z.v1].uint_val = broker_mgr->FlushLogBuffers(); - -internal-op Broker-Flush-Logs -side-effects -type X -eval (void) broker_mgr->FlushLogBuffers(); - -internal-op Get-Port-Transport-Proto -type VV -eval auto mask = frame[z.v2].uint_val & 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; - frame[z.v1].uint_val = v; - -internal-op Conn-Exists -type VV -eval frame[z.v1].int_val = session_mgr->FindConnection(frame[z.v2].record_val) != nullptr; - -internal-op Conn-Exists-Cond -op1-read -type VV -eval if ( ! session_mgr->FindConnection(frame[z.v1].record_val) ) - BRANCH(v2) - -internal-op Not-Conn-Exists-Cond -op1-read -type VV -eval if ( session_mgr->FindConnection(frame[z.v1].record_val) ) - BRANCH(v2) - -internal-op Lookup-Conn -type VV -eval auto cid = frame[z.v2].record_val; - Connection* conn = session_mgr->FindConnection(cid); - ValPtr res; - if ( conn ) - res = conn->GetVal(); - else - { - ZAM_run_time_error(z.loc, "connection ID not a known connection", cid); - res = build_dummy_conn_record(); - } - AssignV1(ZVal(res, res->GetType())); - -internal-op Is-ICMP-Port -type VV -eval frame[z.v1].int_val = (frame[z.v2].uint_val & PORT_SPACE_MASK) == ICMP_PORT_MASK; - -internal-op Is-ICMP-Port-Cond -op1-read -type VV -eval if ( (frame[z.v1].uint_val & PORT_SPACE_MASK) != ICMP_PORT_MASK ) - BRANCH(v2) - -internal-op Not-Is-ICMP-Port-Cond -op1-read -type VV -eval if ( (frame[z.v1].uint_val & PORT_SPACE_MASK) == ICMP_PORT_MASK ) - BRANCH(v2) - -internal-op Is-TCP-Port -type VV -eval frame[z.v1].int_val = (frame[z.v2].uint_val & PORT_SPACE_MASK) == TCP_PORT_MASK; - -internal-op Is-TCP-Port-Cond -op1-read -type VV -eval if ( (frame[z.v1].uint_val & PORT_SPACE_MASK) != TCP_PORT_MASK ) - BRANCH(v2) - -internal-op Not-Is-TCP-Port-Cond -op1-read -type VV -eval if ( (frame[z.v1].uint_val & PORT_SPACE_MASK) == TCP_PORT_MASK ) - BRANCH(v2) - -internal-op Is-UDP-Port -type VV -eval frame[z.v1].int_val = (frame[z.v2].uint_val & PORT_SPACE_MASK) == UDP_PORT_MASK; - -internal-op Is-UDP-Port-Cond -op1-read -type VV -eval if ( (frame[z.v1].uint_val & PORT_SPACE_MASK) != UDP_PORT_MASK ) - BRANCH(v2) - -internal-op Not-Is-UDP-Port-Cond -op1-read -type VV -eval if ( (frame[z.v1].uint_val & PORT_SPACE_MASK) == UDP_PORT_MASK ) - BRANCH(v2) - -internal-op Is-V4-Addr -type VV -eval frame[z.v1].int_val = frame[z.v2].addr_val->AsAddr().GetFamily() == IPv4; - -internal-op Is-V4-Addr-Cond -op1-read -type VV -eval if ( frame[z.v1].addr_val->AsAddr().GetFamily() != IPv4 ) - BRANCH(v2) - -internal-op Not-Is-V4-Addr-Cond -op1-read -type VV -eval if ( frame[z.v1].addr_val->AsAddr().GetFamily() == IPv4 ) - BRANCH(v2) - -internal-op Is-V6-Addr -type VV -eval frame[z.v1].int_val = frame[z.v2].addr_val->AsAddr().GetFamily() == IPv6; - -internal-op Is-V6-Addr-Cond -op1-read -type VV -eval if ( frame[z.v1].addr_val->AsAddr().GetFamily() != IPv6 ) - BRANCH(v2) - -internal-op Not-Is-V6-Addr-Cond -op1-read -type VV -eval if ( frame[z.v1].addr_val->AsAddr().GetFamily() == IPv6 ) - BRANCH(v2) - -internal-op Network-Time -type V -eval frame[z.v1].double_val = run_state::network_time; - -internal-op Current-Time -type V -eval frame[z.v1].double_val = util::current_time(); - -internal-op Reading-Live-Traffic -type V -eval frame[z.v1].int_val = run_state::reading_live; - -internal-op Reading-Live-Traffic-Cond -op1-read -type V -eval if ( ! run_state::reading_live ) - BRANCH(v1) - -internal-op Not-Reading-Live-Traffic-Cond -op1-read -type V -eval if ( run_state::reading_live ) - BRANCH(v1) - -internal-op Reading-Traces -type V -eval frame[z.v1].int_val = run_state::reading_traces; - -internal-op Reading-Traces-Cond -op1-read -type V -eval if ( ! run_state::reading_traces ) - BRANCH(v1) - -internal-op Not-Reading-Traces-Cond -op1-read -type V -eval if ( run_state::reading_traces ) - BRANCH(v1) - -internal-op Sort -op1-read -type V -eval if ( frame[z.v1].vector_val->Size() > 1 ) - frame[z.v1].vector_val->Sort(); - -internal-op Sort -type VV -eval auto vv = frame[z.v2].vector_val; - if ( vv->Size() > 1 ) - vv->Sort(); - zeek::Ref(vv); - Unref(frame[z.v1].vector_val); - frame[z.v1].vector_val = vv; - -internal-op Sort-With-Cmp -op1-read -type VV -eval if ( frame[z.v1].vector_val->Size() > 1 ) - frame[z.v1].vector_val->Sort(frame[z.v2].func_val); - -internal-op Sort-With-Cmp -type VVV -eval auto vv = frame[z.v2].vector_val; - if ( vv->Size() > 1 ) - vv->Sort(frame[z.v3].func_val); - zeek::Ref(vv); - Unref(frame[z.v1].vector_val); - frame[z.v1].vector_val = vv; - -macro EvalStartsWith(str_val, sub_val) - auto str = str_val.string_val; - auto sub = sub_val.string_val; - auto str_n = str->Len(); - auto sub_n = sub->Len(); - if ( str_n < sub_n ) - frame[z.v1].int_val = 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; - frame[z.v1].int_val = i == sub_n; - } - -internal-op Starts-With -type VVV -eval EvalStartsWith(frame[z.v2], frame[z.v3]) - -internal-op Starts-With -type VCV -eval EvalStartsWith(z.c, frame[z.v2]) - -internal-op Starts-With -type VVC -eval EvalStartsWith(frame[z.v2], z.c) - -macro EvalStrCmp(s1_src, s2_src) - auto s1 = s1_src.string_val; - auto s2 = s2_src.string_val; - frame[z.v1].int_val = Bstr_cmp(s1->AsString(), s2->AsString()); - -internal-op StrCmp -type VVV -eval EvalStrCmp(frame[z.v2], frame[z.v3]) - -internal-op StrCmp -type VCV -eval EvalStrCmp(z.c, frame[z.v2]) - -internal-op StrCmp -type VVC -eval EvalStrCmp(frame[z.v2], z.c) - -macro EvalStrStr(big_value, little_value) - auto big = big_value.string_val; - auto little = little_value.string_val; - frame[z.v1].int_val = 1 + big->AsString()->FindSubstring(little->AsString()); - -internal-op StrStr -type VVV -eval EvalStrStr(frame[z.v2], frame[z.v3]) - -internal-op StrStr -type VCV -eval EvalStrStr(z.c, frame[z.v2]) - -internal-op StrStr -type VVC -eval EvalStrStr(frame[z.v2], z.c) - -macro Cat1Op(val) - auto& v1 = frame[z.v1]; - ZVal::DeleteManagedType(v1); - v1 = val; - -macro Cat1OpRef(val) - Cat1Op(val) - zeek::Ref(v1.string_val); - -internal-op Cat1 -type VC -eval Cat1OpRef(z.c) - -internal-op Cat1 -type VV -eval Cat1OpRef(frame[z.v2]) - -macro Cat1FullVal(val) - auto formatted_val = ZVal(ZAM_val_cat(val.ToVal(z.t))); - Cat1Op(formatted_val) - -internal-op Cat1Full -type VC -eval Cat1FullVal(z.c) - -internal-op Cat1Full -type VV -eval Cat1FullVal(frame[z.v2]) - -internal-op CatN -type V -eval auto aux = z.aux; - auto& ca = aux->cat_args; - int n = aux->n; - size_t max_size = 0; - for ( int i = 0; i < n; ++i ) - max_size += ca[i]->MaxSize(frame, aux->elems[i].Slot()); - auto res = new char[max_size + /* slop */ n + 1]; - auto res_p = res; - for ( int i = 0; i < n; ++i ) - ca[i]->RenderInto(frame, aux->elems[i].Slot(), res_p); - *res_p = '\0'; - auto s = new String(true, reinterpret_cast(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() - *res_p = '\0'; - auto s = new String(true, reinterpret_cast(res), res_p - res); - Cat1Op(ZVal(new StringVal(s))) - -internal-op Cat2 -type V -eval CatNPre() - size_t max_size = ca[0]->MaxSize(frame, aux->elems[0].Slot()); - max_size += ca[1]->MaxSize(frame, aux->elems[1].Slot()); - CatNMid() - ca[0]->RenderInto(frame, aux->elems[0].Slot(), res_p); - ca[1]->RenderInto(frame, aux->elems[1].Slot(), res_p); - CatNPost() - -internal-op Cat3 -type V -eval CatNPre() - size_t max_size = ca[0]->MaxSize(frame, aux->elems[0].Slot()); - max_size += ca[1]->MaxSize(frame, aux->elems[1].Slot()); - max_size += ca[2]->MaxSize(frame, aux->elems[2].Slot()); - CatNMid() - ca[0]->RenderInto(frame, aux->elems[0].Slot(), res_p); - ca[1]->RenderInto(frame, aux->elems[1].Slot(), res_p); - ca[2]->RenderInto(frame, aux->elems[2].Slot(), res_p); - CatNPost() - -internal-op Cat4 -type V -eval CatNPre() - size_t max_size = ca[0]->MaxSize(frame, aux->elems[0].Slot()); - max_size += ca[1]->MaxSize(frame, aux->elems[1].Slot()); - max_size += ca[2]->MaxSize(frame, aux->elems[2].Slot()); - max_size += ca[3]->MaxSize(frame, aux->elems[3].Slot()); - CatNMid() - ca[0]->RenderInto(frame, aux->elems[0].Slot(), res_p); - ca[1]->RenderInto(frame, aux->elems[1].Slot(), res_p); - ca[2]->RenderInto(frame, aux->elems[2].Slot(), res_p); - ca[3]->RenderInto(frame, aux->elems[3].Slot(), res_p); - CatNPost() - -internal-op Cat5 -type V -eval CatNPre() - size_t max_size = ca[0]->MaxSize(frame, aux->elems[0].Slot()); - max_size += ca[1]->MaxSize(frame, aux->elems[1].Slot()); - max_size += ca[2]->MaxSize(frame, aux->elems[2].Slot()); - max_size += ca[3]->MaxSize(frame, aux->elems[3].Slot()); - max_size += ca[4]->MaxSize(frame, aux->elems[4].Slot()); - CatNMid() - ca[0]->RenderInto(frame, aux->elems[0].Slot(), res_p); - ca[1]->RenderInto(frame, aux->elems[1].Slot(), res_p); - ca[2]->RenderInto(frame, aux->elems[2].Slot(), res_p); - ca[3]->RenderInto(frame, aux->elems[3].Slot(), res_p); - ca[4]->RenderInto(frame, aux->elems[4].Slot(), res_p); - CatNPost() - -internal-op Cat6 -type V -eval CatNPre() - size_t max_size = ca[0]->MaxSize(frame, aux->elems[0].Slot()); - max_size += ca[1]->MaxSize(frame, aux->elems[1].Slot()); - max_size += ca[2]->MaxSize(frame, aux->elems[2].Slot()); - max_size += ca[3]->MaxSize(frame, aux->elems[3].Slot()); - max_size += ca[4]->MaxSize(frame, aux->elems[4].Slot()); - max_size += ca[5]->MaxSize(frame, aux->elems[5].Slot()); - CatNMid() - ca[0]->RenderInto(frame, aux->elems[0].Slot(), res_p); - ca[1]->RenderInto(frame, aux->elems[1].Slot(), res_p); - ca[2]->RenderInto(frame, aux->elems[2].Slot(), res_p); - ca[3]->RenderInto(frame, aux->elems[3].Slot(), res_p); - ca[4]->RenderInto(frame, aux->elems[4].Slot(), res_p); - ca[5]->RenderInto(frame, aux->elems[5].Slot(), res_p); - CatNPost() - -internal-op Cat7 -type V -eval CatNPre() - size_t max_size = ca[0]->MaxSize(frame, aux->elems[0].Slot()); - max_size += ca[1]->MaxSize(frame, aux->elems[1].Slot()); - max_size += ca[2]->MaxSize(frame, aux->elems[2].Slot()); - max_size += ca[3]->MaxSize(frame, aux->elems[3].Slot()); - max_size += ca[4]->MaxSize(frame, aux->elems[4].Slot()); - max_size += ca[5]->MaxSize(frame, aux->elems[5].Slot()); - max_size += ca[6]->MaxSize(frame, aux->elems[6].Slot()); - CatNMid() - ca[0]->RenderInto(frame, aux->elems[0].Slot(), res_p); - ca[1]->RenderInto(frame, aux->elems[1].Slot(), res_p); - ca[2]->RenderInto(frame, aux->elems[2].Slot(), res_p); - ca[3]->RenderInto(frame, aux->elems[3].Slot(), res_p); - ca[4]->RenderInto(frame, aux->elems[4].Slot(), res_p); - ca[5]->RenderInto(frame, aux->elems[5].Slot(), res_p); - ca[6]->RenderInto(frame, aux->elems[6].Slot(), res_p); - CatNPost() - -internal-op Cat8 -type V -eval CatNPre() - size_t max_size = ca[0]->MaxSize(frame, aux->elems[0].Slot()); - max_size += ca[1]->MaxSize(frame, aux->elems[1].Slot()); - max_size += ca[2]->MaxSize(frame, aux->elems[2].Slot()); - max_size += ca[3]->MaxSize(frame, aux->elems[3].Slot()); - max_size += ca[4]->MaxSize(frame, aux->elems[4].Slot()); - max_size += ca[5]->MaxSize(frame, aux->elems[5].Slot()); - max_size += ca[6]->MaxSize(frame, aux->elems[6].Slot()); - max_size += ca[7]->MaxSize(frame, aux->elems[7].Slot()); - CatNMid() - ca[0]->RenderInto(frame, aux->elems[0].Slot(), res_p); - ca[1]->RenderInto(frame, aux->elems[1].Slot(), res_p); - ca[2]->RenderInto(frame, aux->elems[2].Slot(), res_p); - ca[3]->RenderInto(frame, aux->elems[3].Slot(), res_p); - ca[4]->RenderInto(frame, aux->elems[4].Slot(), res_p); - ca[5]->RenderInto(frame, aux->elems[5].Slot(), res_p); - ca[6]->RenderInto(frame, aux->elems[6].Slot(), res_p); - ca[7]->RenderInto(frame, aux->elems[7].Slot(), res_p); - CatNPost() - -macro AnalyzerName(tag) - auto atype = tag.ToVal(z.t); - auto val = atype->AsEnumVal(); - Unref(frame[z.v1].string_val); - 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 ) - frame[z.v1].string_val = new StringVal(component->CanonicalName()); - else - frame[z.v1].string_val = new StringVal(""); - -internal-op Analyzer-Name -type VV -eval AnalyzerName(frame[z.v2]) - -internal-op Analyzer-Name -type VC -eval AnalyzerName(z.c) - -macro FilesAddOrRemoveAnalyzer(file_id_slot, tag, args_slot, METHOD) - auto file_id = frame[z.file_id_slot].string_val; - using zeek::BifType::Record::Files::AnalyzerArgs; - auto rv = frame[z.args_slot].record_val->CoerceTo(AnalyzerArgs); - bool result = zeek::file_mgr->METHOD( - file_id->CheckString(), - zeek::file_mgr->GetComponentTag(tag.ToVal(z.t).get()), - std::move(rv)); - -macro FilesAddAnalyzer(file_id_slot, tag, args_slot) - FilesAddOrRemoveAnalyzer(file_id_slot, tag, args_slot, AddAnalyzer) - -internal-op Files-Add-Analyzer -op1-read -type VVV -eval FilesAddAnalyzer(v1, frame[z.v2], v3) - -internal-op Files-Add-Analyzer -op1-read -type ViV -eval FilesAddAnalyzer(v1, z.c, v2) - -internal-op Files-Add-Analyzer -type VVVV -side-effects OP_FILES_ADD_ANALYZER_VVV OP_VVV -eval FilesAddAnalyzer(v2, frame[z.v3], v4) - frame[z.v1].int_val = result; - -internal-op Files-Add-Analyzer -type VViV -side-effects OP_FILES_ADD_ANALYZER_ViV OP_VVC -eval FilesAddAnalyzer(v2, z.c, v3) - frame[z.v1].int_val = result; - -macro FilesRemoveAnalyzer(file_id_slot, tag, args_slot) - FilesAddOrRemoveAnalyzer(file_id_slot, tag, args_slot, RemoveAnalyzer) - -internal-op Files-Remove-Analyzer -op1-read -type VVV -eval FilesRemoveAnalyzer(v1, frame[z.v2], v3) - -internal-op Files-Remove-Analyzer -op1-read -type ViV -eval FilesRemoveAnalyzer(v1, z.c, v2) - -internal-op Files-Remove-Analyzer -type VVVV -side-effects OP_FILES_REMOVE_ANALYZER_VVV OP_VVV -eval FilesRemoveAnalyzer(v2, frame[z.v3], v4) - frame[z.v1].int_val = result; - -internal-op Files-Remove-Analyzer -type VViV -side-effects OP_FILES_REMOVE_ANALYZER_ViV OP_VVC -eval FilesRemoveAnalyzer(v2, z.c, v3) - frame[z.v1].int_val = result; - -macro AnalyzerEnabled(tag) - auto atype = tag.ToVal(z.t); - auto c = zeek::file_mgr->Lookup(atype->AsEnumVal()); - frame[z.v1].int_val = c && c->Enabled(); - -internal-op Analyzer-Enabled -type VV -eval AnalyzerEnabled(frame[z.v2]) - -internal-op Analyzer-Enabled -type VC -eval AnalyzerEnabled(z.c) - -macro FileAnalyzerName(tag) - auto atype = tag.ToVal(z.t); - Unref(frame[z.v1].string_val); - frame[z.v1] = ZVal(file_mgr->GetComponentNameVal({NewRef{}, atype->AsEnumVal()})); - -internal-op File-Analyzer-Name -type VV -eval FileAnalyzerName(frame[z.v2]) - -internal-op File-Analyzer-Name -type VC -eval FileAnalyzerName(z.c) - -macro IsProtocolAnalyzer(tag) - auto atype = tag.ToVal(z.t); - frame[z.v1].int_val = analyzer_mgr->Lookup(atype->AsEnumVal()) != nullptr; - -internal-op Is-Protocol-Analyzer -type VV -eval IsProtocolAnalyzer(frame[z.v2]) - -internal-op Is-Protocol-Analyzer -type VC -eval IsProtocolAnalyzer(z.c) - -internal-op Clear-Table -op1-read -type V -eval frame[z.v1].table_val->RemoveAll(); - -internal-op Files-Enable-Reassembly -op1-read -type V -eval auto f = frame[z.v1].string_val->CheckString(); - file_mgr->EnableReassembly(f); - -internal-op Files-Set-Reassembly-Buffer -op1-read -type VV -eval auto f = frame[z.v1].string_val->CheckString(); - file_mgr->SetReassemblyBuffer(f, frame[z.v2].uint_val); - -internal-op Files-Set-Reassembly-Buffer -type VVV -side-effects OP_FILES_SET_REASSEMBLY_BUFFER_VV OP_VV -eval auto f = frame[z.v2].string_val->CheckString(); - frame[z.v1].int_val = file_mgr->SetReassemblyBuffer(f, frame[z.v3].uint_val); - -internal-op Files-Set-Reassembly-Buffer -op1-read -type VC -eval auto f = frame[z.v1].string_val->CheckString(); - file_mgr->SetReassemblyBuffer(f, zeek_uint_t(z.v2)); - -internal-op Files-Set-Reassembly-Buffer -type VVC -side-effects OP_FILES_SET_REASSEMBLY_BUFFER_VC OP_VC -eval auto f = frame[z.v2].string_val->CheckString(); - frame[z.v1].int_val = file_mgr->SetReassemblyBuffer(f, zeek_uint_t(z.v2)); - -macro GetBytesThresh(cid, is_orig) - zeek::analyzer::Analyzer* a = analyzer::conn_size::GetConnsizeAnalyzer(cid); - auto res = 0U; - if ( a ) - res = static_cast(a)->GetByteAndPacketThreshold(true, is_orig); - frame[z.v1].uint_val = res; - -internal-op Get-Bytes-Thresh -type VVV -eval GetBytesThresh(frame[z.v2].record_val, frame[z.v3].int_val) - -internal-op Get-Bytes-Thresh -type VVi -eval GetBytesThresh(frame[z.v2].record_val, z.c.uint_val) - -macro SetBytesThresh(cid, threshold, is_orig) - bool res = false; - zeek::analyzer::Analyzer* a = analyzer::conn_size::GetConnsizeAnalyzer(cid); - if ( a ) - { - static_cast(a)->SetByteAndPacketThreshold(threshold, true, is_orig); - res = true; - } - -internal-op Set-Bytes-Thresh -op1-read -type VVV -eval SetBytesThresh(frame[z.v1].record_val, frame[z.v2].uint_val, frame[z.v3].int_val) - -internal-op Set-Bytes-Thresh -op1-read -type VVi -eval SetBytesThresh(frame[z.v1].record_val, frame[z.v2].uint_val, z.c.int_val) - -internal-op Set-Bytes-Thresh -op1-read -type ViV -eval SetBytesThresh(frame[z.v1].record_val, z.c.uint_val, frame[z.v2].int_val) - -internal-op Set-Bytes-Thresh -op1-read -type Vii -eval SetBytesThresh(frame[z.v1].record_val, z.c.uint_val, z.v2) - -internal-op Set-Bytes-Thresh -type VVVV -side-effects OP_SET_BYTES_THRESH_VVV OP_VVV -eval SetBytesThresh(frame[z.v2].record_val, frame[z.v3].uint_val, frame[z.v4].int_val) - frame[z.v1].int_val = res; - -internal-op Set-Bytes-Thresh -type VVVi -side-effects OP_SET_BYTES_THRESH_VVi OP_VVV_I3 -eval SetBytesThresh(frame[z.v2].record_val, frame[z.v3].uint_val, z.c.int_val) - frame[z.v1].int_val = res; - -internal-op Set-Bytes-Thresh -type VViV -side-effects OP_SET_BYTES_THRESH_ViV OP_VVV_I3 -eval SetBytesThresh(frame[z.v2].record_val, z.c.uint_val, frame[z.v3].int_val) - frame[z.v1].int_val = res; - -internal-op Set-Bytes-Thresh -type VVii -side-effects OP_SET_BYTES_THRESH_Vii OP_VVC_I2 -eval SetBytesThresh(frame[z.v2].record_val, z.c.uint_val, zeek_uint_t(z.v3)) - frame[z.v1].int_val = res; - -######################################## -# Instructions for known script functions -######################################## - -internal-op Func-Id-String -type VV -eval auto id_rec = frame[z.v2].record_val; - auto orig_h = id_rec->RawField(0).addr_val->AsAddr().AsString(); - auto resp_h = id_rec->RawField(2).addr_val->AsAddr().AsString(); - auto orig_p = static_cast(id_rec->RawField(1).uint_val) & ~PORT_SPACE_MASK; - auto resp_p = static_cast(id_rec->RawField(3).uint_val) & ~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(frame[z.v1].string_val); - frame[z.v1].string_val = new StringVal(buf); - -######################################## -# Instructions for script-level idioms -######################################## - -internal-op MinU -type VVC -eval frame[z.v1].uint_val = std::min(frame[z.v2].uint_val, z.c.uint_val); - -internal-op MinI -type VVC -eval frame[z.v1].int_val = std::min(frame[z.v2].int_val, z.c.int_val); - -internal-op MinD -type VVC -eval frame[z.v1].double_val = std::min(frame[z.v2].double_val, z.c.double_val); - -internal-op MinU -type VVV -eval frame[z.v1].uint_val = std::min(frame[z.v2].uint_val, frame[z.v3].uint_val); - -internal-op MinI -type VVV -eval frame[z.v1].int_val = std::min(frame[z.v2].int_val, frame[z.v3].int_val); - -internal-op MinD -type VVV -eval frame[z.v1].double_val = std::min(frame[z.v2].double_val, frame[z.v3].double_val); - -internal-op MaxU -type VVC -eval frame[z.v1].uint_val = std::max(frame[z.v2].uint_val, z.c.uint_val); - -internal-op MaxI -type VVC -eval frame[z.v1].int_val = std::max(frame[z.v2].int_val, z.c.int_val); - -internal-op MaxD -type VVC -eval frame[z.v1].double_val = std::max(frame[z.v2].double_val, z.c.double_val); - -internal-op MaxU -type VVV -eval frame[z.v1].uint_val = std::max(frame[z.v2].uint_val, frame[z.v3].uint_val); - -internal-op MaxI -type VVV -eval frame[z.v1].int_val = std::max(frame[z.v2].int_val, frame[z.v3].int_val); - -internal-op MaxD -type VVV -eval frame[z.v1].double_val = std::max(frame[z.v2].double_val, frame[z.v3].double_val); diff --git a/src/script_opt/ZAM/OPs/ZBI.op b/src/script_opt/ZAM/OPs/ZBI.op new file mode 100644 index 0000000000..29d50f13f0 --- /dev/null +++ b/src/script_opt/ZAM/OPs/ZBI.op @@ -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(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(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(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(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(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($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(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(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(""); + +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(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(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; diff --git a/src/script_opt/ZAM/OPs/aggr-assignments.op b/src/script_opt/ZAM/OPs/aggr-assignments.op new file mode 100644 index 0000000000..c64b0077a6 --- /dev/null +++ b/src/script_opt/ZAM/OPs/aggr-assignments.op @@ -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"); diff --git a/src/script_opt/ZAM/OPs/binary-exprs.op b/src/script_opt/ZAM/OPs/binary-exprs.op new file mode 100644 index 0000000000..17d90f9979 --- /dev/null +++ b/src/script_opt/ZAM/OPs/binary-exprs.op @@ -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 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($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(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 diff --git a/src/script_opt/ZAM/OPs/calls.op b/src/script_opt/ZAM/OPs/calls.op new file mode 100644 index 0000000000..5fcbdda607 --- /dev/null +++ b/src/script_opt/ZAM/OPs/calls.op @@ -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 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()) diff --git a/src/script_opt/ZAM/OPs/coercions.op b/src/script_opt/ZAM/OPs/coercions.op new file mode 100644 index 0000000000..fef6ea8096 --- /dev/null +++ b/src/script_opt/ZAM/OPs/coercions.op @@ -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(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(INT64_MIN) ) + ERROR("underflow converting double to int"); + else if ( v > static_cast(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(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(Z_TYPE); + AttributesPtr attrs = tv->GetAttrs(); + auto t = make_intrusive(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(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)) + } diff --git a/src/script_opt/ZAM/OPs/constructors.op b/src/script_opt/ZAM/OPs/constructors.op new file mode 100644 index 0000000000..c55b81c6bc --- /dev/null +++ b/src/script_opt/ZAM/OPs/constructors.op @@ -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(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(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(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()->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(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; diff --git a/src/script_opt/ZAM/OPs/indexing.op b/src/script_opt/ZAM/OPs/indexing.op new file mode 100644 index 0000000000..85f3691150 --- /dev/null +++ b/src/script_opt/ZAM/OPs/indexing.op @@ -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(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(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; diff --git a/src/script_opt/ZAM/OPs/internal.op b/src/script_opt/ZAM/OPs/internal.op new file mode 100644 index 0000000000..dd29579ee6 --- /dev/null +++ b/src/script_opt/ZAM/OPs/internal.op @@ -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(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>(); + 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(); diff --git a/src/script_opt/ZAM/OPs/iterations.op b/src/script_opt/ZAM/OPs/iterations.op new file mode 100644 index 0000000000..92c4895b62 --- /dev/null +++ b/src/script_opt/ZAM/OPs/iterations.op @@ -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(); diff --git a/src/script_opt/ZAM/OPs/macros.op b/src/script_opt/ZAM/OPs/macros.op new file mode 100644 index 0000000000..92d2eedba5 --- /dev/null +++ b/src/script_opt/ZAM/OPs/macros.op @@ -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) diff --git a/src/script_opt/ZAM/OPs/non-uniform.op b/src/script_opt/ZAM/OPs/non-uniform.op new file mode 100644 index 0000000000..f53cc6f57a --- /dev/null +++ b/src/script_opt/ZAM/OPs/non-uniform.op @@ -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()->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($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>(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(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)) diff --git a/src/script_opt/ZAM/OPs/rel-exprs.op b/src/script_opt/ZAM/OPs/rel-exprs.op new file mode 100644 index 0000000000..d8fdd4d94b --- /dev/null +++ b/src/script_opt/ZAM/OPs/rel-exprs.op @@ -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() diff --git a/src/script_opt/ZAM/OPs/script-idioms.op b/src/script_opt/ZAM/OPs/script-idioms.op new file mode 100644 index 0000000000..040e4bfab5 --- /dev/null +++ b/src/script_opt/ZAM/OPs/script-idioms.op @@ -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(DirectField(id_rec, 1).AsCount()) & ~PORT_SPACE_MASK; + auto resp_p = static_cast(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); diff --git a/src/script_opt/ZAM/OPs/stmts.op b/src/script_opt/ZAM/OPs/stmts.op new file mode 100644 index 0000000000..37d99b6c71 --- /dev/null +++ b/src/script_opt/ZAM/OPs/stmts.op @@ -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 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(Z_TYPE)); + Unref($$); + $$ = r; + +internal-op Init-Vector +class V +op-types V +eval auto vt = cast_intrusive(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(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(func); + wi->Instantiate(std::move(lambda)); + std::vector 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(wi, wi->WhenExprGlobals(), local_aggrs, timeout, Z_FRAME, Z_LOC->Loc()); diff --git a/src/script_opt/ZAM/OPs/unary-exprs.op b/src/script_opt/ZAM/OPs/unary-exprs.op new file mode 100644 index 0000000000..a44cbe937d --- /dev/null +++ b/src/script_opt/ZAM/OPs/unary-exprs.op @@ -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());