diff --git a/tools/binpac/lib/binpac_exception.h b/tools/binpac/lib/binpac_exception.h index 07960f6248..cdbfd70405 100644 --- a/tools/binpac/lib/binpac_exception.h +++ b/tools/binpac/lib/binpac_exception.h @@ -1,6 +1,9 @@ #ifndef binpac_exception_h #define binpac_exception_h +#include +#include + namespace binpac { class Exception @@ -45,19 +48,19 @@ class ExceptionInvalidCase : public Exception { public: ExceptionInvalidCase(const char* location, - int index, + int64_t index, const char *expected) : location_(location), index_(index), expected_(expected) { - append(binpac_fmt("invalid case: %s: %d (%s)", + append(binpac_fmt("invalid case: %s: %" PRIi64 " (%s)", location, index, expected)); } protected: const char* location_; - int index_; + int64_t index_; string expected_; }; @@ -65,17 +68,17 @@ class ExceptionInvalidCaseIndex : public Exception { public: ExceptionInvalidCaseIndex(const char* location, - int index) + int64_t index) : location_(location), index_(index) { - append(binpac_fmt("invalid index for case: %s: %d", + append(binpac_fmt("invalid index for case: %s: %" PRIi64, location, index)); } protected: const char* location_; - int index_; + int64_t index_; }; class ExceptionInvalidOffset : public Exception diff --git a/tools/binpac/src/pac_case.cc b/tools/binpac/src/pac_case.cc index 1e501fc301..95cb3524b5 100644 --- a/tools/binpac/src/pac_case.cc +++ b/tools/binpac/src/pac_case.cc @@ -5,9 +5,13 @@ #include "pac_output.h" #include "pac_typedecl.h" #include "pac_utils.h" +#include "pac_btype.h" #include "pac_case.h" +#include +#include + CaseType::CaseType(Expr* index_expr, CaseFieldList* cases) : Type(CASE), index_expr_(index_expr), cases_(cases) { @@ -64,7 +68,9 @@ void CaseType::Prepare(Env* env, int flags) ASSERT(flags & TO_BE_PARSED); index_var_ = new ID(strfmt("%s_case_index", value_var()->Name())); - env->AddID(index_var_, MEMBER_VAR, extern_type_int); + // 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, 0); // Sort the cases_ to put the default case at the end of the list CaseFieldList::iterator default_case_it = @@ -100,13 +106,25 @@ void CaseType::Prepare(Env* env, int flags) void CaseType::GenPrivDecls(Output* out_h, Env* env) { - out_h->println("int %s;", env->LValue(index_var_)); + 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) { - out_h->println("int %s const { return %s; }", + 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); } @@ -168,7 +186,7 @@ void CaseType::DoGenParseCode(Output* out_cc, Env* env, { out_cc->println("default:"); out_cc->inc_indent(); - out_cc->println("throw binpac::ExceptionInvalidCaseIndex(\"%s\", %s);", + out_cc->println("throw binpac::ExceptionInvalidCaseIndex(\"%s\", (int64)%s);", decl_id()->Name(), env->RValue(index_var_)); out_cc->println("break;"); out_cc->dec_indent(); @@ -248,18 +266,80 @@ CaseField::~CaseField() delete_list(ExprList, index_); } -void GenCaseStr(ExprList *index_list, Output *out_cc, Env *env) +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); - out_cc->println("case %d:", index_const); + + // 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); + } + } + } + + // 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. + out_cc->println("case ((%s) %d):", + switch_type->DataTypeStr().c_str(), index_const); } } else @@ -296,7 +376,7 @@ void CaseField::GenPubDecls(Output* out_h, Env* env) out_h->println("switch ( %s )", env->RValue(index_var_)); out_h->inc_indent(); out_h->println("{"); - GenCaseStr(index_, out_h, env); + GenCaseStr(index_, out_h, env, case_type()->IndexExpr()->DataType(env)); out_h->inc_indent(); out_h->println("break; // OK"); out_h->dec_indent(); @@ -304,7 +384,7 @@ void CaseField::GenPubDecls(Output* out_h, Env* env) out_h->println("default:"); out_h->inc_indent(); out_h->println( - "throw binpac::ExceptionInvalidCase(\"%s\", %s, \"%s\");", + "throw binpac::ExceptionInvalidCase(\"%s\", (int64)%s, \"%s\");", id_->LocName(), env->RValue(index_var_), OrigExprList(index_).c_str()); @@ -335,7 +415,7 @@ void CaseField::GenInitCode(Output* out_cc, Env* env) void CaseField::GenCleanUpCode(Output* out_cc, Env* env) { - GenCaseStr(index_, out_cc, env); + GenCaseStr(index_, out_cc, env, case_type()->IndexExpr()->DataType(env)); out_cc->inc_indent(); out_cc->println("// Clean up \"%s\"", id_->Name()); out_cc->println("{"); @@ -349,7 +429,7 @@ void CaseField::GenCleanUpCode(Output* out_cc, Env* env) void CaseField::GenParseCode(Output* out_cc, Env* env, const DataPtr& data, const ID* size_var) { - GenCaseStr(index_, out_cc, env); + GenCaseStr(index_, out_cc, env, case_type()->IndexExpr()->DataType(env)); out_cc->inc_indent(); out_cc->println("// Parse \"%s\"", id_->Name()); out_cc->println("{"); diff --git a/tools/binpac/src/pac_case.h b/tools/binpac/src/pac_case.h index c9e416cfe1..7e7bb482ce 100644 --- a/tools/binpac/src/pac_case.h +++ b/tools/binpac/src/pac_case.h @@ -32,6 +32,8 @@ public: Type *ValueType() const; + Expr* IndexExpr() const { return index_expr_; } + bool IsPointerType() const { return ValueType()->IsPointerType(); } protected: @@ -94,6 +96,6 @@ protected: // Generate a list of "case X:" lines from index_list. Each index // expression must be constant foldable. -void GenCaseStr(ExprList *index_list, Output *out_cc, Env *env); +void GenCaseStr(ExprList *index_list, Output *out_cc, Env *env, Type* switch_type); #endif // pac_case_h diff --git a/tools/binpac/src/pac_exception.cc b/tools/binpac/src/pac_exception.cc index e9648495fd..64ad6663d9 100644 --- a/tools/binpac/src/pac_exception.cc +++ b/tools/binpac/src/pac_exception.cc @@ -69,3 +69,15 @@ ExceptionNonConstExpr::ExceptionNonConstExpr(const Expr* expr) { append(strfmt("Expression `%s' is not constant", expr->orig())); } + +ExceptionInvalidCaseSizeExpr::ExceptionInvalidCaseSizeExpr(const Expr* expr) + : Exception(expr), expr(expr) + { + append(strfmt("Expression `%s' is greater than the 32-bit limit for use as a case index", expr->orig())); + } + +ExceptionInvalidCaseLimitExpr::ExceptionInvalidCaseLimitExpr(const Expr* expr) + : Exception(expr), expr(expr) + { + append(strfmt("Expression `%s' as a case index is outside the numeric limit of the type used for the switch expression", expr->orig())); + } diff --git a/tools/binpac/src/pac_exception.h b/tools/binpac/src/pac_exception.h index 5920af0d54..c3320cf98e 100644 --- a/tools/binpac/src/pac_exception.h +++ b/tools/binpac/src/pac_exception.h @@ -92,4 +92,22 @@ private: const Expr *expr; }; +class ExceptionInvalidCaseSizeExpr : public Exception +{ +public: + ExceptionInvalidCaseSizeExpr(const Expr* expr); + +private: + const Expr *expr; +}; + +class ExceptionInvalidCaseLimitExpr : public Exception +{ +public: + ExceptionInvalidCaseLimitExpr(const Expr* expr); + +private: + const Expr *expr; +}; + #endif /* pac_exception_h */ diff --git a/tools/binpac/src/pac_expr.cc b/tools/binpac/src/pac_expr.cc index 3f74aa8e3f..4062525a57 100644 --- a/tools/binpac/src/pac_expr.cc +++ b/tools/binpac/src/pac_expr.cc @@ -257,6 +257,7 @@ void Expr::GenCaseEval(Output *out_cc, Env *env) (*i)->value()->ForceIDEval(out_cc, env); out_cc->println("switch ( %s )", operand_[0]->EvalExpr(out_cc, env)); + Type* switch_type = operand_[0]->DataType(env); out_cc->inc_indent(); out_cc->println("{"); @@ -274,7 +275,7 @@ void Expr::GenCaseEval(Output *out_cc, Env *env) } else { - GenCaseStr(index, out_cc, env); + GenCaseStr(index, out_cc, env, switch_type); out_cc->inc_indent(); out_cc->println("%s = %s;", env->LValue(val_var), @@ -285,7 +286,7 @@ void Expr::GenCaseEval(Output *out_cc, Env *env) } // Generate the default case after all other cases - GenCaseStr(0, out_cc, env); + GenCaseStr(0, out_cc, env, switch_type); out_cc->inc_indent(); if ( default_case ) { @@ -295,7 +296,7 @@ void Expr::GenCaseEval(Output *out_cc, Env *env) } else { - out_cc->println("throw binpac::ExceptionInvalidCaseIndex(\"%s\", %s);", + out_cc->println("throw binpac::ExceptionInvalidCaseIndex(\"%s\", (int64)%s);", Location(), operand_[0]->EvalExpr(out_cc, env)); } out_cc->println("break;"); diff --git a/tools/binpac/src/pac_let.cc b/tools/binpac/src/pac_let.cc index c850784554..06dd0416ce 100644 --- a/tools/binpac/src/pac_let.cc +++ b/tools/binpac/src/pac_let.cc @@ -126,7 +126,7 @@ LetDecl::LetDecl(ID *id, Type *type, Expr *expr) Env *env = global_env(); int c; if ( expr_ && expr_->ConstFold(env, &c) ) - env->AddConstID(id_, c); + env->AddConstID(id_, c, type); else env->AddID(id_, GLOBAL_VAR, type_); }