#include "pac_record.h" #include "pac_attr.h" #include "pac_dataptr.h" #include "pac_exception.h" #include "pac_expr.h" #include "pac_exttype.h" #include "pac_field.h" #include "pac_output.h" #include "pac_type.h" #include "pac_typedecl.h" #include "pac_utils.h" #include "pac_varfield.h" RecordType::RecordType(RecordFieldList* record_fields) : Type(RECORD) { // Here we assume that the type is a standalone type. value_var_ = nullptr; // Put all fields in fields_ foreach (i, RecordFieldList, record_fields) AddField(*i); // Put RecordField's in record_fields_ record_fields_ = record_fields; parsing_dataptr_var_field_ = nullptr; } RecordType::~RecordType() { // Do not delete_list(RecordFieldList, record_fields_) // because the fields are also in fields_. delete record_fields_; delete parsing_dataptr_var_field_; } const ID* RecordType::parsing_dataptr_var() const { return parsing_dataptr_var_field_ ? parsing_dataptr_var_field_->id() : nullptr; } bool RecordType::DefineValueVar() const { return false; } string RecordType::DataTypeStr() const { ASSERT(type_decl()); return strfmt("%s*", type_decl()->class_name().c_str()); } void RecordType::Prepare(Env* env, int flags) { ASSERT(flags & TO_BE_PARSED); RecordField* prev = nullptr; int offset = 0; int seq = 0; foreach (i, RecordFieldList, record_fields_) { RecordField* f = *i; f->set_record_type(this); f->set_prev(prev); if ( prev ) prev->set_next(f); prev = f; if ( offset >= 0 ) { f->set_static_offset(offset); int w = f->StaticSize(env, offset); if ( w < 0 ) offset = -1; else offset += w; } ++seq; f->set_parsing_state_seq(seq); } if ( incremental_parsing() ) { #if 0 ASSERT(! parsing_state_var_field_); ID *parsing_state_var_id = new ID("parsing_state"); parsing_state_var_field_ = new PrivVarField( parsing_state_var_id, extern_type_int->Clone()); AddField(parsing_state_var_field_); ID *parsing_dataptr_var_id = new ID("parsing_dataptr"); parsing_dataptr_var_field_ = new TempVarField( parsing_dataptr_var_id, extern_type_const_byteptr->Clone()); parsing_dataptr_var_field_->Prepare(env); #endif } Type::Prepare(env, flags); } void RecordType::GenPubDecls(Output* out_h, Env* env) { Type::GenPubDecls(out_h, env); } void RecordType::GenPrivDecls(Output* out_h, Env* env) { Type::GenPrivDecls(out_h, env); } void RecordType::GenInitCode(Output* out_cc, Env* env) { Type::GenInitCode(out_cc, env); } void RecordType::GenCleanUpCode(Output* out_cc, Env* env) { Type::GenCleanUpCode(out_cc, env); } void RecordType::DoGenParseCode(Output* out_cc, Env* env, const DataPtr& data, int flags) { if ( ! incremental_input() && StaticSize(env) >= 0 ) GenBoundaryCheck(out_cc, env, data); if ( incremental_parsing() ) { out_cc->println("switch ( %s ) {", env->LValue(parsing_state_id)); out_cc->println("case 0:"); out_cc->inc_indent(); foreach (i, RecordFieldList, record_fields_) { RecordField* f = *i; f->GenParseCode(out_cc, env); out_cc->println(""); } out_cc->println(""); out_cc->println("%s = true;", env->LValue(parsing_complete_var())); out_cc->dec_indent(); out_cc->println("}"); } else { ASSERT(data.id() == begin_of_data && data.offset() == 0); foreach (i, RecordFieldList, record_fields_) { RecordField* f = *i; f->GenParseCode(out_cc, env); out_cc->println(""); } if ( incremental_input() ) { ASSERT(parsing_complete_var()); out_cc->println("%s = true;", env->LValue(parsing_complete_var())); } } if ( ! incremental_input() && AddSizeVar(out_cc, env) ) { const DataPtr& end_of_record_dataptr = record_fields_->back()->getFieldEnd(out_cc, env); out_cc->println("%s = %s - %s;", env->LValue(size_var()), end_of_record_dataptr.ptr_expr(), env->RValue(begin_of_data)); env->SetEvaluated(size_var()); } if ( ! boundary_checked() ) { RecordField* last_field = record_fields_->back(); if ( ! last_field->BoundaryChecked() ) GenBoundaryCheck(out_cc, env, data); } } void RecordType::GenDynamicSize(Output* out_cc, Env* env, const DataPtr& data) { GenParseCode(out_cc, env, data, 0); } int RecordType::StaticSize(Env* env) const { int tot_w = 0; foreach (i, RecordFieldList, record_fields_) { RecordField* f = *i; int w = f->StaticSize(env, tot_w); if ( w < 0 ) return -1; tot_w += w; } return tot_w; } void RecordType::SetBoundaryChecked() { Type::SetBoundaryChecked(); if ( StaticSize(env()) < 0 || attr_length_expr_ ) // Don't assume sufficient bounds checking has been done on fields // if the record is of variable size or if its size is set from &length // (whose value is not necessarily trustworthy). return; foreach (i, RecordFieldList, record_fields_) { RecordField* f = *i; f->SetBoundaryChecked(); } } void RecordType::DoMarkIncrementalInput() { foreach (i, RecordFieldList, record_fields_) { RecordField* f = *i; f->type()->MarkIncrementalInput(); } } bool RecordType::DoTraverse(DataDepVisitor* visitor) { return Type::DoTraverse(visitor); } bool RecordType::ByteOrderSensitive() const { foreach (i, RecordFieldList, record_fields_) { RecordField* f = *i; if ( f->RequiresByteOrder() ) return true; } return false; } RecordField::RecordField(FieldType tof, ID* id, Type* type) : Field(tof, TYPE_TO_BE_PARSED | CLASS_MEMBER | PUBLIC_READABLE, id, type) { begin_of_field_dataptr = nullptr; end_of_field_dataptr = nullptr; field_size_expr = nullptr; field_offset_expr = nullptr; end_of_field_dataptr_var = nullptr; record_type_ = nullptr; prev_ = nullptr; next_ = nullptr; static_offset_ = -1; parsing_state_seq_ = 0; boundary_checked_ = false; } RecordField::~RecordField() { delete begin_of_field_dataptr; delete end_of_field_dataptr; delete[] field_size_expr; delete[] field_offset_expr; delete end_of_field_dataptr_var; } const DataPtr& RecordField::getFieldBegin(Output* out_cc, Env* env) { if ( prev() ) return prev()->getFieldEnd(out_cc, env); else { // The first field if ( ! begin_of_field_dataptr ) { begin_of_field_dataptr = new DataPtr(env, begin_of_data, 0); } return *begin_of_field_dataptr; } } const DataPtr& RecordField::getFieldEnd(Output* out_cc, Env* env) { if ( end_of_field_dataptr ) return *end_of_field_dataptr; const DataPtr& begin_ptr = getFieldBegin(out_cc, env); if ( record_type()->incremental_parsing() ) { ASSERT(0); if ( ! end_of_field_dataptr ) { const ID* dataptr_var = record_type()->parsing_dataptr_var(); ASSERT(dataptr_var); end_of_field_dataptr = new DataPtr(env, dataptr_var, 0); } } else { int field_offset; if ( begin_ptr.id() == begin_of_data ) field_offset = begin_ptr.offset(); else field_offset = -1; // unknown int field_size = StaticSize(env, field_offset); if ( field_size >= 0 ) // can be statically determinted { end_of_field_dataptr = new DataPtr(env, begin_ptr.id(), begin_ptr.offset() + field_size); } else { // If not, we add a variable for the offset after the field end_of_field_dataptr_var = new ID(strfmt("dataptr_after_%s", id()->Name())); env->AddID(end_of_field_dataptr_var, TEMP_VAR, extern_type_const_byteptr); GenFieldEnd(out_cc, env, begin_ptr); end_of_field_dataptr = new DataPtr(env, end_of_field_dataptr_var, 0); } } return *end_of_field_dataptr; } const char* RecordField::FieldSize(Output* out_cc, Env* env) { if ( field_size_expr ) return field_size_expr; const DataPtr& begin = getFieldBegin(out_cc, env); const DataPtr& end = getFieldEnd(out_cc, env); if ( begin.id() == end.id() ) field_size_expr = nfmt("%d", end.offset() - begin.offset()); else field_size_expr = nfmt("(%s - %s)", end.ptr_expr(), begin.ptr_expr()); return field_size_expr; } const char* RecordField::FieldOffset(Output* out_cc, Env* env) { if ( field_offset_expr ) return field_offset_expr; const DataPtr& begin = getFieldBegin(out_cc, env); if ( begin.id() == begin_of_data ) field_offset_expr = nfmt("%d", begin.offset()); else field_offset_expr = nfmt("(%s - %s)", begin.ptr_expr(), env->RValue(begin_of_data)); return field_offset_expr; } // The reasoning behind AttemptBoundaryCheck is: "If my next field // can check its boundary, then I don't have to check mine, and it // will save me a boundary-check." bool RecordField::AttemptBoundaryCheck(Output* out_cc, Env* env) { if ( boundary_checked_ ) return true; // If I do not even know my size till I parse the data, my // next field won't be able to check its boundary now. const DataPtr& begin = getFieldBegin(out_cc, env); if ( StaticSize(env, begin.AbsOffset(begin_of_data)) < 0 ) return false; // Now we ask the next field to check its boundary. if ( next() && next()->AttemptBoundaryCheck(out_cc, env) ) { // If it works, we are all set SetBoundaryChecked(); return true; } else // If it fails, then I can still try to do it by myself return GenBoundaryCheck(out_cc, env); } RecordDataField::RecordDataField(ID* id, Type* type) : RecordField(RECORD_FIELD, id, type) { ASSERT(type_); } RecordDataField::~RecordDataField() {} void RecordDataField::Prepare(Env* env) { Field::Prepare(env); env->SetEvalMethod(id_, this); env->SetField(id_, this); } void RecordDataField::GenParseCode(Output* out_cc, Env* env) { if ( env->Evaluated(id()) ) return; // Always evaluate record fields in order if parsing // is incremental. if ( record_type()->incremental_parsing() && prev() ) prev()->GenParseCode(out_cc, env); DataPtr data(env, nullptr, 0); if ( ! record_type()->incremental_parsing() ) { data = getFieldBegin(out_cc, env); Expr* len_expr = record_type()->attr_length_expr(); int len; if ( ! record_type()->buffer_input() || (len_expr && len_expr->ConstFold(env, &len)) ) AttemptBoundaryCheck(out_cc, env); } out_cc->println("// Parse \"%s\"", id_->Name()); #if 0 out_cc->println("DEBUG_MSG(\"%%.6f Parse %s\\n\", network_time());", id_->Name()); #endif type_->GenPreParsing(out_cc, env); if ( type_->incremental_input() ) { // The enclosing record type must be incrementally parsed out_cc->println("%s = %d;", env->LValue(parsing_state_id), parsing_state_seq()); out_cc->println("/* fall through */"); out_cc->dec_indent(); out_cc->println("case %d:", parsing_state_seq()); out_cc->println("{"); out_cc->inc_indent(); } type_->GenParseCode(out_cc, env, data, 0); if ( record_type()->incremental_parsing() ) { ASSERT(type_->incremental_input()); out_cc->println("if ( ! (%s) )", type_->parsing_complete(env).c_str()); out_cc->inc_indent(); out_cc->println("goto %s;", kNeedMoreData); out_cc->dec_indent(); } if ( record_type()->incremental_parsing() ) { #if 0 const ID *dataptr_var = record_type()->parsing_dataptr_var(); ASSERT(dataptr_var); out_cc->println("%s += (%s);", env->LValue(dataptr_var), type_->DataSize(out_cc, env, data).c_str()); #endif out_cc->println("}"); } SetBoundaryChecked(); } void RecordDataField::GenEval(Output* out_cc, Env* env) { GenParseCode(out_cc, env); } void RecordDataField::GenFieldEnd(Output* out_cc, Env* env, const DataPtr& field_begin) { out_cc->println("const_byteptr const %s = %s + (%s);", env->LValue(end_of_field_dataptr_var), field_begin.ptr_expr(), type_->DataSize(out_cc, env, field_begin).c_str()); env->SetEvaluated(end_of_field_dataptr_var); out_cc->println("BINPAC_ASSERT(%s <= %s);", env->RValue(end_of_field_dataptr_var), env->RValue(end_of_data)); } void RecordDataField::SetBoundaryChecked() { RecordField::SetBoundaryChecked(); type_->SetBoundaryChecked(); } bool RecordDataField::GenBoundaryCheck(Output* out_cc, Env* env) { if ( boundary_checked_ ) return true; type_->GenBoundaryCheck(out_cc, env, getFieldBegin(out_cc, env)); SetBoundaryChecked(); return true; } bool RecordDataField::DoTraverse(DataDepVisitor* visitor) { return Field::DoTraverse(visitor); } bool RecordDataField::RequiresAnalyzerContext() const { return Field::RequiresAnalyzerContext() || type()->RequiresAnalyzerContext(); } RecordPaddingField::RecordPaddingField(ID* id, PaddingType ptype, Expr* expr) : RecordField(PADDING_FIELD, id, nullptr), ptype_(ptype), expr_(expr) { wordsize_ = -1; } RecordPaddingField::~RecordPaddingField() {} void RecordPaddingField::Prepare(Env* env) { Field::Prepare(env); if ( ptype_ == PAD_TO_NEXT_WORD ) { if ( ! expr_->ConstFold(env, &wordsize_) ) throw ExceptionPaddingError(this, strfmt("padding word size not a constant")); } } void RecordPaddingField::GenParseCode(Output* out_cc, Env* env) { // Always evaluate record fields in order if parsing // is incremental. if ( record_type()->incremental_parsing() && prev() ) prev()->GenParseCode(out_cc, env); } int RecordPaddingField::StaticSize(Env* env, int offset) const { int length; int target_offset; int offset_in_word; switch ( ptype_ ) { case PAD_BY_LENGTH: return expr_->ConstFold(env, &length) ? length : -1; case PAD_TO_OFFSET: // If the current offset cannot be statically // determined, we need to Generate code to // check the offset if ( offset == -1 ) return -1; if ( ! expr_->ConstFold(env, &target_offset) ) return -1; // If both the current and target offsets // can be statically computed, we can get its // static size if ( offset > target_offset ) throw ExceptionPaddingError(this, strfmt("current offset = %d, " "target offset = %d", offset, target_offset)); return target_offset - offset; case PAD_TO_NEXT_WORD: if ( offset == -1 || wordsize_ == -1 ) return -1; offset_in_word = offset % wordsize_; return (offset_in_word == 0) ? 0 : wordsize_ - offset_in_word; } return -1; } void RecordPaddingField::GenFieldEnd(Output* out_cc, Env* env, const DataPtr& field_begin) { ASSERT(! env->Evaluated(end_of_field_dataptr_var)); char* padding_var; switch ( ptype_ ) { case PAD_BY_LENGTH: out_cc->println("if ( (%s) < 0 ) { // check for negative pad length", expr_->EvalExpr(out_cc, env)); out_cc->inc_indent(); out_cc->println("throw binpac::ExceptionInvalidStringLength(\"%s\", %s);", Location(), expr_->EvalExpr(out_cc, env)); out_cc->dec_indent(); out_cc->println("}"); out_cc->println(""); out_cc->println("const_byteptr const %s = %s + (%s);", env->LValue(end_of_field_dataptr_var), field_begin.ptr_expr(), expr_->EvalExpr(out_cc, env)); out_cc->println("// Checking out-of-bound padding for \"%s\"", field_id_str_.c_str()); out_cc->println("if ( %s > %s || %s < %s ) {", env->LValue(end_of_field_dataptr_var), env->RValue(end_of_data), env->LValue(end_of_field_dataptr_var), field_begin.ptr_expr()); out_cc->inc_indent(); out_cc->println("throw binpac::ExceptionOutOfBound(\"%s\",", field_id_str_.c_str()); out_cc->println(" (%s), ", expr_->EvalExpr(out_cc, env)); out_cc->println(" (%s) - (%s));", env->RValue(end_of_data), env->LValue(end_of_field_dataptr_var)); out_cc->dec_indent(); out_cc->println("}"); out_cc->println(""); break; case PAD_TO_OFFSET: out_cc->println("const_byteptr %s = %s + (%s);", env->LValue(end_of_field_dataptr_var), env->RValue(begin_of_data), expr_->EvalExpr(out_cc, env)); out_cc->println("if ( %s < %s ) {", env->LValue(end_of_field_dataptr_var), field_begin.ptr_expr()); out_cc->inc_indent(); out_cc->println("// throw binpac::ExceptionInvalidOffset(\"%s\", %s - %s, %s);", id_->LocName(), field_begin.ptr_expr(), env->RValue(begin_of_data), expr_->EvalExpr(out_cc, env)); out_cc->println("%s = %s;", env->LValue(end_of_field_dataptr_var), field_begin.ptr_expr()); out_cc->dec_indent(); out_cc->println("}"); out_cc->println("if ( %s > %s ) {", env->LValue(end_of_field_dataptr_var), env->RValue(end_of_data)); out_cc->inc_indent(); out_cc->println("throw binpac::ExceptionOutOfBound(\"%s\",", field_id_str_.c_str()); out_cc->println(" (%s), ", expr_->EvalExpr(out_cc, env)); out_cc->println(" (%s) - (%s));", env->RValue(end_of_data), env->LValue(end_of_field_dataptr_var)); out_cc->dec_indent(); out_cc->println("}"); break; case PAD_TO_NEXT_WORD: padding_var = nfmt("%s__size", id()->Name()); out_cc->println("int %s = (%s - %s) %% %d;", padding_var, field_begin.ptr_expr(), env->RValue(begin_of_data), wordsize_); out_cc->println("%s = (%s == 0) ? 0 : %d - %s;", padding_var, padding_var, wordsize_, padding_var); out_cc->println("const_byteptr const %s = %s + %s;", env->LValue(end_of_field_dataptr_var), field_begin.ptr_expr(), padding_var); delete[] padding_var; break; } env->SetEvaluated(end_of_field_dataptr_var); } bool RecordPaddingField::GenBoundaryCheck(Output* out_cc, Env* env) { if ( boundary_checked_ ) return true; const DataPtr& begin = getFieldBegin(out_cc, env); char* size; int ss = StaticSize(env, begin.AbsOffset(begin_of_data)); ASSERT(ss >= 0); size = nfmt("%d", ss); begin.GenBoundaryCheck(out_cc, env, size, field_id_str_.c_str()); delete[] size; SetBoundaryChecked(); return true; } bool RecordPaddingField::DoTraverse(DataDepVisitor* visitor) { return Field::DoTraverse(visitor) && (! expr_ || expr_->Traverse(visitor)); }