| 1466 | constexpr char kApiImplements[] = "api_implements"; |
| 1467 | |
| 1468 | std::set<string> ReachableFunctions( |
| 1469 | const FunctionLibraryDefinition& flib, |
| 1470 | const protobuf::RepeatedPtrField<NodeDef>& nodes) { |
| 1471 | // Functions that are reachable from the graph. |
| 1472 | std::set<string> reachable_funcs; |
| 1473 | |
| 1474 | // For any functions, if it has attribute "api_implements" = |
| 1475 | // "some_interface" and it is reachable, then it means any other |
| 1476 | // function with same attribute name and value could also be potentially |
| 1477 | // reachable, eg via implementation_selector swapping the nodedef. |
| 1478 | absl::flat_hash_set<string> reachable_api_interface; |
| 1479 | |
| 1480 | // Functions might be reachable from the nested function calls, so we keep a |
| 1481 | // queue of functions that we have to check. |
| 1482 | gtl::InlinedVector<const FunctionDef*, 4> func_queue; |
| 1483 | |
| 1484 | // Add reachable and not already processed functions to the functions queue. |
| 1485 | const auto add_to_func_queue = [&](const string& func_name) { |
| 1486 | const FunctionDef* func = flib.Find(func_name); |
| 1487 | if (func && reachable_funcs.find(func_name) == reachable_funcs.end()) { |
| 1488 | func_queue.push_back(func); |
| 1489 | } |
| 1490 | }; |
| 1491 | |
| 1492 | // If any function with certain API name is reachable, all the other functions |
| 1493 | // with same API name should also be checked. |
| 1494 | const auto add_function_with_api_interface = [&](const string& api_name) { |
| 1495 | if (!reachable_api_interface.contains(api_name)) { |
| 1496 | reachable_api_interface.insert(api_name); |
| 1497 | for (const auto& func_name : flib.ListFunctionNames()) { |
| 1498 | const auto& func_def = flib.Find(func_name); |
| 1499 | const auto attr_it = func_def->attr().find(kApiImplements); |
| 1500 | if (attr_it != func_def->attr().end() && |
| 1501 | attr_it->second.s() == api_name) { |
| 1502 | add_to_func_queue(func_name); |
| 1503 | } |
| 1504 | } |
| 1505 | } |
| 1506 | }; |
| 1507 | |
| 1508 | // Add all the functions that are reachable from the given node to the queue. |
| 1509 | const auto process_node = [&](const NodeDef& node) { |
| 1510 | // Node itself can be a call to the function. |
| 1511 | add_to_func_queue(node.op()); |
| 1512 | |
| 1513 | // Or node can have an attribute referencing a function. |
| 1514 | for (const auto& attr : node.attr()) { |
| 1515 | const auto& attr_value = attr.second; |
| 1516 | |
| 1517 | // 1. AttrValue.func |
| 1518 | if (attr_value.has_func()) { |
| 1519 | add_to_func_queue(attr_value.func().name()); |
| 1520 | } |
| 1521 | |
| 1522 | // 2. AttrValue.ListValue.func |
| 1523 | if (attr_value.has_list()) { |
| 1524 | for (const auto& func : attr_value.list().func()) { |
| 1525 | add_to_func_queue(func.name()); |
no test coverage detected