From 3ac26e23f6c61d9b5898a209760dcf97d3768aea Mon Sep 17 00:00:00 2001 From: Daniel Thaler Date: Thu, 31 Oct 2024 18:14:41 +0100 Subject: [PATCH] Allow better control over a2l updates The command line switch --update now accepts an optional parameter (FULL or ADDRESSES). A full update is the default if the parameter is missing, and acts the same as previously. If ADDRESSES is chosen, then only the addresses of a2l objects are updated, but all the type information and other info remains unchanged. Additionally, the switch --update-preserve has been removed. Instead there is now a switch --update-mode PRESERVE | STRICT. The --update-mode PRESERVE behaves like --update-preserve, and keeps unknown objects. With --update-mode STRICT, unknown objects, or invalid type settings in the a2l trigger an error instead. --- src/insert.rs | 6 +- src/main.rs | 140 +++- src/update/axis_pts.rs | 325 ++++++--- src/update/blob.rs | 125 ++-- src/update/characteristic.rs | 333 ++++++--- src/update/ifdata_update.rs | 58 +- src/update/instance.rs | 225 +++--- src/update/measurement.rs | 243 ++++--- src/update/mod.rs | 676 ++++++++++++++++-- src/update/typedef.rs | 70 +- tests/elffiles/update_test.c | 145 ++-- tests/elffiles/update_test.elf | Bin 28696 -> 27984 bytes tests/elffiles/update_typedef_test.c | 84 +++ tests/elffiles/update_typedef_test.elf | Bin 0 -> 28696 bytes tests/update_test1.a2l | 431 +++++------ tests/update_test2.a2l | 475 ++++++------ tests/update_typedef_test1.a2l | 255 +++++++ tests/update_typedef_test2.a2l | 243 +++++++ ...ate_test3.a2l => update_typedef_test3.a2l} | 0 ...ate_test4.a2l => update_typedef_test4.a2l} | 0 20 files changed, 2784 insertions(+), 1050 deletions(-) create mode 100644 tests/elffiles/update_typedef_test.c create mode 100644 tests/elffiles/update_typedef_test.elf create mode 100644 tests/update_typedef_test1.a2l create mode 100644 tests/update_typedef_test2.a2l rename tests/{update_test3.a2l => update_typedef_test3.a2l} (100%) rename tests/{update_test4.a2l => update_typedef_test4.a2l} (100%) diff --git a/src/insert.rs b/src/insert.rs index c43f691..732f72b 100644 --- a/src/insert.rs +++ b/src/insert.rs @@ -81,8 +81,7 @@ pub(crate) fn insert_items( || sym_info .typeinfo .get_arraytype() - .map(is_simple_type) - .unwrap_or(false) + .is_some_and(is_simple_type) { if is_calib { match insert_characteristic_sym( @@ -196,8 +195,7 @@ fn insert_measurement_sym( let typeinfo = sym_info .typeinfo .get_pointer(&debug_data.types) - .map(|(_, t)| t) - .unwrap_or(sym_info.typeinfo); + .map_or(sym_info.typeinfo, |(_, t)| t); // handle arrays and unwrap the typeinfo update::set_matrix_dim( diff --git a/src/main.rs b/src/main.rs index 6a92f28..004250a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,6 +7,7 @@ use std::{ fmt::Display, time::Instant, }; +use update::{UpdateMode, UpdateType}; mod datatype; mod dwarf; @@ -97,12 +98,6 @@ fn core() -> Result<(), String> { let show_xcp = *arg_matches .get_one::("SHOW_XCP") .expect("option show-xcp must always exist"); - let update = *arg_matches - .get_one::("UPDATE") - .expect("option update must always exist"); - let update_preserve = *arg_matches - .get_one::("SAFE_UPDATE") - .expect("option update-preserve must always exist"); let enable_structures = *arg_matches .get_one::("ENABLE_STRUCTURES") .expect("option enable-structures must always exist"); @@ -119,6 +114,7 @@ fn core() -> Result<(), String> { .get_one::("MERGEINCLUDES") .expect("option merge-includes must always exist"); let verbose = arg_matches.get_count("VERBOSE"); + let opt_update_type = arg_matches.get_one::("UPDATE_TYPE"); let now = Instant::now(); cond_print!( @@ -293,18 +289,28 @@ fn core() -> Result<(), String> { if let Some(debugdata) = &elf_info { // update addresses - if update || update_preserve { + if let Some(update_type) = opt_update_type { + let update_mode = arg_matches + .get_one::("UPDATE_MODE") + .unwrap_or(&UpdateMode::Default); + let mut log_msgs = Vec::::new(); - let summary = update::update_addresses( + let (summary, strict_error) = update::update_a2l( &mut a2l_file, debugdata, &mut log_msgs, - update_preserve, + *update_type, + *update_mode, enable_structures, ); - for msg in log_msgs { - cond_print!(verbose, now, msg); + let display_msg = if verbose > 0 || update_mode != &UpdateMode::Strict { + verbose + } else { + 1 + }; + for msg in &log_msgs { + cond_print!(display_msg, now, msg); } cond_print!(verbose, now, "Address update done\nSummary:"); @@ -348,6 +354,11 @@ fn core() -> Result<(), String> { summary.instance_updated, summary.instance_not_updated ) ); + + // in strict mode, exit with error if there are any problems + if update_mode == &UpdateMode::Strict && strict_error { + return Err("Exiting because strict mode is enabled.".to_string()); + } } // create new items @@ -640,21 +651,34 @@ fn get_args() -> ArgMatches { .number_of_values(0) .action(clap::ArgAction::SetTrue) ) - .arg(Arg::new("UPDATE") - .help("Update the addresses of all objects in the A2L file based on the elf file.\nObjects that cannot be found in the elf file will be deleted.\nThe arg --elffile must be present.") + .arg(Arg::new("UPDATE_TYPE") + .help("Update the A2L file based on the elf file. The update type can be one of: + FULL: Update the address and type info of all items. This is the default. + ADDRESSES: Update only the addresses. +The arg --elffile must be present.") .short('u') .long("update") - .number_of_values(0) - .action(clap::ArgAction::SetTrue) + .value_parser(UpdateTypeParser) + .num_args(0..=1) + .action(clap::ArgAction::Append) + .default_missing_value("FULL") .requires("ELFFILE") ) - .arg(Arg::new("SAFE_UPDATE") - .help("Update the addresses of all objects in the A2L file based on the elf file.\nObjects that cannot be found in the elf file will be preserved; their adresses will be set to zero.\nThe arg --elffile must be present.") - .long("update-preserve") - .number_of_values(0) - .action(clap::ArgAction::SetTrue) + .arg(Arg::new("UPDATE_MODE") + .help("Update the A2L file based on the elf file. Action can be one of: + DEFAULT: Unknown objects are removed, invalid settings are updated. + STRICT: Unknown objects or invalid settings trigger an error. + PRESERVE: Unknown objects are preserved, with the address set to zero. +The arg --update must be present.") + .long("update-mode") + .value_parser(UpdateModeParser) + .num_args(0..=1) + .action(clap::ArgAction::Append) + .default_missing_value("DEFAULT") .requires("ELFFILE") + .requires("UPDATE_TYPE") ) + .arg(Arg::new("ENABLE_STRUCTURES") .help("Enable the the use of INSTANCE, TYPEDEF_STRUCTURE & co. for all operations. Requires a2l version 1.7.1") .short('t') @@ -813,11 +837,6 @@ fn get_args() -> ArgMatches { .multiple(false) .required(true) ) - .group( - ArgGroup::new("UPDATE_ARGGROUP") - .args(["UPDATE", "SAFE_UPDATE"]) - .multiple(false) - ) .group( ArgGroup::new("INSERT_ARGGROUP") .args(["INSERT_CHARACTERISTIC", "INSERT_CHARACTERISTIC_RANGE", "INSERT_CHARACTERISTIC_REGEX", @@ -968,3 +987,74 @@ impl Display for A2lVersion { } } } + +#[derive(Clone, Copy)] +struct UpdateModeParser; + +impl clap::builder::TypedValueParser for UpdateModeParser { + type Value = UpdateMode; + + fn parse_ref( + &self, + cmd: &clap::Command, + arg: Option<&clap::Arg>, + value: &std::ffi::OsStr, + ) -> Result { + match value.to_string_lossy().as_ref() { + "DEFAULT" => Ok(UpdateMode::Default), + "STRICT" => Ok(UpdateMode::Strict), + "PRESERVE" => Ok(UpdateMode::Preserve), + _ => { + let mut err = + clap::Error::new(clap::error::ErrorKind::ValueValidation).with_cmd(cmd); + if let Some(arg) = arg { + err.insert( + clap::error::ContextKind::InvalidArg, + clap::error::ContextValue::String(arg.to_string()), + ); + } + let strval = value.to_string_lossy(); + err.insert( + clap::error::ContextKind::InvalidValue, + clap::error::ContextValue::String(String::from(strval)), + ); + Err(err) + } + } + } +} + +#[derive(Clone, Copy)] +struct UpdateTypeParser; + +impl clap::builder::TypedValueParser for UpdateTypeParser { + type Value = UpdateType; + + fn parse_ref( + &self, + cmd: &clap::Command, + arg: Option<&clap::Arg>, + value: &std::ffi::OsStr, + ) -> Result { + match value.to_string_lossy().as_ref() { + "FULL" => Ok(UpdateType::Full), + "ADDRESSES" => Ok(UpdateType::Addresses), + _ => { + let mut err = + clap::Error::new(clap::error::ErrorKind::ValueValidation).with_cmd(cmd); + if let Some(arg) = arg { + err.insert( + clap::error::ContextKind::InvalidArg, + clap::error::ContextValue::String(arg.to_string()), + ); + } + let strval = value.to_string_lossy(); + err.insert( + clap::error::ContextKind::InvalidValue, + clap::error::ContextValue::String(String::from(strval)), + ); + Err(err) + } + } + } +} diff --git a/src/update/axis_pts.rs b/src/update/axis_pts.rs index ec7dbcf..079366a 100644 --- a/src/update/axis_pts.rs +++ b/src/update/axis_pts.rs @@ -1,158 +1,251 @@ +use crate::datatype::get_a2l_datatype; use crate::dwarf::DwarfDataType; use crate::dwarf::{DebugData, TypeInfo}; +use crate::symbol::SymbolInfo; use crate::A2lVersion; use a2lfile::{A2lObject, AxisPts, Module}; use std::collections::HashMap; use std::collections::HashSet; +use std::vec; use crate::update::{ adjust_limits, enums::{cond_create_enum_conversion, update_enum_compu_methods}, get_axis_pts_x_memberid, get_inner_type, get_symbol_info, - ifdata_update::{update_ifdata, zero_if_data}, - log_update_errors, set_symbol_link, update_record_layout, + ifdata_update::{update_ifdata_address, update_ifdata_type, zero_if_data}, + make_symbol_link_string, set_symbol_link, update_record_layout, A2lUpdateInfo, A2lUpdater, }; -use super::{make_symbol_link_string, UpdateInfo}; +use super::UpdateResult; -pub(crate) fn update_module_axis_pts( - info: &mut UpdateInfo, - compu_method_index: &HashMap, -) -> (u32, u32) { +pub(crate) fn update_all_module_axis_pts( + data: &mut A2lUpdater, + info: &A2lUpdateInfo, +) -> Vec { let mut enum_convlist = HashMap::::new(); let mut removed_items = HashSet::::new(); let mut axis_pts_list = Vec::new(); - let mut axis_pts_updated: u32 = 0; - let mut axis_pts_not_updated: u32 = 0; + let mut results = vec![]; - std::mem::swap(&mut info.module.axis_pts, &mut axis_pts_list); + std::mem::swap(&mut data.module.axis_pts, &mut axis_pts_list); for mut axis_pts in axis_pts_list { - match update_axis_pts_address(&mut axis_pts, info.debug_data, info.version) { - Ok(typeinfo) => { - // the variable used for the axis should be a 1-dimensional array, or a struct containing a 1-dimensional array - // if the type is a struct, then the AXIS_PTS_X inside the referenced RECORD_LAYOUT tells us which member of the struct to use. - let member_id = get_axis_pts_x_memberid( - info.module, - &info.reclayout_info, - &axis_pts.deposit_record, - ); - if let Some(inner_typeinfo) = get_inner_type(typeinfo, member_id) { - match &inner_typeinfo.datatype { - DwarfDataType::Array { dim, arraytype, .. } => { - // update max_axis_points to match the size of the array - if !dim.is_empty() { - axis_pts.max_axis_points = dim[0] as u16; - } - if let DwarfDataType::Enum { enumerators, .. } = &arraytype.datatype { - // an array of enums? it could be done... - if axis_pts.conversion == "NO_COMPU_METHOD" { - axis_pts.conversion = arraytype - .name - .clone() - .unwrap_or_else(|| { - format!("{}_compu_method", axis_pts.name) - }) - .clone(); - } - cond_create_enum_conversion( - info.module, - &axis_pts.conversion, - enumerators, - ); - enum_convlist.insert(axis_pts.conversion.clone(), arraytype); - } - } - DwarfDataType::Enum { .. } => { - // likely not useful, because what purpose would an axis consisting of a single enum value serve? - enum_convlist.insert(axis_pts.conversion.clone(), typeinfo); - } - _ => {} - } - - let opt_compu_method = compu_method_index - .get(&axis_pts.conversion) - .and_then(|idx| info.module.compu_method.get(*idx)); - let (ll, ul) = adjust_limits( - inner_typeinfo, - axis_pts.lower_limit, - axis_pts.upper_limit, - opt_compu_method, - ); - axis_pts.lower_limit = ll; - axis_pts.upper_limit = ul; - } - - // update the data type in the referenced RECORD_LAYOUT - axis_pts.deposit_record = update_record_layout( - info.module, - &mut info.reclayout_info, - &axis_pts.deposit_record, - typeinfo, - ); - - // put the updated AXIS_PTS back on the module's list - info.module.axis_pts.push(axis_pts); - axis_pts_updated += 1; - } - Err(errmsgs) => { - log_update_errors(info.log_msgs, errmsgs, "AXIS_PTS", axis_pts.get_line()); - - if info.preserve_unknown { - axis_pts.address = 0; - zero_if_data(&mut axis_pts.if_data); - info.module.axis_pts.push(axis_pts); - } else { - // item is removed implicitly, because it is not added back to the list - removed_items.insert(axis_pts.name.clone()); - } - axis_pts_not_updated += 1; + let update_result = update_module_axis_pts(&mut axis_pts, info, data, &mut enum_convlist); + if matches!(update_result, UpdateResult::SymbolNotFound { .. }) { + if info.preserve_unknown { + axis_pts.address = 0; + zero_if_data(&mut axis_pts.if_data); + data.module.axis_pts.push(axis_pts); + } else { + removed_items.insert(axis_pts.name.clone()); } + } else { + data.module.axis_pts.push(axis_pts); } + results.push(update_result); } // update COMPU_VTABs and COMPU_VTAB_RANGEs based on the data types used in MEASUREMENTs etc. - update_enum_compu_methods(info.module, &enum_convlist); - cleanup_removed_axis_pts(info.module, &removed_items); + update_enum_compu_methods(data.module, &enum_convlist); + cleanup_removed_axis_pts(data.module, &removed_items); - (axis_pts_updated, axis_pts_not_updated) + results } -// update the address of an AXIS_PTS object -fn update_axis_pts_address<'a>( +fn update_module_axis_pts<'dbg>( axis_pts: &mut AxisPts, - debug_data: &'a DebugData, - version: A2lVersion, -) -> Result<&'a TypeInfo, Vec> { + info: &A2lUpdateInfo<'dbg>, + data: &mut A2lUpdater<'_>, + enum_convlist: &mut HashMap, +) -> UpdateResult { match get_symbol_info( &axis_pts.name, &axis_pts.symbol_link, &axis_pts.if_data, - debug_data, + info.debug_data, ) { + // match update_axis_pts_address(&mut axis_pts, info.debug_data, info.version) { Ok(sym_info) => { - if version >= A2lVersion::V1_6_0 { - // make sure a valid SYMBOL_LINK exists - let symbol_link_text = make_symbol_link_string(&sym_info, debug_data); - set_symbol_link(&mut axis_pts.symbol_link, symbol_link_text); + update_axis_pts_address(axis_pts, info.debug_data, info.version, &sym_info); + update_ifdata_address(&mut axis_pts.if_data, &sym_info.name, sym_info.address); + + if info.full_update { + // update the data type of the AXIS_PTS object + update_ifdata_type(&mut axis_pts.if_data, sym_info.typeinfo); + update_axis_pts_datatype(data, axis_pts, info, &sym_info, enum_convlist); + + UpdateResult::Updated + } else if info.strict_update { + // verify that the data type of the AXIS_PTS object is still correct + verify_axis_pts_datatype(data, info, axis_pts, sym_info) } else { - axis_pts.symbol_link = None; + // The address of the AXIS_PTS object has been updated, and no update of the data type was requested + UpdateResult::Updated + } + } + Err(errmsgs) => UpdateResult::SymbolNotFound { + blocktype: "AXIS_PTS", + name: axis_pts.name.clone(), + line: axis_pts.get_line(), + errors: errmsgs, + }, + } +} + +// update the address of an AXIS_PTS object +pub(crate) fn update_axis_pts_address( + axis_pts: &mut AxisPts, + debug_data: &DebugData, + version: A2lVersion, + sym_info: &SymbolInfo, +) { + if version >= A2lVersion::V1_6_0 { + // make sure a valid SYMBOL_LINK exists + let symbol_link_text = make_symbol_link_string(sym_info, debug_data); + set_symbol_link(&mut axis_pts.symbol_link, symbol_link_text); + } else { + axis_pts.symbol_link = None; + } + + if axis_pts.address == 0 { + // if the address was previously "0" then force it to be displayed as hex after the update + axis_pts.get_layout_mut().item_location.2 .1 = true; + } + axis_pts.address = sym_info.address as u32; +} + +// update the data type + associated info of an AXIS_PTS object +fn update_axis_pts_datatype<'dbg>( + data: &mut A2lUpdater, + axis_pts: &mut AxisPts, + info: &A2lUpdateInfo<'dbg>, + sym_info: &SymbolInfo<'dbg>, + enum_convlist: &mut HashMap, +) { + // the variable used for the axis should be a 1-dimensional array, or a struct containing a 1-dimensional array + // if the type is a struct, then the AXIS_PTS_X inside the referenced RECORD_LAYOUT tells us which member of the struct to use. + let member_id = + get_axis_pts_x_memberid(data.module, &data.reclayout_info, &axis_pts.deposit_record); + if let Some(inner_typeinfo) = get_inner_type(sym_info.typeinfo, member_id) { + match &inner_typeinfo.datatype { + DwarfDataType::Array { dim, arraytype, .. } => { + // this is the only reasonable case for an AXIS_PTS object + // update max_axis_points to match the size of the array + if !dim.is_empty() { + axis_pts.max_axis_points = dim[0] as u16; + } + update_axis_pts_conversion(data.module, axis_pts, arraytype, enum_convlist); + } + DwarfDataType::Enum { .. } => { + // likely not useful, because what purpose would an axis consisting of a single enum value serve? + // print warning? + axis_pts.max_axis_points = 1; + update_axis_pts_conversion(data.module, axis_pts, inner_typeinfo, enum_convlist); + } + _ => { + // this is a very strange AXIS_PTS object + // skip updating the data type, since there is no safe way to proceed + } + } + + let opt_compu_method = info + .compu_method_index + .get(&axis_pts.conversion) + .and_then(|idx| data.module.compu_method.get(*idx)); + let (ll, ul) = adjust_limits( + inner_typeinfo, + axis_pts.lower_limit, + axis_pts.upper_limit, + opt_compu_method, + ); + axis_pts.lower_limit = ll; + axis_pts.upper_limit = ul; + } + + // update the data type in the referenced RECORD_LAYOUT + axis_pts.deposit_record = update_record_layout( + data.module, + &mut data.reclayout_info, + &axis_pts.deposit_record, + sym_info.typeinfo, + ); +} + +fn update_axis_pts_conversion<'dbg>( + module: &mut Module, + axis_pts: &mut AxisPts, + typeinfo: &'dbg TypeInfo, + enum_convlist: &mut HashMap, +) { + if let DwarfDataType::Enum { enumerators, .. } = &typeinfo.datatype { + if axis_pts.conversion == "NO_COMPU_METHOD" { + axis_pts.conversion = typeinfo + .name + .clone() + .unwrap_or_else(|| format!("{}_compu_method", axis_pts.name)) + .clone(); + } + cond_create_enum_conversion(module, &axis_pts.conversion, enumerators); + enum_convlist.insert(axis_pts.conversion.clone(), typeinfo); + } + // can't delete existing COMPU_METHODs in an else branch, because they might contain user-defined conversion formulas +} + +fn verify_axis_pts_datatype( + data: &mut A2lUpdater, + info: &A2lUpdateInfo<'_>, + axis_pts: &AxisPts, + sym_info: SymbolInfo<'_>, +) -> UpdateResult { + let member_id = + get_axis_pts_x_memberid(data.module, &data.reclayout_info, &axis_pts.deposit_record); + if let Some(inner_typeinfo) = get_inner_type(sym_info.typeinfo, member_id) { + let max_axis_pts = if let DwarfDataType::Array { dim, .. } = &inner_typeinfo.datatype { + *dim.first().unwrap_or(&1) as u16 + } else { + 1 + }; + let opt_compu_method = info + .compu_method_index + .get(&axis_pts.conversion) + .and_then(|idx| data.module.compu_method.get(*idx)); + let (ll, ul) = adjust_limits( + inner_typeinfo, + axis_pts.lower_limit, + axis_pts.upper_limit, + opt_compu_method, + ); + + let mut bad_datatype = false; + if let Some(axis_pts_x) = data + .reclayout_info + .idxmap + .get(&axis_pts.deposit_record) + .and_then(|rl_idx| data.module.record_layout.get(*rl_idx)) + .and_then(|rl| rl.axis_pts_x.as_ref()) + { + let calc_datatype = get_a2l_datatype(inner_typeinfo); + if axis_pts_x.datatype != calc_datatype { + bad_datatype = true; } + } - if axis_pts.address == 0 { - // if the address was previously "0" then force it to be displayed as hex after the update - axis_pts.get_layout_mut().item_location.2.1 = true; + if max_axis_pts != axis_pts.max_axis_points + || ll != axis_pts.lower_limit + || ul != axis_pts.upper_limit + || bad_datatype + { + UpdateResult::InvalidDataType { + blocktype: "AXIS_PTS", + name: axis_pts.name.clone(), + line: axis_pts.get_line(), } - axis_pts.address = sym_info.address as u32; - update_ifdata( - &mut axis_pts.if_data, - &sym_info.name, - sym_info.typeinfo, - sym_info.address, - ); - - Ok(sym_info.typeinfo) + } else { + UpdateResult::Updated } - Err(errmsgs) => Err(errmsgs), + } else { + // returning "Updated" is very questionable - the AXIS_PTS is basically nonsense get_inner_type fails. + // But: There would definitely not be a data type change + UpdateResult::Updated } } diff --git a/src/update/blob.rs b/src/update/blob.rs index 92d56b1..a833659 100644 --- a/src/update/blob.rs +++ b/src/update/blob.rs @@ -1,72 +1,99 @@ -use crate::dwarf::{DebugData, TypeInfo}; +use crate::dwarf::DebugData; +use crate::symbol::SymbolInfo; use a2lfile::{A2lObject, Blob, Module}; use std::collections::HashSet; -use super::ifdata_update::{update_ifdata, zero_if_data}; +use super::ifdata_update::{update_ifdata_address, update_ifdata_type, zero_if_data}; use super::{ - cleanup_item_list, get_symbol_info, log_update_errors, make_symbol_link_string, set_symbol_link, + cleanup_item_list, get_symbol_info, make_symbol_link_string, set_symbol_link, A2lUpdateInfo, + A2lUpdater, UpdateResult, }; -pub(crate) fn update_module_blobs( - module: &mut Module, - debug_data: &DebugData, - log_msgs: &mut Vec, - preserve_unknown: bool, -) -> (u32, u32) { +// update all BLOB objects in a module +pub(crate) fn update_all_module_blobs( + data: &mut A2lUpdater, + info: &A2lUpdateInfo, +) -> Vec { let mut removed_items = HashSet::::new(); let mut blob_list = Vec::new(); - let mut blob_updated: u32 = 0; - let mut blob_not_updated: u32 = 0; - std::mem::swap(&mut module.blob, &mut blob_list); + let mut results = Vec::new(); + + std::mem::swap(&mut data.module.blob, &mut blob_list); for mut blob in blob_list { - match update_blob_address(&mut blob, debug_data) { - Ok(typeinfo) => { - blob.size = typeinfo.get_size() as u32; - module.blob.push(blob); - blob_updated += 1; + let update_result = update_module_blob(&mut blob, info); + if matches!(update_result, UpdateResult::SymbolNotFound { .. }) { + if info.preserve_unknown { + blob.start_address = 0; + zero_if_data(&mut blob.if_data); + data.module.blob.push(blob); + } else { + removed_items.insert(blob.name.clone()); } - Err(errmsgs) => { - log_update_errors(log_msgs, errmsgs, "BLOB", blob.get_line()); + } else { + data.module.blob.push(blob); + } + results.push(update_result); + } + cleanup_removed_blobs(data.module, &removed_items); + + results +} + +// update a single BLOB object +fn update_module_blob(blob: &mut Blob, info: &A2lUpdateInfo<'_>) -> UpdateResult { + match get_symbol_info( + &blob.name, + &blob.symbol_link, + &blob.if_data, + info.debug_data, + ) { + // match update_blob_address(&mut blob, debug_data) { + Ok(sym_info) => { + update_blob_address(blob, info.debug_data, &sym_info); - if preserve_unknown { - blob.start_address = 0; - zero_if_data(&mut blob.if_data); - module.blob.push(blob); + update_ifdata_address(&mut blob.if_data, &sym_info.name, sym_info.address); + + if info.full_update { + // update the data type of the BLOB object + update_ifdata_type(&mut blob.if_data, sym_info.typeinfo); + + blob.size = sym_info.typeinfo.get_size() as u32; + UpdateResult::Updated + } else if info.strict_update { + // a blob has no data type, but the blob size could be wrong + if blob.size != sym_info.typeinfo.get_size() as u32 { + UpdateResult::InvalidDataType { + blocktype: "BLOB", + name: blob.name.clone(), + line: blob.get_line(), + } } else { - // item is removed implicitly, because it is not added back to the list - removed_items.insert(blob.name.clone()); + UpdateResult::Updated } - blob_not_updated += 1; + } else { + // no data type update requested, and strict update is also not requested + UpdateResult::Updated } } + Err(errmsgs) => UpdateResult::SymbolNotFound { + blocktype: "BLOB", + name: blob.name.clone(), + line: blob.get_line(), + errors: errmsgs, + }, } - cleanup_removed_blobs(module, &removed_items); - - (blob_updated, blob_not_updated) } // update the address of a BLOB object -fn update_blob_address<'a>( +fn update_blob_address<'dbg>( blob: &mut Blob, - debug_data: &'a DebugData, -) -> Result<&'a TypeInfo, Vec> { - match get_symbol_info(&blob.name, &blob.symbol_link, &blob.if_data, debug_data) { - Ok(sym_info) => { - // make sure a valid SYMBOL_LINK exists - let symbol_link_text = make_symbol_link_string(&sym_info, debug_data); - set_symbol_link(&mut blob.symbol_link, symbol_link_text); - blob.start_address = sym_info.address as u32; - update_ifdata( - &mut blob.if_data, - &sym_info.name, - sym_info.typeinfo, - sym_info.address, - ); - - Ok(sym_info.typeinfo) - } - Err(errmsgs) => Err(errmsgs), - } + debug_data: &'dbg DebugData, + sym_info: &SymbolInfo<'dbg>, +) { + // make sure a valid SYMBOL_LINK exists + let symbol_link_text = make_symbol_link_string(sym_info, debug_data); + set_symbol_link(&mut blob.symbol_link, symbol_link_text); + blob.start_address = sym_info.address as u32; } pub(crate) fn cleanup_removed_blobs(module: &mut Module, removed_items: &HashSet) { diff --git a/src/update/characteristic.rs b/src/update/characteristic.rs index 28f8f2b..cdb1287 100644 --- a/src/update/characteristic.rs +++ b/src/update/characteristic.rs @@ -1,5 +1,7 @@ +use crate::datatype::get_a2l_datatype; use crate::dwarf::DwarfDataType; use crate::dwarf::{DebugData, TypeInfo}; +use crate::symbol::SymbolInfo; use crate::A2lVersion; use a2lfile::{A2lObject, AxisDescr, Characteristic, CharacteristicType, Module, RecordLayout}; use std::collections::HashMap; @@ -9,88 +11,157 @@ use crate::update::{ adjust_limits, cleanup_item_list, enums::{cond_create_enum_conversion, update_enum_compu_methods}, get_fnc_values_memberid, get_inner_type, get_symbol_info, - ifdata_update::{update_ifdata, zero_if_data}, - log_update_errors, make_symbol_link_string, set_bitmask, set_matrix_dim, set_symbol_link, - update_record_layout, RecordLayoutInfo, UpdateInfo, + ifdata_update::{update_ifdata_address, update_ifdata_type, zero_if_data}, + make_symbol_link_string, set_bitmask, set_matrix_dim, set_symbol_link, update_record_layout, + A2lUpdateInfo, A2lUpdater, UpdateResult, }; -pub(crate) fn update_module_characteristics( - info: &mut UpdateInfo, - compu_method_index: &HashMap, -) -> (u32, u32) { +// update all CHARACTERISTICs in the module +pub(crate) fn update_all_module_characteristics( + data: &mut A2lUpdater, + info: &A2lUpdateInfo<'_>, +) -> Vec { let mut enum_convlist = HashMap::::new(); let mut removed_items = HashSet::::new(); let mut characteristic_list = Vec::new(); - let mut characteristic_updated: u32 = 0; - let mut characteristic_not_updated: u32 = 0; + // let mut characteristic_updated: u32 = 0; + // let mut characteristic_not_updated: u32 = 0; + let mut results = vec![]; // store the max_axis_points of each AXIS_PTS, so that the AXIS_DESCRs inside of CHARACTERISTICS can be updated to match - let axis_pts_dim: HashMap = info + let axis_pts_dim: HashMap = data .module .axis_pts .iter() .map(|item| (item.name.clone(), item.max_axis_points)) .collect(); - std::mem::swap(&mut info.module.characteristic, &mut characteristic_list); + std::mem::swap(&mut data.module.characteristic, &mut characteristic_list); for mut characteristic in characteristic_list { - if characteristic.virtual_characteristic.is_none() { - // only update the address if the CHARACTERISTIC is not a VIRTUAL_CHARACTERISTIC - match update_characteristic_address(&mut characteristic, info.debug_data, info.version) - { - Ok(typeinfo) => { + let update_result = update_module_characteristic( + &mut characteristic, + info, + data, + &mut enum_convlist, + &axis_pts_dim, + ); + if matches!(update_result, UpdateResult::SymbolNotFound { .. }) { + if info.preserve_unknown { + characteristic.address = 0; + zero_if_data(&mut characteristic.if_data); + data.module.characteristic.push(characteristic); + } else { + removed_items.insert(characteristic.name.clone()); + } + } else { + data.module.characteristic.push(characteristic); + } + results.push(update_result); + } + + // update COMPU_VTABs and COMPU_VTAB_RANGEs based on the data types used in CHARACTERISTICs + update_enum_compu_methods(data.module, &enum_convlist); + cleanup_removed_characteristics(data.module, &removed_items); + + results +} + +// update a single CHARACTERISTIC object +fn update_module_characteristic<'dbg>( + characteristic: &mut Characteristic, + info: &A2lUpdateInfo<'dbg>, + data: &mut A2lUpdater<'_>, + enum_convlist: &mut HashMap, + axis_pts_dim: &HashMap, +) -> UpdateResult { + if characteristic.virtual_characteristic.is_none() { + // only update the address if the CHARACTERISTIC is not a VIRTUAL_CHARACTERISTIC + match get_symbol_info( + &characteristic.name, + &characteristic.symbol_link, + &characteristic.if_data, + info.debug_data, + ) { + Ok(sym_info) => { + update_characteristic_address( + characteristic, + info.debug_data, + info.version, + &sym_info, + ); + + update_ifdata_address( + &mut characteristic.if_data, + &sym_info.name, + sym_info.address, + ); + + if info.full_update { + // update the data type of the CHARACTERISTIC object + update_ifdata_type(&mut characteristic.if_data, sym_info.typeinfo); + // update as much as possible of the information inside the CHARACTERISTIC - update_characteristic_information( - info.module, - &mut info.reclayout_info, - &mut characteristic, - typeinfo, - &mut enum_convlist, - &axis_pts_dim, + update_characteristic_datatype( + data, + characteristic, + sym_info.typeinfo, + enum_convlist, + axis_pts_dim, info.version >= A2lVersion::V1_7_0, - compu_method_index, + &info.compu_method_index, ); - - info.module.characteristic.push(characteristic); - characteristic_updated += 1; - } - Err(errmsgs) => { - log_update_errors( - info.log_msgs, - errmsgs, - "CHARACTERISTIC", - characteristic.get_line(), - ); - - if info.preserve_unknown { - characteristic.address = 0; - zero_if_data(&mut characteristic.if_data); - info.module.characteristic.push(characteristic); - } else { - // item is removed implicitly, because it is not added back to the list - removed_items.insert(characteristic.name.clone()); - } - characteristic_not_updated += 1; + UpdateResult::Updated + } else if info.strict_update { + // verify that the data type of the CHARACTERISTIC object is still correct + verify_characteristic_datatype( + data, + info, + characteristic, + sym_info.typeinfo, + info.version >= A2lVersion::V1_7_0, + ) + } else { + // no type update, but the address was updated + UpdateResult::Updated } } - } else { - // computed CHARACTERISTICS with a VIRTUAL_CHARACTERISTIC block shouldn't have an address and don't need to be updated - info.module.characteristic.push(characteristic); + Err(errors) => UpdateResult::SymbolNotFound { + blocktype: "CHARACTERISTIC", + name: characteristic.name.clone(), + line: characteristic.get_line(), + errors, + }, } + } else { + // computed CHARACTERISTICS with a VIRTUAL_CHARACTERISTIC block shouldn't have an address and don't need to be updated + UpdateResult::Updated } +} - // update COMPU_VTABs and COMPU_VTAB_RANGEs based on the data types used in CHARACTERISTICs - update_enum_compu_methods(info.module, &enum_convlist); - cleanup_removed_characteristics(info.module, &removed_items); +// update the address of a CHARACTERISTIC +fn update_characteristic_address<'dbg>( + characteristic: &mut Characteristic, + debug_data: &'dbg DebugData, + version: A2lVersion, + sym_info: &SymbolInfo<'dbg>, +) { + if version >= A2lVersion::V1_6_0 { + // make sure a valid SYMBOL_LINK exists + let symbol_link_text = make_symbol_link_string(sym_info, debug_data); + set_symbol_link(&mut characteristic.symbol_link, symbol_link_text); + } else { + characteristic.symbol_link = None; + } - (characteristic_updated, characteristic_not_updated) + if characteristic.address == 0 { + characteristic.get_layout_mut().item_location.3 .1 = true; + } + characteristic.address = sym_info.address as u32; } // update as much as possible of the information inside the CHARACTERISTIC -#[allow(clippy::too_many_arguments)] -fn update_characteristic_information<'enumlist, 'typeinfo: 'enumlist>( - module: &mut Module, - recordlayout_info: &mut RecordLayoutInfo, +fn update_characteristic_datatype<'enumlist, 'typeinfo: 'enumlist>( + data: &mut A2lUpdater, characteristic: &mut Characteristic, typeinfo: &'typeinfo TypeInfo, enum_convlist: &'enumlist mut HashMap, @@ -98,7 +169,8 @@ fn update_characteristic_information<'enumlist, 'typeinfo: 'enumlist>( use_new_matrix_dim: bool, compu_method_index: &HashMap, ) { - let member_id = get_fnc_values_memberid(module, recordlayout_info, &characteristic.deposit); + let member_id = + get_fnc_values_memberid(data.module, &data.reclayout_info, &characteristic.deposit); if let Some(inner_typeinfo) = get_inner_type(typeinfo, member_id) { if let DwarfDataType::Enum { enumerators, .. } = &inner_typeinfo.datatype { let enum_name = inner_typeinfo @@ -108,13 +180,13 @@ fn update_characteristic_information<'enumlist, 'typeinfo: 'enumlist>( if characteristic.conversion == "NO_COMPU_METHOD" { characteristic.conversion = enum_name; } - cond_create_enum_conversion(module, &characteristic.conversion, enumerators); + cond_create_enum_conversion(data.module, &characteristic.conversion, enumerators); enum_convlist.insert(characteristic.conversion.clone(), inner_typeinfo); } let opt_compu_method = compu_method_index .get(&characteristic.conversion) - .and_then(|idx| module.compu_method.get(*idx)); + .and_then(|idx| data.module.compu_method.get(*idx)); let (ll, ul) = adjust_limits( inner_typeinfo, characteristic.lower_limit, @@ -123,6 +195,8 @@ fn update_characteristic_information<'enumlist, 'typeinfo: 'enumlist>( ); characteristic.lower_limit = ll; characteristic.upper_limit = ul; + + set_bitmask(&mut characteristic.bit_mask, inner_typeinfo); } // Patch up incomplete characteristics: Curve, Map, Cuboid, Cube4 and Cube5 all require AXIS_DESCR to function correctly @@ -160,19 +234,24 @@ fn update_characteristic_information<'enumlist, 'typeinfo: 'enumlist>( characteristic.matrix_dim = None; } - let record_layout = if let Some(idx) = recordlayout_info.idxmap.get(&characteristic.deposit) { - Some(&module.record_layout[*idx]) + let record_layout = if let Some(idx) = data.reclayout_info.idxmap.get(&characteristic.deposit) { + Some(&data.module.record_layout[*idx]) } else { None }; + update_characteristic_axis( &mut characteristic.axis_descr, record_layout, axis_pts_dim, typeinfo, ); - characteristic.deposit = - update_record_layout(module, recordlayout_info, &characteristic.deposit, typeinfo); + characteristic.deposit = update_record_layout( + data.module, + &mut data.reclayout_info, + &characteristic.deposit, + typeinfo, + ); } // update all the AXIS_DESCRs inside a CHARACTERISTIC (or TYPEDEF_CHARACTERISTIC) @@ -213,7 +292,7 @@ pub(crate) fn update_characteristic_axis( } } else if idx <= 5 { // an internal axis, using info from the typeinfo and the record layout - if let Some(position) = axis_positions[idx] { + if let Some(&Some(position)) = axis_positions.get(idx) { if let Some(TypeInfo { datatype: DwarfDataType::Array { dim, .. }, .. @@ -226,42 +305,106 @@ pub(crate) fn update_characteristic_axis( } } -// update the address of a CHARACTERISTIC -fn update_characteristic_address<'a>( - characteristic: &mut Characteristic, - debug_data: &'a DebugData, - version: A2lVersion, -) -> Result<&'a TypeInfo, Vec> { - match get_symbol_info( - &characteristic.name, - &characteristic.symbol_link, - &characteristic.if_data, - debug_data, - ) { - Ok(sym_info) => { - if version >= A2lVersion::V1_6_0 { - // make sure a valid SYMBOL_LINK exists - let symbol_link_text = make_symbol_link_string(&sym_info, debug_data); - set_symbol_link(&mut characteristic.symbol_link, symbol_link_text); - } else { - characteristic.symbol_link = None; +fn verify_characteristic_datatype<'dbg>( + data: &mut A2lUpdater, + info: &A2lUpdateInfo<'dbg>, + characteristic: &Characteristic, + typeinfo: &'dbg TypeInfo, + use_new_matrix_dim: bool, +) -> UpdateResult { + let mut bad_characteristic = false; + let member_id = + get_fnc_values_memberid(data.module, &data.reclayout_info, &characteristic.deposit); + if let Some(inner_typeinfo) = get_inner_type(typeinfo, member_id) { + if let DwarfDataType::Enum { .. } = &inner_typeinfo.datatype { + if characteristic.conversion == "NO_COMPU_METHOD" { + bad_characteristic = true; + } + } + + let opt_compu_method = info + .compu_method_index + .get(&characteristic.conversion) + .and_then(|idx| data.module.compu_method.get(*idx)); + let (ll, ul) = adjust_limits( + inner_typeinfo, + characteristic.lower_limit, + characteristic.upper_limit, + opt_compu_method, + ); + if ll != characteristic.lower_limit || ul != characteristic.upper_limit { + bad_characteristic = true; + } + + let mut dummy_bitmask = characteristic.bit_mask.clone(); + set_bitmask(&mut dummy_bitmask, inner_typeinfo); + + let mut dummy_matrix_dim = characteristic.matrix_dim.clone(); + match characteristic.characteristic_type { + CharacteristicType::Value => { + // a scalar value should not have a matrix dimension, either before or after the update + set_matrix_dim(&mut dummy_matrix_dim, inner_typeinfo, use_new_matrix_dim); + if dummy_matrix_dim.is_some() + || characteristic.matrix_dim.is_some() + || characteristic.number.is_some() + { + bad_characteristic = true; + } + } + CharacteristicType::ValBlk => { + // the matrix dim of a ValBlk must exist and remain unchanged + set_matrix_dim(&mut dummy_matrix_dim, inner_typeinfo, use_new_matrix_dim); + if characteristic.matrix_dim.is_none() + || dummy_matrix_dim != characteristic.matrix_dim + { + bad_characteristic = true; + } + } + CharacteristicType::Map + | CharacteristicType::Curve + | CharacteristicType::Cuboid + | CharacteristicType::Cube4 + | CharacteristicType::Cube5 => { + // map ... cube5 should each have axis_descr describing their axes + if characteristic.axis_descr.is_empty() { + bad_characteristic = true; + } + } + CharacteristicType::Ascii => { + // no extra checks for ASCII } + } - if characteristic.address == 0 { - characteristic.get_layout_mut().item_location.3.1 = true; + // check if the data type of the deposit record is correct + // to do this, we need to look up the record layout, and get its fnc_values + if let Some(fnc_values) = data + .reclayout_info + .idxmap + .get(&characteristic.deposit) + .and_then(|rl_idx| data.module.record_layout.get(*rl_idx)) + .and_then(|rl| rl.fnc_values.as_ref()) + { + let a2l_datatype = get_a2l_datatype(inner_typeinfo); + if a2l_datatype != fnc_values.datatype { + bad_characteristic = true; } - characteristic.address = sym_info.address as u32; - set_bitmask(&mut characteristic.bit_mask, sym_info.typeinfo); - update_ifdata( - &mut characteristic.if_data, - &sym_info.name, - sym_info.typeinfo, - sym_info.address, - ); + } else { + // no record layout found, or no fnc_values in the record layout: the characteristic is invalid + bad_characteristic = true; + } + } else { + // no inner type found: the characteristic is invalid + bad_characteristic = true; + } - Ok(sym_info.typeinfo) + if bad_characteristic { + UpdateResult::InvalidDataType { + blocktype: "CHARACTERISTIC", + name: characteristic.name.clone(), + line: characteristic.get_line(), } - Err(errmsgs) => Err(errmsgs), + } else { + UpdateResult::Updated } } diff --git a/src/update/ifdata_update.rs b/src/update/ifdata_update.rs index 6b6b5ca..60f3040 100644 --- a/src/update/ifdata_update.rs +++ b/src/update/ifdata_update.rs @@ -3,38 +3,64 @@ use crate::ifdata; use a2lfile::{A2lObject, IfData}; // check if there is a CANAPE_EXT in the IF_DATA vec and update it if it exists -pub(crate) fn update_ifdata( - ifdata_vec: &mut Vec, - symbol_name: &str, - datatype: &TypeInfo, - address: u64, -) { +pub(crate) fn update_ifdata_address(ifdata_vec: &mut Vec, symbol_name: &str, address: u64) { for ifdata in ifdata_vec { if let Some(mut decoded_ifdata) = ifdata::A2mlVector::load_from_ifdata(ifdata) { if let Some(canape_ext) = &mut decoded_ifdata.canape_ext { - update_ifdata_canape_ext(canape_ext, address, symbol_name, datatype); + update_ifdata_address_canape_ext(canape_ext, address, symbol_name); decoded_ifdata.store_to_ifdata(ifdata); } else if let Some(asap1b_ccp) = &mut decoded_ifdata.asap1b_ccp { - update_ifdata_asap1b_ccp(asap1b_ccp, address, datatype); + update_ifdata_address_asap1b_ccp(asap1b_ccp, address); decoded_ifdata.store_to_ifdata(ifdata); } } } } -fn update_ifdata_canape_ext( +fn update_ifdata_address_canape_ext( canape_ext: &mut ifdata::CanapeExt, address: u64, symbol_name: &str, - typeinfo: &TypeInfo, ) { if let Some(link_map) = &mut canape_ext.link_map { if link_map.address == 0 { // if the address was previously "0" then force it to be displayed as hex after the update - link_map.get_layout_mut().item_location.1.1 = true; + link_map.get_layout_mut().item_location.1 .1 = true; } link_map.address = address as i32; link_map.symbol_name = symbol_name.to_string(); + // these can be set to valid values later on by update_ifdata_type_canape_ext + link_map.datatype = 0; + link_map.bit_offset = 0; + link_map.datatype_valid = 0; + } +} + +fn update_ifdata_address_asap1b_ccp(asap1b_ccp: &mut ifdata::Asap1bCcp, address: u64) { + if let Some(dp_blob) = &mut asap1b_ccp.dp_blob { + dp_blob.address_extension = 0; + dp_blob.base_address = address as u32; + dp_blob.size = 0; + } +} + +// check if there is a CANAPE_EXT in the IF_DATA vec and update it if it exists +pub(crate) fn update_ifdata_type(ifdata_vec: &mut Vec, datatype: &TypeInfo) { + for ifdata in ifdata_vec { + if let Some(mut decoded_ifdata) = ifdata::A2mlVector::load_from_ifdata(ifdata) { + if let Some(canape_ext) = &mut decoded_ifdata.canape_ext { + update_ifdata_type_canape_ext(canape_ext, datatype); + decoded_ifdata.store_to_ifdata(ifdata); + } else if let Some(asap1b_ccp) = &mut decoded_ifdata.asap1b_ccp { + update_ifdata_type_asap1b_ccp(asap1b_ccp, datatype); + decoded_ifdata.store_to_ifdata(ifdata); + } + } + } +} + +fn update_ifdata_type_canape_ext(canape_ext: &mut ifdata::CanapeExt, typeinfo: &TypeInfo) { + if let Some(link_map) = &mut canape_ext.link_map { match &typeinfo.datatype { DwarfDataType::Uint8 => { link_map.datatype = 0x87; @@ -114,7 +140,7 @@ fn update_ifdata_canape_ext( link_map.datatype_valid = 1; } DwarfDataType::Array { arraytype, .. } => { - update_ifdata_canape_ext(canape_ext, address, symbol_name, arraytype); + update_ifdata_type_canape_ext(canape_ext, arraytype); } _ => { link_map.datatype = 0; @@ -125,11 +151,8 @@ fn update_ifdata_canape_ext( } } -fn update_ifdata_asap1b_ccp(asap1b_ccp: &mut ifdata::Asap1bCcp, address: u64, typeinfo: &TypeInfo) { +fn update_ifdata_type_asap1b_ccp(asap1b_ccp: &mut ifdata::Asap1bCcp, typeinfo: &TypeInfo) { if let Some(dp_blob) = &mut asap1b_ccp.dp_blob { - dp_blob.address_extension = 0; - dp_blob.base_address = address as u32; - match &typeinfo.datatype { DwarfDataType::Uint8 | DwarfDataType::Sint8 => dp_blob.size = 1, DwarfDataType::Uint16 | DwarfDataType::Sint16 => dp_blob.size = 2, @@ -140,6 +163,9 @@ fn update_ifdata_asap1b_ccp(asap1b_ccp: &mut ifdata::Asap1bCcp, address: u64, ty dp_blob.size = 8; } DwarfDataType::Enum { size, .. } => dp_blob.size = *size as u32, + DwarfDataType::Array { arraytype, .. } => { + update_ifdata_type_asap1b_ccp(asap1b_ccp, arraytype); + } _ => { // size is not set because we don't know // for example if the datatype is Struct, then the record_layout must be taken into the calculation diff --git a/src/update/instance.rs b/src/update/instance.rs index 9f9d033..c0a6ac9 100644 --- a/src/update/instance.rs +++ b/src/update/instance.rs @@ -1,128 +1,173 @@ -use crate::dwarf::{DebugData, TypeInfo}; +use crate::{ + dwarf::{DebugData, TypeInfo}, + symbol::SymbolInfo, +}; use a2lfile::{A2lObject, Instance, Module}; use std::collections::HashSet; use crate::update::{ cleanup_removed_axis_pts, cleanup_removed_blobs, cleanup_removed_characteristics, cleanup_removed_measurements, get_symbol_info, - ifdata_update::{update_ifdata, zero_if_data}, - log_update_errors, set_symbol_link, TypedefNames, TypedefReferrer, TypedefsRefInfo, + ifdata_update::{update_ifdata_address, update_ifdata_type, zero_if_data}, + make_symbol_link_string, set_address_type, set_matrix_dim, set_symbol_link, A2lUpdateInfo, + A2lUpdater, TypedefNames, TypedefReferrer, TypedefsRefInfo, UpdateResult, }; -use super::{make_symbol_link_string, set_address_type, set_matrix_dim, UpdateInfo}; - -pub(crate) fn update_module_instances<'dbg>( - info: &mut UpdateInfo<'_, 'dbg, '_>, +// update all INSTANCE objects in a module +pub(crate) fn update_all_module_instances<'dbg>( + data: &mut A2lUpdater, + info: &A2lUpdateInfo<'dbg>, nameset: &TypedefNames, -) -> (u32, u32, TypedefsRefInfo<'dbg>) { +) -> (Vec, TypedefsRefInfo<'dbg>) { let mut removed_items = HashSet::::new(); - let mut instance_list = Vec::new(); - let mut instance_updated: u32 = 0; - let mut instance_not_updated: u32 = 0; let mut typedef_types = TypedefsRefInfo::new(); - std::mem::swap(&mut info.module.instance, &mut instance_list); + let mut results = Vec::new(); + + let mut instance_list = Vec::new(); + std::mem::swap(&mut data.module.instance, &mut instance_list); for mut instance in instance_list { - match update_instance_address(&mut instance, info.debug_data) { - Ok((typedef_ref, typeinfo)) => { - if nameset.contains(&typedef_ref) { - // Each INSTANCE can have: - // - an ADDRESS_TYPE, which means that it is a pointer to some data - // - a MATRIX_DIM, meaning this instance is an array of some data - // when ADDRESS_TYPE and MATRIX_DIM are present at the same time, the INSTANCE represents - // a pointer to an array, not an array of pointers. - // - // Therefore the typeinfo should be transformed to the base data type by first unwrapping - // one pointer (if any), and then getting an array element type (if any) - // More complicted constructions like pointers to pointers, arrays of pointers, etc. can not be represented directly - set_address_type(&mut instance.address_type, typeinfo); - let basetype = typeinfo - .get_pointer(&info.debug_data.types) - .map_or(typeinfo, |(_, t)| t); - - set_matrix_dim(&mut instance.matrix_dim, basetype, true); - let basetype = basetype.get_arraytype().unwrap_or(basetype); - - typedef_types.entry(typedef_ref).or_default().push(( - Some(basetype), - TypedefReferrer::Instance(info.module.instance.len()), - )); - - info.module.instance.push(instance); - instance_updated += 1; - } else if info.preserve_unknown { - info.module.instance.push(instance); - instance_updated += 1; - } else { - info.log_msgs.push(format!("Error updating INSTANCE on line {}: type ref {} does not refer to any TYPEDEF_*", instance.get_line(), instance.type_ref)); - instance_not_updated += 1; - } - } - Err(errmsgs) => { - log_update_errors(info.log_msgs, errmsgs, "INSTANCE", instance.get_line()); - - if info.preserve_unknown { - instance.start_address = 0; - zero_if_data(&mut instance.if_data); - typedef_types - .entry(instance.type_ref.clone()) - .or_default() - .push((None, TypedefReferrer::Instance(info.module.instance.len()))); - info.module.instance.push(instance); - } else { - // item is removed implicitly, because it is not added back to the list - removed_items.insert(instance.name.clone()); - } - instance_not_updated += 1; + let (update_result, opt_typeinfo) = update_module_instance(&mut instance, info, nameset); + + // prepare the typedef map entry for the instance + let entry = typedef_types.entry(instance.type_ref.clone()); + let len = data.module.instance.len(); + let typedef_map_value = (opt_typeinfo, TypedefReferrer::Instance(len)); + + if matches!(update_result, UpdateResult::SymbolNotFound { .. }) { + if info.preserve_unknown { + instance.start_address = 0; + zero_if_data(&mut instance.if_data); + data.module.instance.push(instance); + // the typedef_map_value is a dummy value here, whose typeinfo is None + // this makes sure that the corresponding TYPEDEF_* object is retained. + // This only matters if enable_structures is set, since TYPEDEFS are not modified otherwise. + entry.or_default().push(typedef_map_value); + } else { + removed_items.insert(instance.name.clone()); } + } else { + data.module.instance.push(instance); + // store the typeinfo and the index of the INSTANCE object to enable updating the TYPEDEF_* object later + entry.or_default().push(typedef_map_value); } + results.push(update_result); } - cleanup_removed_instances(info.module, &removed_items); + cleanup_removed_instances(data.module, &removed_items); - (instance_updated, instance_not_updated, typedef_types) + (results, typedef_types) } -// update the address of an INSTANCE object -fn update_instance_address<'a>( +// update a single INSTANCE object +fn update_module_instance<'dbg>( instance: &mut Instance, - debug_data: &'a DebugData, -) -> Result<(String, &'a TypeInfo), Vec> { + info: &A2lUpdateInfo<'dbg>, + nameset: &TypedefNames, +) -> (UpdateResult, Option<&'dbg TypeInfo>) { match get_symbol_info( &instance.name, &instance.symbol_link, &instance.if_data, - debug_data, + info.debug_data, ) { + // match update_instance_address(&mut instance, info.debug_data) { Ok(sym_info) => { - // make sure a valid SYMBOL_LINK exists - let symbol_link_text = make_symbol_link_string(&sym_info, debug_data); - set_symbol_link(&mut instance.symbol_link, symbol_link_text); + update_instance_address(instance, info.debug_data, &sym_info); + update_ifdata_address(&mut instance.if_data, &sym_info.name, sym_info.address); - if instance.start_address == 0 { - // if the start address was previously "0" then force it to be displayed as hex after the update - instance.get_layout_mut().item_location.3.1 = true; - } - instance.start_address = sym_info.address as u32; + let type_ref_valid = nameset.contains(&instance.type_ref); - let typeinfo = sym_info + // save the typeinfo associated with the TYPEDEF_* object. + // Do this even for invalid type references, because the TYPEDEF_* object might be added later. + let basetype = sym_info .typeinfo - .get_pointer(&debug_data.types) + .get_pointer(&info.debug_data.types) .map_or(sym_info.typeinfo, |(_, t)| t); - let typeinfo = typeinfo.get_arraytype().unwrap_or(typeinfo); - update_ifdata( - &mut instance.if_data, - &sym_info.name, - typeinfo, - sym_info.address, - ); + let basetype = basetype.get_arraytype().unwrap_or(basetype); - // return the name of the linked TYPEDEF_ - Ok((instance.type_ref.clone(), sym_info.typeinfo)) + if info.full_update { + if type_ref_valid { + update_instance_datatype(info, instance, sym_info.typeinfo); + } + (UpdateResult::Updated, Some(basetype)) + } else if info.strict_update { + // Verify that the data type of the INSTANCE object is still correct: + // Since update_instance_datatype does not modify any referenced data, it is + // possible to simply compare the instance before and after the update + let mut instance_copy = instance.clone(); + if type_ref_valid { + update_instance_datatype(info, &mut instance_copy, sym_info.typeinfo); + } + if *instance != instance_copy { + let result = UpdateResult::InvalidDataType { + blocktype: "INSTANCE", + name: instance.name.clone(), + line: instance.get_line(), + }; + (result, Some(basetype)) + } else { + (UpdateResult::Updated, Some(basetype)) + } + } else { + // The address of the INSTANCE object has been updated, and no update of the data type was requested + (UpdateResult::Updated, Some(basetype)) + } + } + Err(errmsgs) => { + let result = UpdateResult::SymbolNotFound { + blocktype: "INSTANCE", + name: instance.name.clone(), + line: instance.get_line(), + errors: errmsgs, + }; + (result, None) } - Err(errmsgs) => Err(errmsgs), } } +fn update_instance_datatype( + info: &A2lUpdateInfo, + instance: &mut Instance, + typeinfo: &crate::dwarf::TypeInfo, +) { + // Each INSTANCE can have: + // - an ADDRESS_TYPE, which means that it is a pointer to some data + // - a MATRIX_DIM, meaning this instance is an array of some data + // when ADDRESS_TYPE and MATRIX_DIM are present at the same time, the INSTANCE represents + // a pointer to an array, not an array of pointers. + // + // Therefore the typeinfo should be transformed to the base data type by first unwrapping + // one pointer (if any), and then getting an array element type (if any) + // More complicted constructions like pointers to pointers, arrays of pointers, etc. can not be represented directly + set_address_type(&mut instance.address_type, typeinfo); + let typeinfo_1 = typeinfo + .get_pointer(&info.debug_data.types) + .map_or(typeinfo, |(_, t)| t); + + set_matrix_dim(&mut instance.matrix_dim, typeinfo_1, true); + + // update the data type of the INSTANCE - this only uses the innermost type + let typeinfo_2 = typeinfo_1.get_arraytype().unwrap_or(typeinfo_1); + update_ifdata_type(&mut instance.if_data, typeinfo_2); +} + +// update the address of an INSTANCE object +fn update_instance_address<'a>( + instance: &mut Instance, + debug_data: &'a DebugData, + sym_info: &SymbolInfo<'a>, +) { + // make sure a valid SYMBOL_LINK exists + let symbol_link_text = make_symbol_link_string(sym_info, debug_data); + set_symbol_link(&mut instance.symbol_link, symbol_link_text); + + if instance.start_address == 0 { + // if the start address was previously "0" then force it to be displayed as hex after the update + instance.get_layout_mut().item_location.3 .1 = true; + } + instance.start_address = sym_info.address as u32; +} + pub(crate) fn cleanup_removed_instances(module: &mut Module, removed_items: &HashSet) { // INSTANCEs can take the place of AXIS_PTS, BLOBs, CHARACTERISTICs or MEASUREMENTs, depending on which kind of TYPEDEF the instance is based on cleanup_removed_axis_pts(module, removed_items); diff --git a/src/update/measurement.rs b/src/update/measurement.rs index a1c24ae..196c73d 100644 --- a/src/update/measurement.rs +++ b/src/update/measurement.rs @@ -1,5 +1,6 @@ use crate::dwarf::DwarfDataType; use crate::dwarf::{DebugData, TypeInfo}; +use crate::symbol::SymbolInfo; use crate::A2lVersion; use a2lfile::{A2lObject, Measurement, Module}; use std::collections::HashMap; @@ -9,92 +10,135 @@ use crate::update::{ adjust_limits, cleanup_item_list, enums::{cond_create_enum_conversion, update_enum_compu_methods}, get_a2l_datatype, get_symbol_info, - ifdata_update::{update_ifdata, zero_if_data}, - log_update_errors, set_bitmask, set_matrix_dim, set_measurement_ecu_address, set_symbol_link, + ifdata_update::{update_ifdata_address, update_ifdata_type, zero_if_data}, + set_bitmask, set_matrix_dim, set_measurement_ecu_address, set_symbol_link, A2lUpdater, }; -use super::{make_symbol_link_string, set_address_type, UpdateInfo}; +use super::{make_symbol_link_string, set_address_type, A2lUpdateInfo, UpdateResult}; -pub(crate) fn update_module_measurements( - info: &mut UpdateInfo, - compu_method_index: &HashMap, -) -> (u32, u32) { +pub(crate) fn update_all_module_measurements( + data: &mut A2lUpdater, + info: &A2lUpdateInfo, +) -> Vec { let mut removed_items = HashSet::::new(); let mut enum_convlist = HashMap::::new(); let mut measurement_list = Vec::new(); - let mut measurement_updated: u32 = 0; - let mut measurement_not_updated: u32 = 0; + let mut results = Vec::new(); - std::mem::swap(&mut info.module.measurement, &mut measurement_list); + std::mem::swap(&mut data.module.measurement, &mut measurement_list); for mut measurement in measurement_list { - if measurement.var_virtual.is_none() { - // only MEASUREMENTS that are not VIRTUAL can be updated - match update_measurement_address(&mut measurement, info.debug_data, info.version) { - Ok(typeinfo) => { - // update all the information instide a MEASUREMENT - update_content( - info.module, - info.debug_data, - &mut measurement, - typeinfo, - &mut enum_convlist, - info.version >= A2lVersion::V1_7_0, - compu_method_index, - ); + let update_result = + update_module_measurement(&mut measurement, info, data, &mut enum_convlist); + if matches!(update_result, UpdateResult::SymbolNotFound { .. }) { + if info.preserve_unknown { + measurement.ecu_address = None; + zero_if_data(&mut measurement.if_data); + data.module.measurement.push(measurement); + } else { + removed_items.insert(measurement.name.clone()); + } + } else { + data.module.measurement.push(measurement); + } + results.push(update_result); + } - info.module.measurement.push(measurement); - measurement_updated += 1; - } - Err(errmsgs) => { - log_update_errors( - info.log_msgs, - errmsgs, - "MEASUREMENT", - measurement.get_line(), + // update COMPU_VTABs and COMPU_VTAB_RANGEs based on the data types used in MEASUREMENTs + update_enum_compu_methods(data.module, &enum_convlist); + cleanup_removed_measurements(data.module, &removed_items); + + results +} + +fn update_module_measurement<'dbg>( + measurement: &mut Measurement, + info: &A2lUpdateInfo<'dbg>, + data: &mut A2lUpdater<'_>, + enum_convlist: &mut HashMap, +) -> UpdateResult { + if measurement.var_virtual.is_none() { + // only MEASUREMENTS that are not VIRTUAL can be updated + match get_symbol_info( + &measurement.name, + &measurement.symbol_link, + &measurement.if_data, + info.debug_data, + ) { + // match update_measurement_address(&mut measurement, info.debug_data, info.version) { + Ok(sym_info) => { + update_measurement_address(measurement, info.debug_data, info.version, &sym_info); + + update_ifdata_address(&mut measurement.if_data, &sym_info.name, sym_info.address); + + if info.full_update { + // update the data type of the MEASUREMENT object + update_ifdata_type(&mut measurement.if_data, sym_info.typeinfo); + + // update all the information instide a MEASUREMENT + update_measurement_datatype( + info, + data.module, + measurement, + sym_info.typeinfo, + enum_convlist, ); - if info.preserve_unknown { - measurement.ecu_address = None; - zero_if_data(&mut measurement.if_data); - info.module.measurement.push(measurement); - } else { - // item is removed implicitly, because it is not added back to the list - // but we need to track the name of the removed item so that references to it can be deleted - removed_items.insert(measurement.name.clone()); - } - measurement_not_updated += 1; + UpdateResult::Updated + } else if info.strict_update { + // verify that the data type of the MEASUREMENT object is still correct + verify_measurement_datatype(info, data.module, measurement, sym_info.typeinfo) + } else { + // no type update, but the address was updated + UpdateResult::Updated } } - } else { - // VIRTUAL MEASUREMENTS don't need an address - info.module.measurement.push(measurement); + Err(errmsgs) => UpdateResult::SymbolNotFound { + blocktype: "MEASUREMENT", + name: measurement.name.clone(), + line: measurement.get_line(), + errors: errmsgs, + }, } + } else { + // VIRTUAL MEASUREMENTS don't have an address, and don't need to be updated + UpdateResult::Updated } +} - // update COMPU_VTABs and COMPU_VTAB_RANGEs based on the data types used in MEASUREMENTs - update_enum_compu_methods(info.module, &enum_convlist); - cleanup_removed_measurements(info.module, &removed_items); +// update the address of a MEASUREMENT object +fn update_measurement_address<'dbg>( + measurement: &mut Measurement, + debug_data: &'dbg DebugData, + version: A2lVersion, + sym_info: &SymbolInfo<'dbg>, +) { + if version >= A2lVersion::V1_6_0 { + // make sure a valid SYMBOL_LINK exists + let symbol_link_text = make_symbol_link_string(sym_info, debug_data); + set_symbol_link(&mut measurement.symbol_link, symbol_link_text); + } else { + measurement.symbol_link = None; + } - (measurement_updated, measurement_not_updated) + set_measurement_ecu_address(&mut measurement.ecu_address, sym_info.address); } -// update datatype, limits and dimension of a MEASURMENT -pub(crate) fn update_content<'enumlist, 'typeinfo: 'enumlist>( +// update datatype, limits and dimension of a MEASUREMENT +fn update_measurement_datatype<'enumlist, 'typeinfo: 'enumlist>( + info: &A2lUpdateInfo<'typeinfo>, module: &mut Module, - debug_data: &'typeinfo DebugData, measurement: &mut Measurement, typeinfo: &'typeinfo TypeInfo, enum_convlist: &'enumlist mut HashMap, - use_new_matrix_dim: bool, - compu_method_index: &HashMap, ) { // handle pointers - only allowed for version 1.7.0+ (the caller should take care of this precondition) set_address_type(&mut measurement.address_type, typeinfo); let typeinfo = typeinfo - .get_pointer(&debug_data.types) + .get_pointer(&info.debug_data.types) .map_or(typeinfo, |(_, t)| t); // handle arrays and unwrap the typeinfo + let use_new_matrix_dim = info.version >= A2lVersion::V1_7_0; set_matrix_dim(&mut measurement.matrix_dim, typeinfo, use_new_matrix_dim); measurement.array_size = None; let typeinfo = typeinfo.get_arraytype().unwrap_or(typeinfo); @@ -110,7 +154,8 @@ pub(crate) fn update_content<'enumlist, 'typeinfo: 'enumlist>( enum_convlist.insert(measurement.conversion.clone(), typeinfo); } - let opt_compu_method = compu_method_index + let opt_compu_method = info + .compu_method_index .get(&measurement.conversion) .and_then(|idx| module.compu_method.get(*idx)); let (ll, ul) = adjust_limits( @@ -126,38 +171,64 @@ pub(crate) fn update_content<'enumlist, 'typeinfo: 'enumlist>( set_bitmask(&mut measurement.bit_mask, typeinfo); } -// update the address of a MEASUREMENT object -fn update_measurement_address<'a>( - measurement: &mut Measurement, - debug_data: &'a DebugData, - version: A2lVersion, -) -> Result<&'a TypeInfo, Vec> { - match get_symbol_info( - &measurement.name, - &measurement.symbol_link, - &measurement.if_data, - debug_data, - ) { - Ok(sym_info) => { - if version >= A2lVersion::V1_6_0 { - // make sure a valid SYMBOL_LINK exists - let symbol_link_text = make_symbol_link_string(&sym_info, debug_data); - set_symbol_link(&mut measurement.symbol_link, symbol_link_text); - } else { - measurement.symbol_link = None; - } +fn verify_measurement_datatype<'enumlist, 'typeinfo: 'enumlist>( + info: &A2lUpdateInfo<'typeinfo>, + module: &Module, + measurement: &Measurement, + typeinfo: &'typeinfo TypeInfo, +) -> UpdateResult { + // handle pointers - only allowed for version 1.7.0+ (the caller should take care of this precondition) + let mut dummy_address_type = measurement.address_type.clone(); + set_address_type(&mut dummy_address_type, typeinfo); + let typeinfo = typeinfo + .get_pointer(&info.debug_data.types) + .map_or(typeinfo, |(_, t)| t); + + // handle arrays and unwrap the typeinfo + let use_new_matrix_dim = info.version >= A2lVersion::V1_7_0; + let mut dummy_matrix_dim = measurement.matrix_dim.clone(); + set_matrix_dim(&mut dummy_matrix_dim, typeinfo, use_new_matrix_dim); + let typeinfo = typeinfo.get_arraytype().unwrap_or(typeinfo); + + let mut bad_conversion = false; + if let DwarfDataType::Enum { .. } = &typeinfo.datatype { + if measurement.conversion == "NO_COMPU_METHOD" { + // the type is enum, so there should be a conversion method, but there is none + bad_conversion = true; + } + } + + let opt_compu_method = info + .compu_method_index + .get(&measurement.conversion) + .and_then(|idx| module.compu_method.get(*idx)); + let (ll, ul) = adjust_limits( + typeinfo, + measurement.lower_limit, + measurement.upper_limit, + opt_compu_method, + ); - set_measurement_ecu_address(&mut measurement.ecu_address, sym_info.address); - update_ifdata( - &mut measurement.if_data, - &sym_info.name, - sym_info.typeinfo, - sym_info.address, - ); + let computed_datatype = get_a2l_datatype(typeinfo); + let mut dummy_bitmask = measurement.bit_mask.clone(); + set_bitmask(&mut dummy_bitmask, typeinfo); - Ok(sym_info.typeinfo) + if dummy_address_type != measurement.address_type + || dummy_matrix_dim != measurement.matrix_dim + || dummy_bitmask != measurement.bit_mask + || ll != measurement.lower_limit + || ul != measurement.upper_limit + || computed_datatype != measurement.datatype + || bad_conversion + { + // the information based on the data type of the MEASUREMENT is not correct + UpdateResult::InvalidDataType { + blocktype: "MEASUREMENT", + name: measurement.name.clone(), + line: measurement.get_line(), } - Err(errmsgs) => Err(errmsgs), + } else { + UpdateResult::Updated } } diff --git a/src/update/mod.rs b/src/update/mod.rs index ecd6bcc..cd78ebd 100644 --- a/src/update/mod.rs +++ b/src/update/mod.rs @@ -4,7 +4,9 @@ use a2lfile::{ A2lFile, A2lObject, AddrType, AddressType, BitMask, CompuMethod, EcuAddress, IfData, MatrixDim, Module, SymbolLink, }; +use instance::update_all_module_instances; use std::collections::{HashMap, HashSet}; +use std::ops::AddAssign; mod axis_pts; mod blob; @@ -20,13 +22,26 @@ use crate::datatype::{get_a2l_datatype, get_type_limits}; use crate::dwarf::DwarfDataType; use crate::symbol::{find_symbol, SymbolInfo}; use axis_pts::*; -use blob::{cleanup_removed_blobs, update_module_blobs}; +use blob::{cleanup_removed_blobs, update_all_module_blobs}; use characteristic::*; -use instance::update_module_instances; use measurement::*; use record_layout::*; use typedef::update_module_typedefs; +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum UpdateType { + Full, + Addresses, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum UpdateMode { + Default, + Strict, + Preserve, +} + +#[derive(Debug, Clone)] pub(crate) struct UpdateSumary { pub(crate) measurement_updated: u32, pub(crate) measurement_not_updated: u32, @@ -54,13 +69,40 @@ pub(crate) struct TypedefNames { structure: HashSet, } -pub(crate) struct UpdateInfo<'a2l, 'dbg, 'log> { - pub(crate) module: &'a2l mut Module, +#[derive(Debug, Clone, PartialEq)] +enum UpdateResult { + Updated, + SymbolNotFound { + blocktype: &'static str, + name: String, + line: u32, + errors: Vec, + }, + InvalidDataType { + blocktype: &'static str, + name: String, + line: u32, + }, +} + +// the data used by the a2l update has been split into two parts. +// The A2lUpdateInfo struct contains the data that is constant for the whole update process. +#[derive(Debug)] +pub(crate) struct A2lUpdateInfo<'dbg> { pub(crate) debug_data: &'dbg DebugData, - pub(crate) log_msgs: &'log mut Vec, pub(crate) preserve_unknown: bool, + pub(crate) strict_update: bool, + pub(crate) full_update: bool, pub(crate) version: A2lVersion, - pub(crate) reclayout_info: RecordLayoutInfo, + pub(crate) enable_structures: bool, + pub(crate) compu_method_index: HashMap, +} + +// This struct contains the data that is modified / updated during the a2l update process. +#[derive(Debug)] +pub(crate) struct A2lUpdater<'a2l> { + module: &'a2l mut Module, + reclayout_info: RecordLayoutInfo, } type TypedefsRefInfo<'a> = HashMap, TypedefReferrer)>>; @@ -68,75 +110,126 @@ type TypedefsRefInfo<'a> = HashMap, TypedefRef // perform an address update. // This update can be destructive (any object that cannot be updated will be discarded) // or non-destructive (addresses of invalid objects will be set to zero). -pub(crate) fn update_addresses( +pub(crate) fn update_a2l( a2l_file: &mut A2lFile, debug_data: &DebugData, log_msgs: &mut Vec, - preserve_unknown: bool, + update_type: UpdateType, + update_mode: UpdateMode, enable_structures: bool, -) -> UpdateSumary { +) -> (UpdateSumary, bool) { let version = A2lVersion::from(&*a2l_file); - let mut summary = UpdateSumary::new(); + let mut strict_error = false; for module in &mut a2l_file.project.module { - let reclayout_info = RecordLayoutInfo::build(module); - let mut info = UpdateInfo { + let (mut data, update_info) = init_update( + debug_data, + module, + version, + update_type, + update_mode, + enable_structures, + ); + let (module_summary, module_strict_error) = run_update(&mut data, &update_info, log_msgs); + summary += module_summary; + strict_error |= module_strict_error; + } + (summary, strict_error) +} + +pub fn init_update<'a2l, 'dbg>( + debug_data: &'dbg DebugData, + module: &'a2l mut Module, + version: A2lVersion, + update_type: UpdateType, + update_mode: UpdateMode, + enable_structures: bool, +) -> (A2lUpdater<'a2l>, A2lUpdateInfo<'dbg>) { + let preserve_unknown = update_mode == UpdateMode::Preserve; + let strict_update = update_mode == UpdateMode::Strict; + let full_update = update_type == UpdateType::Full; + let reclayout_info = RecordLayoutInfo::build(module); + + let compu_method_index = module + .compu_method + .iter() + .enumerate() + .map(|(idx, item)| (item.name.clone(), idx)) + .collect::>(); + ( + A2lUpdater { module, + reclayout_info, + }, + A2lUpdateInfo { debug_data, - log_msgs, preserve_unknown, + strict_update, + full_update, version, - reclayout_info, - }; + enable_structures, + compu_method_index, + }, + ) +} - let compu_method_index = info - .module - .compu_method - .iter() - .enumerate() - .map(|(idx, item)| (item.name.clone(), idx)) - .collect::>(); - - // update all AXIS_PTS - let (updated, not_updated) = update_module_axis_pts(&mut info, &compu_method_index); - summary.measurement_updated += updated; - summary.measurement_not_updated += not_updated; - - // update all MEASUREMENTs - let (updated, not_updated) = update_module_measurements(&mut info, &compu_method_index); - summary.measurement_updated += updated; - summary.measurement_not_updated += not_updated; - - // update all CHARACTERISTICs - let (updated, not_updated) = update_module_characteristics(&mut info, &compu_method_index); - summary.characteristic_updated += updated; - summary.characteristic_not_updated += not_updated; - - // update all BLOBs - let (updated, not_updated) = - update_module_blobs(info.module, debug_data, info.log_msgs, preserve_unknown); - summary.blob_updated += updated; - summary.blob_not_updated += not_updated; - - let typedef_names = TypedefNames::new(info.module); - - // update all INSTANCEs - let (updated, not_updated, typedef_ref_info) = - update_module_instances(&mut info, &typedef_names); - summary.instance_updated += updated; - summary.instance_not_updated += not_updated; - - if enable_structures { - update_module_typedefs( - &mut info, - typedef_ref_info, - typedef_names, - &compu_method_index, - ); - } +fn run_update( + data: &mut A2lUpdater, + info: &A2lUpdateInfo, + log_msgs: &mut Vec, +) -> (UpdateSumary, bool) { + let mut summary = UpdateSumary::new(); + let mut strict_error = false; + + // update all AXIS_PTS + let result = update_all_module_axis_pts(data, info); + strict_error |= result.iter().any(|r| r != &UpdateResult::Updated); + let (updated, not_updated) = log_update_results(log_msgs, &result); + summary.axis_pts_updated += updated; + summary.axis_pts_not_updated += not_updated; + + // update all MEASUREMENTs + let results = update_all_module_measurements(data, info); + strict_error |= results.iter().any(|r| r != &UpdateResult::Updated); + let (updated, not_updated) = log_update_results(log_msgs, &results); + summary.measurement_updated += updated; + summary.measurement_not_updated += not_updated; + + // update all CHARACTERISTICs + let results = update_all_module_characteristics(data, info); + strict_error |= results.iter().any(|r| r != &UpdateResult::Updated); + let (updated, not_updated) = log_update_results(log_msgs, &results); + summary.characteristic_updated += updated; + summary.characteristic_not_updated += not_updated; + + // update all BLOBs + let results = update_all_module_blobs(data, info); + strict_error |= results.iter().any(|r| r != &UpdateResult::Updated); + let (updated, not_updated) = log_update_results(log_msgs, &results); + summary.blob_updated += updated; + summary.blob_not_updated += not_updated; + + let typedef_names = TypedefNames::new(data.module); + + // update all INSTANCEs + let (update_result, typedef_ref_info) = update_all_module_instances(data, info, &typedef_names); + strict_error |= results.iter().any(|r| r != &UpdateResult::Updated); + let (updated, not_updated) = log_update_results(log_msgs, &update_result); + summary.instance_updated += updated; + summary.instance_not_updated += not_updated; + + if info.full_update && info.enable_structures { + update_module_typedefs( + info, + data.module, + log_msgs, + typedef_ref_info, + typedef_names, + &mut data.reclayout_info, + ); } - summary + (summary, strict_error) } // try to get the symbol name used in the elf file, and find its address and type @@ -202,6 +295,42 @@ fn log_update_errors(errorlog: &mut Vec, errmsgs: Vec, blockname } } +fn log_update_results(errorlog: &mut Vec, results: &[UpdateResult]) -> (u32, u32) { + let mut updated = 0; + let mut not_updated = 0; + for result in results { + match result { + UpdateResult::Updated => updated += 1, + UpdateResult::SymbolNotFound { + blocktype, + name, + line, + errors, + } => { + for err in errors { + errorlog.push(format!( + "Error updating {blocktype} {name} on line {line}: {err}", + )); + } + log_update_errors(errorlog, errors.clone(), blocktype, *line); + not_updated += 1; + } + UpdateResult::InvalidDataType { + blocktype, + name, + line, + } => { + errorlog.push(format!( + "Error updating {blocktype} {name} on line {line}: data type has changed", + )); + updated += 1; + } + } + } + + (updated, not_updated) +} + pub(crate) fn make_symbol_link_string(sym_info: &SymbolInfo, debug_data: &DebugData) -> String { let mut name = sym_info.name.to_string(); if !sym_info.is_unique { @@ -274,7 +403,7 @@ fn set_measurement_ecu_address(opt_ecu_address: &mut Option, address if let Some(ecu_address) = opt_ecu_address { if ecu_address.address == 0 { // force hex output for the address, if the address was set as "0" (decimal) - ecu_address.get_layout_mut().item_location.0.1 = true; + ecu_address.get_layout_mut().item_location.0 .1 = true; } ecu_address.address = address as u32; } else { @@ -491,11 +620,30 @@ impl TypedefNames { } } +impl AddAssign for UpdateSumary { + fn add_assign(&mut self, other: Self) { + self.axis_pts_not_updated += other.axis_pts_not_updated; + self.axis_pts_updated += other.axis_pts_updated; + self.blob_not_updated += other.blob_not_updated; + self.blob_updated += other.blob_updated; + self.characteristic_not_updated += other.characteristic_not_updated; + self.characteristic_updated += other.characteristic_updated; + self.measurement_not_updated += other.measurement_not_updated; + self.measurement_updated += other.measurement_updated; + self.instance_not_updated += other.instance_not_updated; + self.instance_updated += other.instance_updated; + } +} + #[cfg(test)] mod test { - use super::adjust_limits; - use crate::dwarf::{DwarfDataType, TypeInfo}; + use super::*; + use crate::{ + dwarf::{DwarfDataType, TypeInfo}, + A2lVersion, + }; use a2lfile::{Coeffs, CoeffsLinear, CompuMethod, ConversionType}; + use std::ffi::OsString; #[test] fn test_adjust_limits() { @@ -538,4 +686,404 @@ mod test { assert_eq!(lower, 0.0); assert_eq!(upper, 10200.0); } + + fn test_setup(a2l_name: &str) -> (crate::dwarf::DebugData, a2lfile::A2lFile) { + let mut log_msgs = Vec::new(); + let a2l = a2lfile::load(a2l_name, None, &mut log_msgs, true).unwrap(); + let debug_data = + crate::dwarf::DebugData::load(&OsString::from("tests/elffiles/update_test.elf"), false) + .unwrap(); + (debug_data, a2l) + } + + #[test] + fn test_update_axis_pts_ok() { + let (debug_data, mut a2l) = test_setup("tests/update_test1.a2l"); + + // test address only update, in strict mode + let version = A2lVersion::from(&a2l); + let (mut data, info) = init_update( + &debug_data, + &mut a2l.project.module[0], + version, + UpdateType::Addresses, + UpdateMode::Strict, + true, + ); + + let mut log_msgs = Vec::new(); + let result = update_all_module_axis_pts(&mut data, &info); + assert!(result.iter().all(|r| r == &UpdateResult::Updated)); + assert_eq!(result.len(), 3); + let (updated, not_updated) = log_update_results(&mut log_msgs, &result); + assert_eq!(updated, 3); + assert_eq!(not_updated, 0); + assert!(log_msgs.is_empty()); + + // test full update + let version = A2lVersion::from(&a2l); + let (mut data, info) = init_update( + &debug_data, + &mut a2l.project.module[0], + version, + UpdateType::Full, + UpdateMode::Default, + true, + ); + + let mut log_msgs = Vec::new(); + let result = update_all_module_axis_pts(&mut data, &info); + assert!(result.iter().all(|r| r == &UpdateResult::Updated)); + assert_eq!(result.len(), 3); + let (updated, not_updated) = log_update_results(&mut log_msgs, &result); + assert_eq!(updated, 3); + assert_eq!(not_updated, 0); + assert!(log_msgs.is_empty()); + } + + #[test] + fn test_update_axis_pts_bad() { + let (debug_data, mut a2l) = test_setup("tests/update_test2.a2l"); + + // test address only update, in strict mode + let version = A2lVersion::from(&a2l); + let (mut data, info) = init_update( + &debug_data, + &mut a2l.project.module[0], + version, + UpdateType::Addresses, + UpdateMode::Strict, + true, + ); + let result = update_all_module_axis_pts(&mut data, &info); + assert_eq!(result.len(), 4); + assert!(matches!(result[0], UpdateResult::InvalidDataType { .. })); + assert!(matches!(result[1], UpdateResult::InvalidDataType { .. })); + assert!(matches!(result[2], UpdateResult::Updated)); + assert!(matches!(result[3], UpdateResult::SymbolNotFound { .. })); + } + + #[test] + fn test_update_blob_ok() { + let (debug_data, mut a2l) = test_setup("tests/update_test1.a2l"); + + // test address only update, in strict mode + let version = A2lVersion::from(&a2l); + let (mut data, info) = init_update( + &debug_data, + &mut a2l.project.module[0], + version, + UpdateType::Addresses, + UpdateMode::Strict, + true, + ); + + let mut log_msgs = Vec::new(); + let result = update_all_module_blobs(&mut data, &info); + assert!(result.iter().all(|r| r == &UpdateResult::Updated)); + assert_eq!(result.len(), 2); + let (updated, not_updated) = log_update_results(&mut log_msgs, &result); + assert_eq!(updated, 2); + assert_eq!(not_updated, 0); + assert!(log_msgs.is_empty()); + + // test full update + let version = A2lVersion::from(&a2l); + let (mut data, info) = init_update( + &debug_data, + &mut a2l.project.module[0], + version, + UpdateType::Full, + UpdateMode::Default, + true, + ); + + let mut log_msgs = Vec::new(); + let result = update_all_module_blobs(&mut data, &info); + assert!(result.iter().all(|r| r == &UpdateResult::Updated)); + assert_eq!(result.len(), 2); + let (updated, not_updated) = log_update_results(&mut log_msgs, &result); + assert_eq!(updated, 2); + assert_eq!(not_updated, 0); + assert!(log_msgs.is_empty()); + } + + #[test] + fn test_update_blob_bad() { + let (debug_data, mut a2l) = test_setup("tests/update_test2.a2l"); + + // test address only update, in strict mode + let version = A2lVersion::from(&a2l); + let (mut data, info) = init_update( + &debug_data, + &mut a2l.project.module[0], + version, + UpdateType::Addresses, + UpdateMode::Strict, + true, + ); + let result = update_all_module_blobs(&mut data, &info); + assert_eq!(result.len(), 3); + assert!(matches!(result[0], UpdateResult::InvalidDataType { .. })); + assert!(matches!(result[1], UpdateResult::Updated)); + assert!(matches!(result[2], UpdateResult::SymbolNotFound { .. })); + } + + #[test] + fn test_update_characteristic_ok() { + let (debug_data, mut a2l) = test_setup("tests/update_test1.a2l"); + + // test address only update, in strict mode + let version = A2lVersion::from(&a2l); + let (mut data, info) = init_update( + &debug_data, + &mut a2l.project.module[0], + version, + UpdateType::Addresses, + UpdateMode::Strict, + true, + ); + + let mut log_msgs = Vec::new(); + let result = update_all_module_characteristics(&mut data, &info); + assert!(result.iter().all(|r| r == &UpdateResult::Updated)); + assert_eq!(result.len(), 6); + let (updated, not_updated) = log_update_results(&mut log_msgs, &result); + assert_eq!(updated, 6); + assert_eq!(not_updated, 0); + assert!(log_msgs.is_empty()); + + // test full update + let version = A2lVersion::from(&a2l); + let (mut data, info) = init_update( + &debug_data, + &mut a2l.project.module[0], + version, + UpdateType::Full, + UpdateMode::Default, + true, + ); + + let mut log_msgs = Vec::new(); + let result = update_all_module_characteristics(&mut data, &info); + assert!(result.iter().all(|r| r == &UpdateResult::Updated)); + assert_eq!(result.len(), 6); + let (updated, not_updated) = log_update_results(&mut log_msgs, &result); + assert_eq!(updated, 6); + assert_eq!(not_updated, 0); + assert!(log_msgs.is_empty()); + } + + #[test] + fn test_update_characteristic_bad() { + let (debug_data, mut a2l) = test_setup("tests/update_test2.a2l"); + + // test address only update, in strict mode + let version = A2lVersion::from(&a2l); + let (mut data, info) = init_update( + &debug_data, + &mut a2l.project.module[0], + version, + UpdateType::Addresses, + UpdateMode::Strict, + true, + ); + let result = update_all_module_characteristics(&mut data, &info); + assert_eq!(result.len(), 7); + assert!(matches!(result[0], UpdateResult::InvalidDataType { .. })); + // assert!(matches!(result[1], UpdateResult::InvalidDataType { .. })); // verify currently does not check the size in AXIS_DESCR + assert!(matches!(result[2], UpdateResult::InvalidDataType { .. })); + assert!(matches!(result[3], UpdateResult::Updated)); + assert!(matches!(result[4], UpdateResult::InvalidDataType { .. })); + assert!(matches!(result[5], UpdateResult::InvalidDataType { .. })); + assert!(matches!(result[6], UpdateResult::SymbolNotFound { .. })); + } + + #[test] + fn test_update_instance_ok() { + let (debug_data, mut a2l) = test_setup("tests/update_test1.a2l"); + + // test address only update, in strict mode + let version = A2lVersion::from(&a2l); + let (mut data, info) = init_update( + &debug_data, + &mut a2l.project.module[0], + version, + UpdateType::Addresses, + UpdateMode::Strict, + true, + ); + + let mut log_msgs = Vec::new(); + let typedef_names = TypedefNames::new(&data.module); + let (result, _) = update_all_module_instances(&mut data, &info, &typedef_names); + assert!(result.iter().all(|r| r == &UpdateResult::Updated)); + assert_eq!(result.len(), 1); + let (updated, not_updated) = log_update_results(&mut log_msgs, &result); + assert_eq!(updated, 1); + assert_eq!(not_updated, 0); + assert!(log_msgs.is_empty()); + + // test full update + let version = A2lVersion::from(&a2l); + let (mut data, info) = init_update( + &debug_data, + &mut a2l.project.module[0], + version, + UpdateType::Full, + UpdateMode::Default, + true, + ); + + let mut log_msgs = Vec::new(); + let typedef_names = TypedefNames::new(&data.module); + let (result, _) = update_all_module_instances(&mut data, &info, &typedef_names); + assert!(result.iter().all(|r| r == &UpdateResult::Updated)); + assert_eq!(result.len(), 1); + let (updated, not_updated) = log_update_results(&mut log_msgs, &result); + assert_eq!(updated, 1); + assert_eq!(not_updated, 0); + assert!(log_msgs.is_empty()); + } + + #[test] + fn test_update_instance_bad() { + let (debug_data, mut a2l) = test_setup("tests/update_test2.a2l"); + + // test address only update, in strict mode + let version = A2lVersion::from(&a2l); + let (mut data, info) = init_update( + &debug_data, + &mut a2l.project.module[0], + version, + UpdateType::Addresses, + UpdateMode::Strict, + true, + ); + let typedef_names = TypedefNames::new(&data.module); + let (result, _) = update_all_module_instances(&mut data, &info, &typedef_names); + assert_eq!(result.len(), 3); + assert!(matches!(result[0], UpdateResult::Updated)); + assert!(matches!(result[1], UpdateResult::Updated)); + assert!(matches!(result[2], UpdateResult::SymbolNotFound { .. })); + } + + #[test] + fn test_update_measurement_ok() { + let (debug_data, mut a2l) = test_setup("tests/update_test1.a2l"); + + // test address only update, in strict mode + let version = A2lVersion::from(&a2l); + let (mut data, info) = init_update( + &debug_data, + &mut a2l.project.module[0], + version, + UpdateType::Full, + UpdateMode::Default, + true, + ); + + let mut log_msgs = Vec::new(); + let result = update_all_module_measurements(&mut data, &info); + assert!(result.iter().all(|r| r == &UpdateResult::Updated)); + assert_eq!(result.len(), 5); + let (updated, not_updated) = log_update_results(&mut log_msgs, &result); + assert_eq!(updated, 5); + assert_eq!(not_updated, 0); + assert!(log_msgs.is_empty()); + + // test full update + let version = A2lVersion::from(&a2l); + let (mut data, info) = init_update( + &debug_data, + &mut a2l.project.module[0], + version, + UpdateType::Full, + UpdateMode::Default, + true, + ); + + let mut log_msgs = Vec::new(); + let result = update_all_module_measurements(&mut data, &info); + assert!(result.iter().all(|r| r == &UpdateResult::Updated)); + assert_eq!(result.len(), 5); + let (updated, not_updated) = log_update_results(&mut log_msgs, &result); + assert_eq!(updated, 5); + assert_eq!(not_updated, 0); + assert!(log_msgs.is_empty()); + } + + #[test] + fn test_update_measurement_bad() { + let (debug_data, mut a2l) = test_setup("tests/update_test2.a2l"); + + // test address only update, in strict mode + let version = A2lVersion::from(&a2l); + let (mut data, info) = init_update( + &debug_data, + &mut a2l.project.module[0], + version, + UpdateType::Addresses, + UpdateMode::Strict, + true, + ); + let result = update_all_module_measurements(&mut data, &info); + assert_eq!(result.len(), 6); + assert!(matches!(result[0], UpdateResult::InvalidDataType { .. })); + assert!(matches!(result[1], UpdateResult::InvalidDataType { .. })); + assert!(matches!(result[2], UpdateResult::InvalidDataType { .. })); + assert!(matches!(result[3], UpdateResult::Updated)); + assert!(matches!(result[4], UpdateResult::Updated)); + assert!(matches!(result[5], UpdateResult::SymbolNotFound { .. })); + } + + #[test] + fn test_update_a2l_ok() { + let (debug_data, mut a2l) = test_setup("tests/update_test1.a2l"); + + // test address only update, in strict mode + let mut log_msgs = Vec::new(); + let (summary, strict_error) = update_a2l( + &mut a2l, + &debug_data, + &mut log_msgs, + UpdateType::Addresses, + UpdateMode::Strict, + false, + ); + assert_eq!(strict_error, false); + assert_eq!(summary.axis_pts_not_updated, 0); + assert_eq!(summary.axis_pts_updated, 3); + assert_eq!(summary.blob_not_updated, 0); + assert_eq!(summary.blob_updated, 2); + assert_eq!(summary.characteristic_not_updated, 0); + assert_eq!(summary.characteristic_updated, 6); + assert_eq!(summary.measurement_not_updated, 0); + assert_eq!(summary.measurement_updated, 5); + assert_eq!(summary.instance_not_updated, 0); + assert_eq!(summary.instance_updated, 1); + assert!(log_msgs.is_empty()); + + // test full update + let mut log_msgs = Vec::new(); + let (summary, _) = update_a2l( + &mut a2l, + &debug_data, + &mut log_msgs, + UpdateType::Full, + UpdateMode::Default, + false, + ); + assert_eq!(summary.axis_pts_not_updated, 0); + assert_eq!(summary.axis_pts_updated, 3); + assert_eq!(summary.blob_not_updated, 0); + assert_eq!(summary.blob_updated, 2); + assert_eq!(summary.characteristic_not_updated, 0); + assert_eq!(summary.characteristic_updated, 6); + assert_eq!(summary.measurement_not_updated, 0); + assert_eq!(summary.measurement_updated, 5); + assert_eq!(summary.instance_not_updated, 0); + assert_eq!(summary.instance_updated, 1); + assert!(log_msgs.is_empty()); + } } diff --git a/src/update/typedef.rs b/src/update/typedef.rs index 7462a88..3e8f639 100644 --- a/src/update/typedef.rs +++ b/src/update/typedef.rs @@ -2,8 +2,8 @@ use crate::dwarf::{make_simple_unit_name, DebugData, DwarfDataType, TypeInfo}; use crate::update::enums::{cond_create_enum_conversion, update_enum_compu_methods}; use crate::update::{ adjust_limits, get_a2l_datatype, get_fnc_values_memberid, get_inner_type, set_address_type, - set_bitmask, set_matrix_dim, update_characteristic_axis, update_record_layout, - RecordLayoutInfo, TypedefNames, TypedefReferrer, TypedefsRefInfo, UpdateInfo, + set_bitmask, set_matrix_dim, update_characteristic_axis, update_record_layout, A2lUpdateInfo, + RecordLayoutInfo, TypedefNames, TypedefReferrer, TypedefsRefInfo, }; use a2lfile::{ A2lObject, AddrType, CharacteristicType, FncValues, IndexMode, Module, Number, RecordLayout, @@ -70,19 +70,21 @@ pub(crate) const FLAG_CREATE_CALIB: &str = "||calib||"; pub(crate) const FLAG_CREATE_MEAS: &str = "||meas||"; pub(crate) fn update_module_typedefs( - info: &mut UpdateInfo, + info: &A2lUpdateInfo, + module: &mut Module, + log_msgs: &mut Vec, typedef_ref_info: TypedefsRefInfo, typedef_names: TypedefNames, - compu_method_index: &HashMap, + recordlayout_info: &mut RecordLayoutInfo, ) { let updater = TypedefUpdater::new( - info.module, + module, info.debug_data, - info.log_msgs, + log_msgs, typedef_names, - &mut info.reclayout_info, + recordlayout_info, typedef_ref_info, - compu_method_index, + &info.compu_method_index, ); updater.process_typedefs(info.preserve_unknown, false); @@ -1828,7 +1830,7 @@ mod test { use super::{update_module_typedefs, TypedefUpdater}; use crate::{ dwarf::{DebugData, TypeInfo}, - update::{get_symbol_info, RecordLayoutInfo, TypedefNames, TypedefReferrer, UpdateInfo}, + update::{get_symbol_info, A2lUpdateInfo, RecordLayoutInfo, TypedefNames, TypedefReferrer}, A2lVersion, }; use a2lfile::A2lFile; @@ -1851,8 +1853,10 @@ mod test { #[test] fn test_calc_structure_category() { - let (mut a2l, debug_data, names, mut reclayout) = - test_setup("tests/update_test1.a2l", "tests/elffiles/update_test.elf"); + let (mut a2l, debug_data, names, mut reclayout) = test_setup( + "tests/update_typedef_test1.a2l", + "tests/elffiles/update_typedef_test.elf", + ); let mut msgs = Vec::new(); let dummy_cm_index = HashMap::new(); let mut tdu = TypedefUpdater::new( @@ -1886,8 +1890,10 @@ mod test { #[test] fn test_build_structure_hash() { - let (mut a2l, debug_data, names, mut reclayout) = - test_setup("tests/update_test1.a2l", "tests/elffiles/update_test.elf"); + let (mut a2l, debug_data, names, mut reclayout) = test_setup( + "tests/update_typedef_test1.a2l", + "tests/elffiles/update_typedef_test.elf", + ); let num_structs = a2l.project.module[0].typedef_structure.len(); let mut msgs = Vec::new(); let dummy_cm_index = HashMap::new(); @@ -1925,8 +1931,10 @@ mod test { #[test] fn test_process_structure_components() { - let (mut a2l, debug_data, names, mut reclayout) = - test_setup("tests/update_test2.a2l", "tests/elffiles/update_test.elf"); + let (mut a2l, debug_data, names, mut reclayout) = test_setup( + "tests/update_typedef_test2.a2l", + "tests/elffiles/update_typedef_test.elf", + ); let mut msgs = Vec::new(); let dummy_cm_index = HashMap::new(); let mut tdu = TypedefUpdater::new( @@ -1966,7 +1974,7 @@ mod test { #[test] fn test_create_missing_instance_targets() { let mut a2l = a2lfile::new(); - let elf_name = OsString::from("tests/elffiles/update_test.elf"); + let elf_name = OsString::from("tests/elffiles/update_typedef_test.elf"); let debug_data = crate::dwarf::DebugData::load(&elf_name, false).unwrap(); let typedef_names = TypedefNames::new(&a2l.project.module[0]); let mut recordlayout_info = RecordLayoutInfo::build(&a2l.project.module[0]); @@ -2022,7 +2030,7 @@ mod test { #[test] fn test_create_typedef() { let mut a2l = a2lfile::new(); - let elf_name = OsString::from("tests/elffiles/update_test.elf"); + let elf_name = OsString::from("tests/elffiles/update_typedef_test.elf"); let debug_data = crate::dwarf::DebugData::load(&elf_name, false).unwrap(); let typedef_names = TypedefNames::new(&a2l.project.module[0]); let mut recordlayout_info = RecordLayoutInfo::build(&a2l.project.module[0]); @@ -2097,7 +2105,7 @@ mod test { #[test] fn test_create_typedef2() { let mut a2l = a2lfile::new(); - let elf_name = OsString::from("tests/elffiles/update_test.elf"); + let elf_name = OsString::from("tests/elffiles/update_typedef_test.elf"); let debug_data = crate::dwarf::DebugData::load(&elf_name, false).unwrap(); let typedef_names = TypedefNames::new(&a2l.project.module[0]); let mut recordlayout_info = RecordLayoutInfo::build(&a2l.project.module[0]); @@ -2137,8 +2145,10 @@ mod test { #[test] fn test_update() { - let (mut a2l, debug_data, names, reclayout) = - test_setup("tests/update_test3.a2l", "tests/elffiles/update_test.elf"); + let (mut a2l, debug_data, names, mut reclayout) = test_setup( + "tests/update_typedef_test3.a2l", + "tests/elffiles/update_typedef_test.elf", + ); let mut typedef_ref_info: HashMap> = HashMap::new(); for (idx, inst) in a2l.project.module[0].instance.iter().enumerate() { @@ -2159,19 +2169,27 @@ mod test { let version = A2lVersion::from(&a2l); let mut log_msgs = Vec::new(); - let mut info = UpdateInfo { - module: &mut a2l.project.module[0], + let mut info = A2lUpdateInfo { debug_data: &debug_data, - log_msgs: &mut log_msgs, preserve_unknown: false, + strict_update: false, + full_update: true, version, - reclayout_info: reclayout, + enable_structures: true, + compu_method_index: HashMap::new(), }; - update_module_typedefs(&mut info, typedef_ref_info, names, &HashMap::new()); + update_module_typedefs( + &mut info, + &mut a2l.project.module[0], + &mut log_msgs, + typedef_ref_info, + names, + &mut reclayout, + ); let mut log_msgs = Vec::new(); let mut reference_a2l = - a2lfile::load("tests/update_test4.a2l", None, &mut log_msgs, true).unwrap(); + a2lfile::load("tests/update_typedef_test4.a2l", None, &mut log_msgs, true).unwrap(); // ordering is not guaranteed, so sort both files before comparing them a2l.sort(); diff --git a/tests/elffiles/update_test.c b/tests/elffiles/update_test.c index 03c0f24..c64043c 100644 --- a/tests/elffiles/update_test.c +++ b/tests/elffiles/update_test.c @@ -1,84 +1,99 @@ // update_test.elf built with: arm-none-eabi-gcc, Arm GNU Toolchain 13.2 // arm-none-eabi-gcc -mcpu=cortex-m7 -mthumb -specs=nano.specs -mfloat-abi=hard -nostdlib -g3 update_test.c -o update_test.elf -#include "stdint.h" +#include -typedef enum { - VALUE_1 = 100, - VALUE_2 = 20, - VALUE_3 = 11111 -} MyEnum; +/********************************************** + * curve with an internal axis */ +struct UpdateTest_Curve_InternalAxis { + uint16_t x[4]; + float value[4]; +}; +struct UpdateTest_Curve_InternalAxis Curve_InternalAxis = { + {0U, 100U, 200U, 300U}, {12345.6F, 42.42F, 1000.0F, 65535.9F}}; -typedef uint32_t (*funcptr_t)(uint16_t*, float); +/********************************************** + * curve with an external axis */ +struct UpdateTest_Axis_0 { + uint32_t value[5]; +}; +struct UpdateTest_Curve_ExternalAxis { + float value[5]; +}; +struct UpdateTest_Axis_0 Axis_0 = {{100UL, 200UL, 300UL, 400UL, 500UL}}; +struct UpdateTest_Curve_ExternalAxis Curve_ExternalAxis = { + -99.99F, 12345.6F, 42.42F, 1000.0F, 65535.9F}; -typedef struct StructA { - MyEnum enumval; - uint32_t val_i32; - uint64_t val_i64; - float val_f32; -} StructA; +/********************************************** + * map with two internal axes */ +struct UpdateTest_Map_InternalAxis { + uint16_t x[4]; + uint16_t y[3]; + uint32_t value[3][4]; +}; +struct UpdateTest_Map_InternalAxis Map_InternalAxis = { + {0U, 100U, 200U, 300U}, + {0U, 10U, 20U}, + {{0UL, 1UL, 4UL, 7UL}, {0UL, 2UL, 5UL, 8UL}, {0UL, 3UL, 6UL, 9UL}}}; -struct StructB { - struct StructB* pPrev; - struct StructB* pNext; - StructA s1; - StructA s2; - funcptr_t func; +/********************************************** + * map with two external axes */ +struct UpdateTest_Axis_1 { + uint32_t value[3]; }; +struct UpdateTest_Axis_2 { + uint32_t value[2]; +}; +struct UpdateTest_Map_ExternalAxis { + float value[2][3]; +}; +struct UpdateTest_Axis_1 Axis_1 = {{100UL, 200UL, 300UL}}; +struct UpdateTest_Axis_2 Axis_2 = {{0UL, 1UL}}; +struct UpdateTest_Map_ExternalAxis Map_ExternalAxis = { + {{-1.0F, 0.001F, 22.2F}, {-3.0F, -1.5F, 11.0F}}}; -typedef struct { +/********************************************** + * ValBlk */ +float Characteristic_ValBlk[5] = {1.2, 3.4, 5.6, 7.8, 9.0}; -} BasicData; +/********************************************** + * Value */ +uint32_t Characteristic_Value = 3; -typedef struct RegDef { - union { - uint32_t Value; - struct { - unsigned Bits_ABC:5; - unsigned Bits_DEF:5; - unsigned Bits_GHI:5; - unsigned Bits_JKL:5; - unsigned :12; - }; - }; -} RegDef; +/********************************************** + * Complex BLOB data */ +struct UpdateTest_ComplexBlobData { + uint32_t value_1[16]; + struct { + uint16_t value_2_1; + uint32_t value_2_2; + } value_2[8]; +}; +struct UpdateTest_ComplexBlobData Blob_1; -typedef struct { - uint32_t value; -} TestStruct; +/********************************************** + * Simple BLOB data */ +uint8_t Blob_2[256]; -uint8_t val_u8; -uint16_t val_u16; -uint32_t val_u32; -uint64_t val_u64; -int8_t val_i8; -int16_t val_i16; -int32_t val_i32; -int64_t val_i64; -float val_f; -double val_d; -MyEnum val_e; -void* val_ptr; +/********************************************** + * Measurement matrix */ +uint8_t Measurement_Matrix[5][4]; -RegDef reg; -struct StructB struct_b; -BasicData basic; -funcptr_t func; +/********************************************** + * Measurement value */ +uint16_t Measurement_Value; -TestStruct TEST_struct = {0}; -TestStruct *TEST_structptr = &TEST_struct; -TestStruct **TEST_structptr_ptr = &TEST_structptr; -TestStruct TEST_structarr[10] = { {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0} }; -TestStruct TEST_structarr_arr[2][10] = {{ {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0} }, { {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0} }}; -TestStruct (*TEST_structarr_ptr)[10] = &TEST_structarr; -TestStruct (*TEST_structarr_ptr_arr[2])[10] = {&TEST_structarr, &TEST_structarr}; -TestStruct *TEST_structptr_arr[4] = { &TEST_struct, &TEST_struct, &TEST_struct, &TEST_struct}; -TestStruct *(*TEST_structptr_arr_ptr)[4] = &TEST_structptr_arr; +/********************************************** + * Measurement bitfield */ +struct { + uint32_t bits_1: 5; + uint32_t bits_2: 15; + uint32_t bits_3: 8; +} Measurement_Bitfield; -uint32_t Value_u32; -int8_t Value_i8; +/**********************************************/ -int main() +int main(void) { return 0; -} \ No newline at end of file +} diff --git a/tests/elffiles/update_test.elf b/tests/elffiles/update_test.elf index 0a59a2e2f2e9f06478e2c1753d90f84a58a89ef4..e519d46f20f41bdc8c03f14291a319a0abbbb40a 100644 GIT binary patch delta 9157 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 delta 9783 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 GHUA5B-#Lr` diff --git a/tests/elffiles/update_typedef_test.c b/tests/elffiles/update_typedef_test.c new file mode 100644 index 0000000..11786c8 --- /dev/null +++ b/tests/elffiles/update_typedef_test.c @@ -0,0 +1,84 @@ +// update_typedef_test.elf built with: arm-none-eabi-gcc, Arm GNU Toolchain 13.2 +// arm-none-eabi-gcc -mcpu=cortex-m7 -mthumb -specs=nano.specs -mfloat-abi=hard -nostdlib -g3 update_typedef_test.c -o update_typedef_test.elf + +#include "stdint.h" + +typedef enum { + VALUE_1 = 100, + VALUE_2 = 20, + VALUE_3 = 11111 +} MyEnum; + +typedef uint32_t (*funcptr_t)(uint16_t*, float); + +typedef struct StructA { + MyEnum enumval; + uint32_t val_i32; + uint64_t val_i64; + float val_f32; +} StructA; + +struct StructB { + struct StructB* pPrev; + struct StructB* pNext; + StructA s1; + StructA s2; + funcptr_t func; +}; + +typedef struct { + +} BasicData; + +typedef struct RegDef { + union { + uint32_t Value; + struct { + unsigned Bits_ABC:5; + unsigned Bits_DEF:5; + unsigned Bits_GHI:5; + unsigned Bits_JKL:5; + unsigned :12; + }; + }; +} RegDef; + +typedef struct { + uint32_t value; +} TestStruct; + +uint8_t val_u8; +uint16_t val_u16; +uint32_t val_u32; +uint64_t val_u64; +int8_t val_i8; +int16_t val_i16; +int32_t val_i32; +int64_t val_i64; +float val_f; +double val_d; +MyEnum val_e; +void* val_ptr; + +RegDef reg; +struct StructB struct_b; +BasicData basic; +funcptr_t func; + +TestStruct TEST_struct = {0}; +TestStruct *TEST_structptr = &TEST_struct; +TestStruct **TEST_structptr_ptr = &TEST_structptr; +TestStruct TEST_structarr[10] = { {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0} }; +TestStruct TEST_structarr_arr[2][10] = {{ {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0} }, { {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0} }}; +TestStruct (*TEST_structarr_ptr)[10] = &TEST_structarr; +TestStruct (*TEST_structarr_ptr_arr[2])[10] = {&TEST_structarr, &TEST_structarr}; +TestStruct *TEST_structptr_arr[4] = { &TEST_struct, &TEST_struct, &TEST_struct, &TEST_struct}; +TestStruct *(*TEST_structptr_arr_ptr)[4] = &TEST_structptr_arr; + +uint32_t Value_u32; +int8_t Value_i8; + +int main() +{ + return 0; +} \ No newline at end of file diff --git a/tests/elffiles/update_typedef_test.elf b/tests/elffiles/update_typedef_test.elf new file mode 100644 index 0000000000000000000000000000000000000000..0a59a2e2f2e9f06478e2c1753d90f84a58a89ef4 GIT binary patch literal 28696 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