mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 06:38:20 +00:00
861 lines
25 KiB
C++
861 lines
25 KiB
C++
// See the file "COPYING" in the main distribution directory for copyright.
|
|
|
|
#include "pac_expr.h"
|
|
|
|
#include "pac_case.h"
|
|
#include "pac_cstr.h"
|
|
#include "pac_exception.h"
|
|
#include "pac_exttype.h"
|
|
#include "pac_id.h"
|
|
#include "pac_let.h"
|
|
#include "pac_nullptr.h"
|
|
#include "pac_number.h"
|
|
#include "pac_output.h"
|
|
#include "pac_record.h"
|
|
#include "pac_regex.h"
|
|
#include "pac_typedecl.h"
|
|
#include "pac_utils.h"
|
|
|
|
string OrigExprList(ExprList* list) {
|
|
bool first = true;
|
|
string str;
|
|
foreach (i, ExprList, list) {
|
|
Expr* expr = *i;
|
|
if ( first )
|
|
first = false;
|
|
else
|
|
str += ", ";
|
|
str += expr->orig();
|
|
}
|
|
return str;
|
|
}
|
|
|
|
string EvalExprList(ExprList* exprlist, Output* out, Env* env) {
|
|
string val_list("");
|
|
bool first = true;
|
|
|
|
foreach (i, ExprList, exprlist) {
|
|
if ( ! first )
|
|
val_list += ", ";
|
|
val_list += (*i)->EvalExpr(out, env);
|
|
first = false;
|
|
}
|
|
|
|
return val_list;
|
|
}
|
|
|
|
static const char* expr_fmt[] = {
|
|
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
|
|
#define EXPR_DEF(type, num_op, fmt) fmt,
|
|
#include "pac_expr.def"
|
|
#undef EXPR_DEF
|
|
};
|
|
|
|
void Expr::init() {
|
|
id_ = nullptr;
|
|
num_ = nullptr;
|
|
cstr_ = nullptr;
|
|
regex_ = nullptr;
|
|
num_operands_ = 0;
|
|
operand_[0] = nullptr;
|
|
operand_[1] = nullptr;
|
|
operand_[2] = nullptr;
|
|
args_ = nullptr;
|
|
cases_ = nullptr;
|
|
}
|
|
|
|
Expr::Expr(ID* arg_id) : DataDepElement(EXPR) {
|
|
init();
|
|
expr_type_ = EXPR_ID;
|
|
id_ = arg_id;
|
|
orig_ = strfmt("%s", id_->Name());
|
|
}
|
|
|
|
Expr::Expr(Number* arg_num) : DataDepElement(EXPR) {
|
|
init();
|
|
expr_type_ = EXPR_NUM;
|
|
num_ = arg_num;
|
|
orig_ = strfmt("((int) %s)", num_->Str());
|
|
}
|
|
|
|
Expr::Expr(Nullptr* arg_nullp) : DataDepElement(EXPR) {
|
|
init();
|
|
expr_type_ = EXPR_NULLPTR;
|
|
nullp_ = arg_nullp;
|
|
orig_ = strfmt("%s", nullp_->Str());
|
|
}
|
|
|
|
Expr::Expr(ConstString* cstr) : DataDepElement(EXPR) {
|
|
init();
|
|
expr_type_ = EXPR_CSTR;
|
|
cstr_ = cstr;
|
|
orig_ = cstr_->str();
|
|
}
|
|
|
|
Expr::Expr(RegEx* regex) : DataDepElement(EXPR) {
|
|
init();
|
|
expr_type_ = EXPR_REGEX;
|
|
regex_ = regex;
|
|
orig_ = strfmt("/%s/", regex_->str().c_str());
|
|
}
|
|
|
|
Expr::Expr(ExprType arg_type, Expr* op1) : DataDepElement(EXPR) {
|
|
init();
|
|
expr_type_ = arg_type;
|
|
num_operands_ = 1;
|
|
operand_[0] = op1;
|
|
orig_ = strfmt(expr_fmt[expr_type_], op1->orig());
|
|
}
|
|
|
|
Expr::Expr(ExprType arg_type, Expr* op1, Expr* op2) : DataDepElement(EXPR) {
|
|
init();
|
|
expr_type_ = arg_type;
|
|
num_operands_ = 2;
|
|
operand_[0] = op1;
|
|
operand_[1] = op2;
|
|
operand_[2] = nullptr;
|
|
orig_ = strfmt(expr_fmt[expr_type_], op1->orig(), op2->orig());
|
|
}
|
|
|
|
Expr::Expr(ExprType arg_type, Expr* op1, Expr* op2, Expr* op3) : DataDepElement(EXPR) {
|
|
init();
|
|
expr_type_ = arg_type;
|
|
num_operands_ = 3;
|
|
operand_[0] = op1;
|
|
operand_[1] = op2;
|
|
operand_[2] = op3;
|
|
orig_ = strfmt(expr_fmt[expr_type_], op1->orig(), op2->orig(), op3->orig());
|
|
}
|
|
|
|
Expr::Expr(ExprList* args) : DataDepElement(EXPR) {
|
|
init();
|
|
expr_type_ = EXPR_CALLARGS;
|
|
num_operands_ = -1;
|
|
args_ = args;
|
|
|
|
orig_ = OrigExprList(args_);
|
|
}
|
|
|
|
Expr::Expr(Expr* index, CaseExprList* cases) : DataDepElement(EXPR) {
|
|
init();
|
|
expr_type_ = EXPR_CASE;
|
|
num_operands_ = -1;
|
|
operand_[0] = index;
|
|
cases_ = cases;
|
|
|
|
orig_ = strfmt("case %s of { ", index->orig());
|
|
foreach (i, CaseExprList, cases_) {
|
|
CaseExpr* c = *i;
|
|
orig_ += strfmt("%s => %s; ", OrigExprList(c->index()).c_str(), c->value()->orig());
|
|
}
|
|
orig_ += "}";
|
|
}
|
|
|
|
Expr::~Expr() {
|
|
delete id_;
|
|
delete operand_[0];
|
|
delete operand_[1];
|
|
delete operand_[2];
|
|
delete_list(args_);
|
|
delete_list(cases_);
|
|
}
|
|
|
|
void Expr::AddCaseExpr(CaseExpr* case_expr) {
|
|
ASSERT(str_.empty());
|
|
ASSERT(expr_type_ == EXPR_CASE);
|
|
ASSERT(cases_);
|
|
cases_->push_back(case_expr);
|
|
}
|
|
|
|
void Expr::GenStrFromFormat(Env* env) {
|
|
// The format != "@custom@"
|
|
ASSERT(*expr_fmt[expr_type_] != '@');
|
|
|
|
switch ( num_operands_ ) {
|
|
case 1: str_ = strfmt(expr_fmt[expr_type_], operand_[0]->str()); break;
|
|
case 2: str_ = strfmt(expr_fmt[expr_type_], operand_[0]->str(), operand_[1]->str()); break;
|
|
case 3: str_ = strfmt(expr_fmt[expr_type_], operand_[0]->str(), operand_[1]->str(), operand_[2]->str()); break;
|
|
default:
|
|
DEBUG_MSG("num_operands_ = %d, orig = %s\n", num_operands_, orig());
|
|
ASSERT(0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
|
|
RecordField* GetRecordField(const ID* id, Env* env) {
|
|
Field* field = env->GetField(id);
|
|
ASSERT(field);
|
|
if ( field->tof() != RECORD_FIELD && field->tof() != PADDING_FIELD )
|
|
throw Exception(id, "not a record field");
|
|
RecordField* r = static_cast<RecordField*>(field);
|
|
ASSERT(r);
|
|
return r;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
void Expr::GenCaseEval(Output* out_cc, Env* env) {
|
|
ASSERT(expr_type_ == EXPR_CASE);
|
|
ASSERT(operand_[0]);
|
|
ASSERT(cases_);
|
|
|
|
Type* val_type = DataType(env);
|
|
ID* val_var = env->AddTempID(val_type);
|
|
|
|
// DataType(env) can return a null pointer if an enum value is not
|
|
// defined.
|
|
if ( ! val_type )
|
|
throw Exception(this, "undefined case value");
|
|
|
|
out_cc->println("%s %s;", val_type->DataTypeStr().c_str(), env->LValue(val_var));
|
|
|
|
// force evaluation of IDs appearing in case stmt
|
|
operand_[0]->ForceIDEval(out_cc, env);
|
|
foreach (i, CaseExprList, cases_)
|
|
(*i)->value()->ForceIDEval(out_cc, env);
|
|
|
|
out_cc->println("// NOLINTBEGIN(bugprone-branch-clone)");
|
|
out_cc->println("switch ( %s ) {", operand_[0]->EvalExpr(out_cc, env));
|
|
Type* switch_type = operand_[0]->DataType(env);
|
|
|
|
out_cc->inc_indent();
|
|
|
|
CaseExpr* default_case = nullptr;
|
|
foreach (i, CaseExprList, cases_) {
|
|
CaseExpr* c = *i;
|
|
ExprList* index = c->index();
|
|
if ( ! index ) {
|
|
if ( default_case )
|
|
throw Exception(c, "duplicate default cases");
|
|
default_case = c;
|
|
}
|
|
else {
|
|
GenCaseStr(index, out_cc, env, switch_type);
|
|
out_cc->inc_indent();
|
|
out_cc->println("%s = %s;", env->LValue(val_var), c->value()->EvalExpr(out_cc, env));
|
|
out_cc->println("break;");
|
|
out_cc->dec_indent();
|
|
}
|
|
}
|
|
|
|
// Generate the default case after all other cases
|
|
GenCaseStr(nullptr, out_cc, env, switch_type);
|
|
out_cc->inc_indent();
|
|
if ( default_case ) {
|
|
out_cc->println("%s = %s;", env->LValue(val_var), default_case->value()->EvalExpr(out_cc, env));
|
|
}
|
|
else {
|
|
out_cc->println("throw binpac::ExceptionInvalidCaseIndex(\"%s\", (int64)%s);", Location(),
|
|
operand_[0]->EvalExpr(out_cc, env));
|
|
}
|
|
out_cc->println("break;");
|
|
out_cc->dec_indent();
|
|
|
|
out_cc->dec_indent();
|
|
out_cc->println("}");
|
|
out_cc->println("// NOLINTEND(bugprone-branch-clone)");
|
|
|
|
env->SetEvaluated(val_var);
|
|
str_ = env->RValue(val_var);
|
|
}
|
|
|
|
void Expr::GenEval(Output* out_cc, Env* env) {
|
|
switch ( expr_type_ ) {
|
|
case EXPR_NUM: str_ = num_->Str(); break;
|
|
case EXPR_NULLPTR: str_ = nullp_->Str(); break;
|
|
|
|
case EXPR_ID:
|
|
if ( ! env->Evaluated(id_) )
|
|
env->Evaluate(out_cc, id_);
|
|
str_ = env->RValue(id_);
|
|
break;
|
|
|
|
case EXPR_MEMBER: {
|
|
/*
|
|
For member expressions such X.Y, evaluating
|
|
X only is sufficient. (Actually trying to
|
|
evaluate Y will lead to error because Y is
|
|
not defined in the current environment.)
|
|
*/
|
|
operand_[0]->GenEval(out_cc, env);
|
|
|
|
Type* ty0 = operand_[0]->DataType(env);
|
|
|
|
if ( ty0 ) {
|
|
str_ = strfmt("%s%s", operand_[0]->EvalExpr(out_cc, env), ty0->EvalMember(operand_[1]->id()).c_str());
|
|
}
|
|
else {
|
|
string tmp = strfmt("->%s()", operand_[1]->id()->Name());
|
|
str_ = strfmt("%s%s", operand_[0]->EvalExpr(out_cc, env), tmp.c_str());
|
|
}
|
|
} break;
|
|
|
|
case EXPR_SUBSCRIPT: {
|
|
operand_[0]->GenEval(out_cc, env);
|
|
operand_[1]->GenEval(out_cc, env);
|
|
|
|
string v0 = operand_[0]->EvalExpr(out_cc, env);
|
|
string v1 = operand_[1]->EvalExpr(out_cc, env);
|
|
|
|
Type* ty0 = operand_[0]->DataType(env);
|
|
if ( ty0 )
|
|
str_ = ty0->EvalElement(v0, v1);
|
|
else
|
|
str_ = strfmt("%s[%s]", v0.c_str(), v1.c_str());
|
|
} break;
|
|
|
|
case EXPR_SIZEOF: {
|
|
const ID* id = operand_[0]->id();
|
|
RecordField* rf;
|
|
Type* ty;
|
|
|
|
try {
|
|
if ( rf = GetRecordField(id, env); rf != nullptr ) {
|
|
str_ = strfmt("%s", rf->FieldSize(out_cc, env));
|
|
}
|
|
} catch ( ExceptionIDNotFound& e ) {
|
|
if ( ty = TypeDecl::LookUpType(id); ty != nullptr ) {
|
|
int ty_size = ty->StaticSize(global_env());
|
|
if ( ty_size >= 0 )
|
|
str_ = strfmt("%d", ty_size);
|
|
else
|
|
throw Exception(id, "unknown size");
|
|
}
|
|
else
|
|
throw Exception(id, "not a record field or type");
|
|
}
|
|
} break;
|
|
|
|
case EXPR_OFFSETOF: {
|
|
const ID* id = operand_[0]->id();
|
|
RecordField* rf = GetRecordField(id, env);
|
|
str_ = strfmt("%s", rf->FieldOffset(out_cc, env));
|
|
} break;
|
|
|
|
case EXPR_CALLARGS: str_ = EvalExprList(args_, out_cc, env); break;
|
|
|
|
case EXPR_CASE: GenCaseEval(out_cc, env); break;
|
|
|
|
default:
|
|
// Evaluate every operand by default
|
|
for ( auto& op : operand_ )
|
|
if ( op )
|
|
op->GenEval(out_cc, env);
|
|
GenStrFromFormat(env);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void Expr::ForceIDEval(Output* out_cc, Env* env) {
|
|
switch ( expr_type_ ) {
|
|
case EXPR_NUM:
|
|
case EXPR_SIZEOF:
|
|
case EXPR_OFFSETOF: break;
|
|
|
|
case EXPR_ID:
|
|
if ( ! env->Evaluated(id_) )
|
|
env->Evaluate(out_cc, id_);
|
|
break;
|
|
|
|
case EXPR_MEMBER: operand_[0]->ForceIDEval(out_cc, env); break;
|
|
|
|
case EXPR_CALLARGS: {
|
|
foreach (i, ExprList, args_)
|
|
(*i)->ForceIDEval(out_cc, env);
|
|
} break;
|
|
|
|
case EXPR_CASE: {
|
|
operand_[0]->ForceIDEval(out_cc, env);
|
|
foreach (i, CaseExprList, cases_)
|
|
(*i)->value()->ForceIDEval(out_cc, env);
|
|
} break;
|
|
|
|
default:
|
|
// Evaluate every operand by default
|
|
for ( auto& op : operand_ )
|
|
if ( op )
|
|
op->ForceIDEval(out_cc, env);
|
|
break;
|
|
}
|
|
}
|
|
|
|
const char* Expr::EvalExpr(Output* out_cc, Env* env) {
|
|
GenEval(out_cc, env);
|
|
return str();
|
|
}
|
|
|
|
Type* Expr::DataType(Env* env) const {
|
|
Type* data_type;
|
|
|
|
switch ( expr_type_ ) {
|
|
case EXPR_ID: data_type = env->GetDataType(id_); break;
|
|
|
|
case EXPR_MEMBER: {
|
|
// Get type of the parent
|
|
Type* parent_type = operand_[0]->DataType(env);
|
|
if ( ! parent_type )
|
|
return nullptr;
|
|
data_type = parent_type->MemberDataType(operand_[1]->id());
|
|
} break;
|
|
|
|
case EXPR_SUBSCRIPT: {
|
|
// Get type of the parent
|
|
Type* parent_type = operand_[0]->DataType(env);
|
|
data_type = parent_type->ElementDataType();
|
|
} break;
|
|
|
|
case EXPR_PAREN: data_type = operand_[0]->DataType(env); break;
|
|
|
|
case EXPR_COND: {
|
|
Type* type1 = operand_[1]->DataType(env);
|
|
Type* type2 = operand_[2]->DataType(env);
|
|
if ( ! Type::CompatibleTypes(type1, type2) ) {
|
|
throw Exception(this, strfmt("type mismatch: %s vs %s", type1->DataTypeStr().c_str(),
|
|
type2->DataTypeStr().c_str()));
|
|
}
|
|
data_type = type1;
|
|
} break;
|
|
|
|
case EXPR_CALL: data_type = operand_[0]->DataType(env); break;
|
|
|
|
case EXPR_CASE: {
|
|
if ( cases_ && ! cases_->empty() ) {
|
|
Type* type1 = cases_->front()->value()->DataType(env);
|
|
Type* numeric_with_largest_width = nullptr;
|
|
|
|
foreach (i, CaseExprList, cases_) {
|
|
Type* type2 = (*i)->value()->DataType(env);
|
|
if ( ! Type::CompatibleTypes(type1, type2) ) {
|
|
throw Exception(this, strfmt("type mismatch: %s vs %s", type1->DataTypeStr().c_str(),
|
|
type2->DataTypeStr().c_str()));
|
|
}
|
|
if ( type1 == extern_type_nullptr )
|
|
type1 = type2;
|
|
|
|
if ( type2 && type2->IsNumericType() ) {
|
|
if ( numeric_with_largest_width ) {
|
|
int largest;
|
|
int contender;
|
|
|
|
// External C++ types like "int", "bool", "enum" use "int"
|
|
// storage internally.
|
|
if ( numeric_with_largest_width->tot() == Type::EXTERN )
|
|
largest = sizeof(int);
|
|
else
|
|
largest = numeric_with_largest_width->StaticSize(env);
|
|
|
|
if ( type2->tot() == Type::EXTERN )
|
|
contender = sizeof(int);
|
|
else
|
|
contender = type2->StaticSize(env);
|
|
|
|
if ( contender > largest )
|
|
numeric_with_largest_width = type2;
|
|
}
|
|
else
|
|
numeric_with_largest_width = type2;
|
|
}
|
|
}
|
|
data_type = numeric_with_largest_width ? numeric_with_largest_width : type1;
|
|
}
|
|
else
|
|
data_type = nullptr;
|
|
} break;
|
|
|
|
case EXPR_NUM:
|
|
case EXPR_SIZEOF:
|
|
case EXPR_OFFSETOF:
|
|
case EXPR_NEG:
|
|
case EXPR_PLUS:
|
|
case EXPR_MINUS:
|
|
case EXPR_TIMES:
|
|
case EXPR_DIV:
|
|
case EXPR_MOD:
|
|
case EXPR_BITNOT:
|
|
case EXPR_BITAND:
|
|
case EXPR_BITOR:
|
|
case EXPR_BITXOR:
|
|
case EXPR_LSHIFT:
|
|
case EXPR_RSHIFT:
|
|
case EXPR_EQUAL:
|
|
case EXPR_GE:
|
|
case EXPR_LE:
|
|
case EXPR_GT:
|
|
case EXPR_LT:
|
|
case EXPR_NOT:
|
|
case EXPR_AND:
|
|
case EXPR_OR: data_type = extern_type_int; break;
|
|
|
|
default: data_type = nullptr; break;
|
|
}
|
|
|
|
return data_type;
|
|
}
|
|
|
|
string Expr::DataTypeStr(Env* env) const {
|
|
Type* type = DataType(env);
|
|
|
|
if ( ! type ) {
|
|
throw Exception(this, strfmt("cannot find data type for expression `%s'", orig()));
|
|
}
|
|
|
|
return type->DataTypeStr();
|
|
}
|
|
|
|
string Expr::SetFunc(Output* out, Env* env) {
|
|
switch ( expr_type_ ) {
|
|
case EXPR_ID: return set_function(id_);
|
|
case EXPR_MEMBER: {
|
|
// Evaluate the parent
|
|
string parent_val(operand_[0]->EvalExpr(out, env));
|
|
return parent_val + "->" + set_function(operand_[1]->id());
|
|
} break;
|
|
default:
|
|
throw Exception(this, strfmt("cannot generate set function "
|
|
"for expression `%s'",
|
|
orig()));
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool Expr::ConstFold(Env* env, int* pn) const {
|
|
switch ( expr_type_ ) {
|
|
case EXPR_NUM: *pn = num_->Num(); return true;
|
|
case EXPR_ID: return env->GetConstant(id_, pn);
|
|
default:
|
|
// ### FIXME: folding consts
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// TODO: build a generic data dependency extraction process
|
|
namespace {
|
|
|
|
// Maximum of two minimal header sizes
|
|
int mhs_max(int h1, int h2) {
|
|
if ( h1 < 0 || h2 < 0 )
|
|
return -1;
|
|
else {
|
|
// return max(h1, h2);
|
|
return h1 > h2 ? h1 : h2;
|
|
}
|
|
}
|
|
|
|
// MHS required to evaluate the field
|
|
int mhs_letfield(Env* env, LetField* field) { return field->expr()->MinimalHeaderSize(env); }
|
|
|
|
int mhs_recordfield(Env* env, RecordField* field) {
|
|
int offset = field->static_offset();
|
|
if ( offset < 0 ) // offset cannot be statically determined
|
|
return -1;
|
|
int size = field->StaticSize(env, offset);
|
|
if ( size < 0 ) // size cannot be statically determined
|
|
return -1;
|
|
return offset + size;
|
|
}
|
|
|
|
int mhs_casefield(Env* env, CaseField* field) {
|
|
// TODO: deal with the index
|
|
int size = field->StaticSize(env);
|
|
if ( size < 0 ) // size cannot be statically determined
|
|
return -1;
|
|
return size;
|
|
}
|
|
|
|
int mhs_field(Env* env, Field* field) {
|
|
int mhs = -1;
|
|
switch ( field->tof() ) {
|
|
case LET_FIELD: {
|
|
LetField* f = static_cast<LetField*>(field);
|
|
ASSERT(f);
|
|
mhs = mhs_letfield(env, f);
|
|
} break;
|
|
|
|
case CONTEXT_FIELD:
|
|
case FLOW_FIELD: ASSERT(0); break;
|
|
|
|
case PARAM_FIELD: mhs = 0; break;
|
|
|
|
case RECORD_FIELD:
|
|
case PADDING_FIELD: {
|
|
RecordField* f = static_cast<RecordField*>(field);
|
|
ASSERT(f);
|
|
mhs = mhs_recordfield(env, f);
|
|
} break;
|
|
|
|
case CASE_FIELD: {
|
|
CaseField* f = static_cast<CaseField*>(field);
|
|
ASSERT(f);
|
|
mhs = mhs_casefield(env, f);
|
|
} break;
|
|
|
|
case PARSE_VAR_FIELD:
|
|
case PRIV_VAR_FIELD:
|
|
case PUB_VAR_FIELD:
|
|
case TEMP_VAR_FIELD: mhs = 0; break;
|
|
|
|
case WITHINPUT_FIELD: {
|
|
// ### TODO: fix this
|
|
mhs = -1;
|
|
} break;
|
|
}
|
|
return mhs;
|
|
}
|
|
|
|
int mhs_id(Env* env, const ID* id) {
|
|
int mhs = -1;
|
|
switch ( env->GetIDType(id) ) {
|
|
case CONST:
|
|
case GLOBAL_VAR:
|
|
case TEMP_VAR:
|
|
case STATE_VAR:
|
|
case FUNC_ID:
|
|
case FUNC_PARAM: mhs = 0; break;
|
|
case MEMBER_VAR:
|
|
case PRIV_MEMBER_VAR: {
|
|
Field* field = env->GetField(id);
|
|
if ( ! field )
|
|
throw ExceptionIDNotField(id);
|
|
mhs = mhs_field(env, field);
|
|
} break;
|
|
case UNION_VAR:
|
|
// TODO: deal with UNION_VAR
|
|
mhs = -1;
|
|
break;
|
|
case MACRO: {
|
|
Expr* e = env->GetMacro(id);
|
|
mhs = e->MinimalHeaderSize(env);
|
|
} break;
|
|
}
|
|
return mhs;
|
|
}
|
|
} // namespace
|
|
|
|
int Expr::MinimalHeaderSize(Env* env) {
|
|
int mhs;
|
|
|
|
switch ( expr_type_ ) {
|
|
case EXPR_NUM:
|
|
// Zero byte is required
|
|
mhs = 0;
|
|
break;
|
|
|
|
case EXPR_ID: mhs = mhs_id(env, id_); break;
|
|
|
|
case EXPR_MEMBER:
|
|
// TODO: this is not a tight bound because
|
|
// one actually does not have to parse the
|
|
// whole record to compute one particular
|
|
// field.
|
|
mhs = operand_[0]->MinimalHeaderSize(env);
|
|
break;
|
|
|
|
case EXPR_SUBSCRIPT: {
|
|
int index;
|
|
Type* array_type = operand_[0]->DataType(env);
|
|
Type* elem_type = array_type->ElementDataType();
|
|
int elem_size = elem_type->StaticSize(env);
|
|
if ( elem_size >= 0 && operand_[1]->ConstFold(env, &index) ) {
|
|
mhs = elem_size * index;
|
|
}
|
|
else {
|
|
mhs = -1;
|
|
}
|
|
} break;
|
|
|
|
case EXPR_SIZEOF: {
|
|
const ID* id = operand_[0]->id();
|
|
ASSERT(id);
|
|
RecordField* rf;
|
|
Type* ty;
|
|
|
|
if ( rf = GetRecordField(id, env); rf != nullptr ) {
|
|
if ( rf->StaticSize(env, -1) >= 0 )
|
|
mhs = 0;
|
|
else
|
|
mhs = mhs_recordfield(env, rf);
|
|
}
|
|
|
|
else if ( ty = TypeDecl::LookUpType(id); ty != nullptr ) {
|
|
mhs = 0;
|
|
}
|
|
|
|
else
|
|
throw Exception(id, "not a record field or type");
|
|
} break;
|
|
|
|
case EXPR_OFFSETOF: {
|
|
const ID* id = operand_[0]->id();
|
|
ASSERT(id);
|
|
RecordField* field = GetRecordField(id, env);
|
|
|
|
mhs = field->static_offset();
|
|
if ( mhs < 0 ) {
|
|
mhs = 0;
|
|
// Take the MHS of the preceding (non-let) field
|
|
RecordField* prev_field = field->prev();
|
|
ASSERT(prev_field);
|
|
mhs = mhs_recordfield(env, prev_field);
|
|
}
|
|
} break;
|
|
|
|
case EXPR_CALLARGS: {
|
|
mhs = 0;
|
|
if ( args_ )
|
|
for ( const auto& arg : *args_ )
|
|
mhs = mhs_max(mhs, arg->MinimalHeaderSize(env));
|
|
} break;
|
|
case EXPR_CASE: {
|
|
mhs = operand_[0]->MinimalHeaderSize(env);
|
|
for ( const auto& ce : *cases_ ) {
|
|
if ( ce->index() )
|
|
for ( const auto& idx : *(ce->index()) )
|
|
mhs = mhs_max(mhs, idx->MinimalHeaderSize(env));
|
|
mhs = mhs_max(mhs, ce->value()->MinimalHeaderSize(env));
|
|
}
|
|
} break;
|
|
default:
|
|
// Evaluate every operand by default
|
|
mhs = 0;
|
|
for ( auto& op : operand_ )
|
|
if ( op )
|
|
mhs = mhs_max(mhs, op->MinimalHeaderSize(env));
|
|
break;
|
|
}
|
|
|
|
return mhs;
|
|
}
|
|
|
|
bool Expr::HasReference(const ID* id) const {
|
|
switch ( expr_type_ ) {
|
|
case EXPR_ID: return *id == *id_;
|
|
|
|
case EXPR_MEMBER: return operand_[0]->HasReference(id);
|
|
|
|
case EXPR_CALLARGS: {
|
|
foreach (i, ExprList, args_)
|
|
if ( (*i)->HasReference(id) )
|
|
return true;
|
|
}
|
|
return false;
|
|
|
|
case EXPR_CASE: {
|
|
foreach (i, CaseExprList, cases_)
|
|
if ( (*i)->HasReference(id) )
|
|
return true;
|
|
}
|
|
return false;
|
|
|
|
default:
|
|
// Evaluate every operand by default
|
|
for ( auto& op : operand_ ) {
|
|
if ( op && op->HasReference(id) ) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool Expr::DoTraverse(DataDepVisitor* visitor) {
|
|
switch ( expr_type_ ) {
|
|
case EXPR_ID: break;
|
|
|
|
case EXPR_MEMBER:
|
|
/*
|
|
For member expressions such X.Y, evaluating
|
|
X only is sufficient. (Actually trying to
|
|
evaluate Y will lead to error because Y is
|
|
not defined in the current environment.)
|
|
*/
|
|
if ( ! operand_[0]->Traverse(visitor) )
|
|
return false;
|
|
break;
|
|
|
|
case EXPR_CALLARGS: {
|
|
foreach (i, ExprList, args_)
|
|
if ( ! (*i)->Traverse(visitor) )
|
|
return false;
|
|
} break;
|
|
|
|
case EXPR_CASE: {
|
|
foreach (i, CaseExprList, cases_)
|
|
if ( ! (*i)->Traverse(visitor) )
|
|
return false;
|
|
} break;
|
|
|
|
default:
|
|
// Evaluate every operand by default
|
|
for ( auto& op : operand_ ) {
|
|
if ( op && ! op->Traverse(visitor) ) {
|
|
return false;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Expr::RequiresAnalyzerContext() const {
|
|
switch ( expr_type_ ) {
|
|
case EXPR_ID: return *id_ == *analyzer_context_id;
|
|
|
|
case EXPR_MEMBER:
|
|
/*
|
|
For member expressions such X.Y, evaluating
|
|
X only is sufficient. (Actually trying to
|
|
evaluate Y will lead to error because Y is
|
|
not defined in the current environment.)
|
|
*/
|
|
return operand_[0]->RequiresAnalyzerContext();
|
|
|
|
case EXPR_CALLARGS: {
|
|
foreach (i, ExprList, args_)
|
|
if ( (*i)->RequiresAnalyzerContext() )
|
|
return true;
|
|
}
|
|
return false;
|
|
|
|
case EXPR_CASE: {
|
|
foreach (i, CaseExprList, cases_)
|
|
if ( (*i)->RequiresAnalyzerContext() )
|
|
return true;
|
|
}
|
|
return false;
|
|
|
|
default:
|
|
// Evaluate every operand by default
|
|
for ( auto& op : operand_ )
|
|
if ( op && op->RequiresAnalyzerContext() ) {
|
|
DEBUG_MSG("'%s' requires analyzer context\n", op->orig());
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
CaseExpr::CaseExpr(ExprList* index, Expr* value)
|
|
: DataDepElement(DataDepElement::CASEEXPR), index_(index), value_(value) {}
|
|
|
|
CaseExpr::~CaseExpr() {
|
|
delete_list(index_);
|
|
index_ = nullptr;
|
|
delete value_;
|
|
}
|
|
|
|
bool CaseExpr::DoTraverse(DataDepVisitor* visitor) {
|
|
foreach (i, ExprList, index_)
|
|
if ( ! (*i)->Traverse(visitor) )
|
|
return false;
|
|
return value_->Traverse(visitor);
|
|
}
|
|
|
|
bool CaseExpr::HasReference(const ID* id) const { return value_->HasReference(id); }
|
|
|
|
bool CaseExpr::RequiresAnalyzerContext() const {
|
|
// index_ should evaluate to constants
|
|
return value_->RequiresAnalyzerContext();
|
|
}
|