Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Repeating life cycles #168

Closed

Conversation

ZeyadTarekk
Copy link
Contributor

  • The current syntax only allows the generation of a succession of method calls.
  • We have changed the design slightly, we now want the user to provide a control flow graph structure.

}
};

std::unordered_map<LifecycleMethodCall, std::vector<LifecycleMethodCall>, NodeHasher> adj_list_;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This representation doesn't seem quite right. Why is the key a MethodCall itself? I thought we said it should be the name used as the key in the json. It should just be a string.
The parsing logic is quite weird, since we seem to create one node for each call, but a node is a std::vector. Basically, you are never using vectors of more than 1 element.

I think this should be redesigned to be something like:
std::unordered_map<std::stringg, LifecycleGraphNode> nodes_
And LifecycleGraphNode should have a std::vector<LifecycleMethodCall> has well as a std::vector<std::string> successors.

@@ -174,7 +201,7 @@ class LifecycleMethod {

std::string base_class_name_;
std::string method_name_;
std::vector<LifecycleMethodCall> callees_;
std::variant<std::vector<LifecycleMethodCall>,LifeCycleMethodGraph> callees_;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe rename this as body_ since this represents the method body (i.e content).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

^ rename this body_ as said before.

Comment on lines 322 to 420
for (const auto& callee : callees_) {
auto* dex_method = callee.get_dex_method(dex_klass);
if (!dex_method) {
// Dex method does not apply for current APK.
// See `LifecycleMethod::validate()`.
continue;
}

++callee_count;

std::vector<Location> invoke_with_registers{this_location};
auto* type_list = callee.get_argument_types();
// This should have been verified at the start of `create_methods`
mt_assert(type_list != nullptr);
for (auto* type : *type_list) {
auto argument_register = method.get_local(type_index_map.at(type));
invoke_with_registers.push_back(argument_register);
}
main_block->invoke(
IROpcode::OPCODE_INVOKE_VIRTUAL, dex_method, invoke_with_registers);
}
// for (const auto& callee : callees_) {
// auto* dex_method = callee.get_dex_method(dex_klass);
// if (!dex_method) {
// // Dex method does not apply for current APK.
// // See `LifecycleMethod::validate()`.
// continue;
// }

// ++callee_count;

// std::vector<Location> invoke_with_registers{this_location};
// auto* type_list = callee.get_argument_types();
// // This should have been verified at the start of `create_methods`
// mt_assert(type_list != nullptr);
// for (auto* type : *type_list) {
// auto argument_register = method.get_local(type_index_map.at(type));
// invoke_with_registers.push_back(argument_register);
// }
// main_block->invoke(
// IROpcode::OPCODE_INVOKE_VIRTUAL, dex_method, invoke_with_registers);
// }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Obviously, we will need to keep this from now otherwise this will break existing lifecycle configurations.

const std::vector<std::string>& successors() const;
bool operator==(const LifecycleGraphNode& other) const;

INCLUDE_DEFAULT_COPY_CONSTRUCTORS_AND_ASSIGNMENTS(LifecycleGraphNode)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for consistency with the rest of our codebase, please move this right after the constructor

Comment on lines 104 to 105
const std::vector<LifecycleMethodCall>& method_calls() const;
const std::vector<std::string>& successors() const;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: inline these since the implementation is really small

can we inline the implementation of these, since those should be small?

Suggested change
const std::vector<LifecycleMethodCall>& method_calls() const;
const std::vector<std::string>& successors() const;
const std::vector<LifecycleMethodCall>& method_calls() const { return method_calls_; }
const std::vector<std::string>& successors() const { return successors_; }

Comment on lines 119 to 121
void addNode(const std::string& node_name,std::vector<LifecycleMethodCall> method_calls,std::vector<std::string> successors);

const LifecycleGraphNode* getNode(const std::string& node_name) const;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: we use snake_case for method names, so it should be add_node and get_node.

@@ -174,7 +201,7 @@ class LifecycleMethod {

std::string base_class_name_;
std::string method_name_;
std::vector<LifecycleMethodCall> callees_;
std::variant<std::vector<LifecycleMethodCall>,LifeCycleMethodGraph> callees_;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

^ rename this body_ as said before.

Comment on lines 45 to 48
if (nodes_.size() != other.nodes_.size()) {
return false;
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not really necessary, nodes_ == other.nodes_ will do the same check.

Comment on lines 65 to 66
JsonValidation::validate_object(node, "node");
const auto& instructions = node["instructions"];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think those are not necessary anymore?

for (const auto& callee : JsonValidation::null_or_array(value, "callees")) {
callees.push_back(LifecycleMethodCall::from_json(callee));
}
return LifecycleMethod(base_class_name, method_name, callees);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: std::move

Suggested change
return LifecycleMethod(base_class_name, method_name, callees);
return LifecycleMethod(base_class_name, method_name, std::move(callees));

} else if (JsonValidation::has_field(value, "control_flow_graph")) {
JsonValidation::validate_object(value, "control_flow_graph");
LifeCycleMethodGraph graph = LifeCycleMethodGraph::from_json(JsonValidation::object(value, "control_flow_graph"));
return LifecycleMethod(base_class_name, method_name, graph);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also here

Suggested change
return LifecycleMethod(base_class_name, method_name, graph);
return LifecycleMethod(base_class_name, method_name, std::move(graph));

}

return LifecycleMethod(base_class_name, method_name, callees);
throw std::invalid_argument("Invalid JSON format for LifecycleMethod");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's be a bit more descriptive, otherwise this will be confusing to the user.
We should also use JsonValidationError.

Suggested change
throw std::invalid_argument("Invalid JSON format for LifecycleMethod");
throw JsonValidationError(
value,
/* field */ std::nullopt,
"key `callees` or `control_flow_graph`");

Comment on lines 247 to 249
// for (const auto& callee : callees_) {
// callee.validate(base_class, class_hierarchies);
// }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This still needs to be addressed. And same thing below.
You can do something like:

if (const auto* callees = std::get_if<std::vector<LifecycleMethodCall>>(callees_)) {
  // handle *callees
} else {
  const auto& graph = std::get< LifeCycleMethodGraph>(callees_);
  // handle graph
}

@arthaud
Copy link
Contributor

arthaud commented Aug 14, 2024

Could we also add tests, as I suggested last week?

Comment on lines +391 to -341
for (const auto& callee : *callees) {
auto* dex_method = callee.get_dex_method(dex_klass);
if (!dex_method) {
// Dex method does not apply for current APK.
// See `LifecycleMethod::validate()`.
continue;
}

++callee_count;

std::vector<Location> invoke_with_registers{this_location};
auto* type_list = callee.get_argument_types();
// This should have been verified at the start of `create_methods`
mt_assert(type_list != nullptr);
for (auto* type : *type_list) {
auto argument_register = method.get_local(type_index_map.at(type));
invoke_with_registers.push_back(argument_register);
std::vector<Location> invoke_with_registers{this_location};
auto* type_list = callee.get_argument_types();
// This should have been verified at the start of `create_methods`
mt_assert(type_list != nullptr);
for (auto* type : *type_list) {
auto argument_register = method.get_local(type_index_map.at(type));
invoke_with_registers.push_back(argument_register);
}
main_block->invoke(
IROpcode::OPCODE_INVOKE_VIRTUAL, dex_method, invoke_with_registers);
}
main_block->invoke(
IROpcode::OPCODE_INVOKE_VIRTUAL, dex_method, invoke_with_registers);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe move these in a function generate_invoke_instructions that take a list of LifecycleMethodCall and the main_block. This will be useful for the next step.

for (const auto& callee : callees_) {
callee.validate(base_class, class_hierarchies);
if (const auto* callees = std::get_if<std::vector<LifecycleMethodCall>>(&body_)) {
// handle *callees
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: remove this comment

@@ -53,6 +53,8 @@ class LifecycleMethodCall {

static LifecycleMethodCall from_json(const Json::Value& value);

const std::string get_method_name() const { return method_name_; }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing & here

Suggested change
const std::string get_method_name() const { return method_name_; }
const std::string& get_method_name() const { return method_name_; }

source/tests/JsonTest.cpp Outdated Show resolved Hide resolved
@facebook-github-bot
Copy link
Contributor

@arthaud has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator.

@facebook-github-bot
Copy link
Contributor

@arthaud merged this pull request in c20adf3.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants