// See the file "COPYING" in the main distribution directory for copyright. #include "pac_case.h" #include #include #include "pac_btype.h" #include "pac_exception.h" #include "pac_expr.h" #include "pac_exttype.h" #include "pac_id.h" #include "pac_output.h" #include "pac_typedecl.h" #include "pac_utils.h" CaseType::CaseType(Expr* index_expr, CaseFieldList* cases) : Type(CASE), index_expr_(index_expr), cases_(cases) { index_var_ = nullptr; foreach (i, CaseFieldList, cases_) AddField(*i); } CaseType::~CaseType() { delete index_var_; delete index_expr_; delete cases_; } void CaseType::AddCaseField(CaseField* f) { // All fields must be added before Prepare() ASSERT(! env()); AddField(f); cases_->push_back(f); } bool CaseType::DefineValueVar() const { return false; } string CaseType::DataTypeStr() const { ASSERT(type_decl()); return strfmt("%s*", type_decl()->class_name().c_str()); } Type* CaseType::ValueType() const { foreach (i, CaseFieldList, cases_) { CaseField* c = *i; return c->type(); } ASSERT(0); return nullptr; } string CaseType::DefaultValue() const { return ValueType()->DefaultValue(); } void CaseType::Prepare(Env* env, int flags) { ASSERT(flags & TO_BE_PARSED); index_var_ = new ID(strfmt("%s_case_index", value_var()->Name())); // Unable to get the type for index_var_ at this moment, but we'll // generate the right type based on index_expr_ later. env->AddID(index_var_, MEMBER_VAR, nullptr); // Sort the cases_ to put the default case at the end of the list CaseFieldList::iterator default_case_it = cases_->end(); // to avoid warning CaseField* default_case = nullptr; foreach (i, CaseFieldList, cases_) { CaseField* c = *i; if ( ! c->index() ) { if ( default_case ) throw Exception(c, "duplicate default case"); default_case_it = i; default_case = c; } } if ( default_case ) { cases_->erase(default_case_it); cases_->push_back(default_case); } foreach (i, CaseFieldList, cases_) { CaseField* c = *i; c->set_index_var(index_var_); c->set_case_type(this); } Type::Prepare(env, flags); } void CaseType::GenPrivDecls(Output* out_h, Env* env) { Type* t = index_expr_->DataType(env); if ( t->tot() != Type::BUILTIN ) // It's a Type::EXTERN with a C++ type of "int", "bool", or "enum", // any of which will convert consistently using an int as storage type. t = extern_type_int; out_h->println("%s %s;", t->DataTypeStr().c_str(), env->LValue(index_var_)); Type::GenPrivDecls(out_h, env); } void CaseType::GenPubDecls(Output* out_h, Env* env) { Type* t = index_expr_->DataType(env); if ( t->tot() != Type::BUILTIN ) t = extern_type_int; out_h->println("%s %s const { return %s; }", t->DataTypeStr().c_str(), env->RValue(index_var_), env->LValue(index_var_)); Type::GenPubDecls(out_h, env); } void CaseType::GenInitCode(Output* out_cc, Env* env) { out_cc->println("%s = -1;", env->LValue(index_var_)); Type::GenInitCode(out_cc, env); } void CaseType::GenCleanUpCode(Output* out_cc, Env* env) { Type::GenCleanUpCode(out_cc, env); env->set_in_branch(true); out_cc->println("// NOLINTBEGIN(bugprone-branch-clone)"); out_cc->println("switch ( %s ) {", env->RValue(index_var_)); out_cc->inc_indent(); foreach (i, CaseFieldList, cases_) { CaseField* c = *i; c->GenCleanUpCode(out_cc, env); } out_cc->dec_indent(); out_cc->println("}"); out_cc->println("// NOLINTEND(bugprone-branch-clone)"); env->set_in_branch(false); } void CaseType::DoGenParseCode(Output* out_cc, Env* env, const DataPtr& data, int flags) { if ( StaticSize(env) >= 0 ) GenBoundaryCheck(out_cc, env, data); bool compute_size_var = false; if ( ! incremental_input() ) compute_size_var = AddSizeVar(out_cc, env); out_cc->println("%s = %s;", env->LValue(index_var_), index_expr_->EvalExpr(out_cc, env)); env->SetEvaluated(index_var_); env->set_in_branch(true); out_cc->println("// NOLINTBEGIN(bugprone-branch-clone)"); out_cc->println("switch ( %s ) {", env->RValue(index_var_)); out_cc->inc_indent(); bool has_default_case = false; foreach (i, CaseFieldList, cases_) { CaseField* c = *i; c->GenParseCode(out_cc, env, data, compute_size_var ? size_var() : nullptr); if ( c->IsDefaultCase() ) has_default_case = true; } if ( ! has_default_case ) { out_cc->println("default:"); out_cc->inc_indent(); out_cc->println("throw binpac::ExceptionInvalidCaseIndex(\"%s\", (int64)%s);", decl_id()->Name(), env->RValue(index_var_)); out_cc->println("break;"); out_cc->dec_indent(); } out_cc->dec_indent(); out_cc->println("}"); out_cc->println("// NOLINTEND(bugprone-branch-clone)"); env->set_in_branch(false); if ( compute_size_var ) env->SetEvaluated(size_var()); } void CaseType::GenDynamicSize(Output* out_cc, Env* env, const DataPtr& data) { GenParseCode(out_cc, env, data, 0); } int CaseType::StaticSize(Env* env) const { int static_w = -1; foreach (i, CaseFieldList, cases_) { CaseField* c = *i; int w = c->StaticSize(env); if ( w < 0 || (static_w >= 0 && w != static_w) ) return -1; static_w = w; } return static_w; } void CaseType::SetBoundaryChecked() { Type::SetBoundaryChecked(); foreach (i, CaseFieldList, cases_) { CaseField* c = *i; c->SetBoundaryChecked(); } } void CaseType::DoMarkIncrementalInput() { foreach (i, CaseFieldList, cases_) { CaseField* c = *i; c->type()->MarkIncrementalInput(); } } bool CaseType::ByteOrderSensitive() const { foreach (i, CaseFieldList, cases_) { CaseField* c = *i; if ( c->RequiresByteOrder() ) return true; } return false; } CaseField::CaseField(ExprList* index, ID* id, Type* type) : Field(CASE_FIELD, TYPE_TO_BE_PARSED | CLASS_MEMBER | PUBLIC_READABLE, id, type), index_(index) { ASSERT(type_); type_->set_value_var(id, MEMBER_VAR); case_type_ = nullptr; index_var_ = nullptr; } CaseField::~CaseField() { delete_list(ExprList, index_); } void GenCaseStr(ExprList* index_list, Output* out_cc, Env* env, Type* switch_type) { if ( index_list ) { foreach (i, ExprList, index_list) { Expr* index_expr = *i; Type* case_type = index_expr->DataType(env); if ( case_type->tot() == Type::BUILTIN && case_type->StaticSize(env) > 4 ) throw ExceptionInvalidCaseSizeExpr(index_expr); int index_const; if ( ! index_expr->ConstFold(env, &index_const) ) throw ExceptionNonConstExpr(index_expr); // External C++ types like "int", "bool", "enum" // all use "int" type internally by default. int case_type_width = 4; int switch_type_width = 4; if ( switch_type->tot() == Type::BUILTIN ) switch_type_width = switch_type->StaticSize(env); if ( case_type->tot() == Type::BUILTIN ) case_type_width = case_type->StaticSize(env); if ( case_type_width > switch_type_width ) { BuiltInType* st = (BuiltInType*)switch_type; if ( switch_type_width == 1 ) { if ( st->bit_type() == BuiltInType::INT8 ) { if ( index_const < std::numeric_limits::min() ) throw ExceptionInvalidCaseLimitExpr(index_expr); if ( index_const > std::numeric_limits::max() ) throw ExceptionInvalidCaseLimitExpr(index_expr); } else { if ( index_const < std::numeric_limits::min() ) throw ExceptionInvalidCaseLimitExpr(index_expr); if ( index_const > std::numeric_limits::max() ) throw ExceptionInvalidCaseLimitExpr(index_expr); } } else if ( switch_type_width == 2 ) { if ( st->bit_type() == BuiltInType::INT16 ) { if ( index_const < std::numeric_limits::min() ) throw ExceptionInvalidCaseLimitExpr(index_expr); if ( index_const > std::numeric_limits::max() ) throw ExceptionInvalidCaseLimitExpr(index_expr); } else { if ( index_const < std::numeric_limits::min() ) throw ExceptionInvalidCaseLimitExpr(index_expr); if ( index_const > std::numeric_limits::max() ) throw ExceptionInvalidCaseLimitExpr(index_expr); } } else { assert(0); } } // We're always using "int" for storage, so ok to just // cast into the type used by the switch statement since // some unsafe stuff is already checked above. if ( ! switch_type->IsBooleanType() ) out_cc->println("case ((%s)%d):", switch_type->DataTypeStr().c_str(), index_const); else out_cc->println("case %s:", index_const == 0 ? "false" : "true"); } } else { out_cc->println("default:"); } } void CaseField::Prepare(Env* env) { ASSERT(index_var_); Field::Prepare(env); } void CaseField::GenPubDecls(Output* out_h, Env* env) { if ( ! ((flags_ & PUBLIC_READABLE) && (flags_ & CLASS_MEMBER)) ) return; // Skip type "empty" if ( type_->DataTypeStr().empty() ) return; out_h->println("%s %s const {", type_->DataTypeConstRefStr().c_str(), env->RValue(id_)); out_h->inc_indent(); if ( ! index_ ) out_h->println("return %s;", lvalue()); else { out_h->println("// NOLINTBEGIN(bugprone-branch-clone)"); out_h->println("switch ( %s ) {", env->RValue(index_var_)); out_h->inc_indent(); GenCaseStr(index_, out_h, env, case_type()->IndexExpr()->DataType(env)); out_h->inc_indent(); out_h->println("break; // OK"); out_h->dec_indent(); out_h->println("default:"); out_h->inc_indent(); out_h->println("throw binpac::ExceptionInvalidCase(\"%s\", (int64)%s, \"%s\");", id_->LocName(), env->RValue(index_var_), OrigExprList(index_).c_str()); out_h->println("break;"); out_h->dec_indent(); out_h->dec_indent(); out_h->println("}"); out_h->println("// NOLINTEND(bugprone-branch-clone)"); out_h->println("return %s;", lvalue()); } out_h->dec_indent(); out_h->println("}"); } void CaseField::GenInitCode(Output* out_cc, Env* env) { // GenCaseStr(index_, out_cc, env); // out_cc->inc_indent(); // out_cc->println("{"); // out_cc->println("// Initialize \"%s\"", id_->Name()); type_->GenInitCode(out_cc, env); // out_cc->println("}"); // out_cc->println("break;"); // out_cc->dec_indent(); } void CaseField::GenCleanUpCode(Output* out_cc, Env* env) { GenCaseStr(index_, out_cc, env, case_type()->IndexExpr()->DataType(env)); out_cc->inc_indent(); out_cc->println("// Clean up \"%s\"", id_->Name()); if ( ! anonymous_field() ) { out_cc->println("{"); out_cc->inc_indent(); type_->GenCleanUpCode(out_cc, env); out_cc->dec_indent(); out_cc->println("}"); } else out_cc->println("{}"); out_cc->println("break;"); out_cc->dec_indent(); } void CaseField::GenParseCode(Output* out_cc, Env* env, const DataPtr& data, const ID* size_var) { GenCaseStr(index_, out_cc, env, case_type()->IndexExpr()->DataType(env)); out_cc->inc_indent(); out_cc->println("// Parse \"%s\"", id_->Name()); out_cc->println("{"); out_cc->inc_indent(); { Env case_env(env, this); type_->GenPreParsing(out_cc, &case_env); type_->GenParseCode(out_cc, &case_env, data, 0); if ( size_var ) { out_cc->println("%s = %s;", case_env.LValue(size_var), type_->DataSize(out_cc, &case_env, data).c_str()); } if ( type_->incremental_input() ) { ASSERT(case_type()->parsing_complete_var()); out_cc->println("%s = %s;", case_env.LValue(case_type()->parsing_complete_var()), case_env.RValue(type_->parsing_complete_var())); } } out_cc->dec_indent(); out_cc->println("}"); out_cc->println("break;"); out_cc->dec_indent(); } bool CaseField::DoTraverse(DataDepVisitor* visitor) { return Field::DoTraverse(visitor) && type()->Traverse(visitor); } bool CaseField::RequiresAnalyzerContext() const { return Field::RequiresAnalyzerContext() || type()->RequiresAnalyzerContext(); }