TEST(PropagationTest1, localDCE1) { g_redex = new RedexContext(); const char* dexfile = std::getenv("dexfile"); ASSERT_NE(nullptr, dexfile); std::vector<DexStore> stores; DexMetadata dm; dm.set_id("classes"); DexStore root_store(dm); root_store.add_classes(load_classes_from_dex(dexfile)); DexClasses& classes = root_store.get_dexen().back(); stores.emplace_back(std::move(root_store)); std::cout << "Loaded classes: " << classes.size() << std::endl ; TRACE(DCE, 2, "Code before:\n"); for(const auto& cls : classes) { TRACE(DCE, 2, "Class %s\n", SHOW(cls)); for (const auto& dm : cls->get_dmethods()) { TRACE(DCE, 2, "dmethod: %s\n", dm->get_name()->c_str()); if (strcmp(dm->get_name()->c_str(), "propagate") == 0) { TRACE(DCE, 2, "dmethod: %s\n", SHOW(dm->get_code())); } } } std::vector<Pass*> passes = { new PeepholePass(), new LocalDcePass(), }; PassManager manager(passes); manager.set_testing_mode(); Json::Value conf_obj = Json::nullValue; ConfigFiles dummy_cfg(conf_obj); manager.run_passes(stores, dummy_cfg); TRACE(DCE, 2, "Code after:\n"); for(const auto& cls : classes) { TRACE(DCE, 2, "Class %s\n", SHOW(cls)); for (const auto& dm : cls->get_dmethods()) { TRACE(DCE, 2, "dmethod: %s\n", dm->get_name()->c_str()); if (strcmp(dm->get_name()->c_str(), "propagate") == 0) { TRACE(DCE, 2, "dmethod: %s\n", SHOW(dm->get_code())); for (auto& mie : InstructionIterable(dm->get_code())) { auto instruction = mie.insn; // Make sure there is no invoke-virtual in the optimized method. ASSERT_NE(instruction->opcode(), OPCODE_INVOKE_VIRTUAL); // Make sure there is no const-class in the optimized method. ASSERT_NE(instruction->opcode(), OPCODE_CONST_CLASS); } } } } }
TEST(DedupBlocksTest, useSwitch) { g_redex = new RedexContext(); const char* dexfile = std::getenv("dexfile"); EXPECT_NE(nullptr, dexfile); std::vector<DexStore> stores; DexMetadata dm; dm.set_id("classes"); DexStore root_store(dm); root_store.add_classes(load_classes_from_dex(dexfile)); DexClasses& classes = root_store.get_dexen().back(); stores.emplace_back(std::move(root_store)); TRACE(RME, 1, "Code before:\n"); for (const auto& cls : classes) { TRACE(RME, 1, "Class %s\n", SHOW(cls)); for (const auto& m : cls->get_vmethods()) { TRACE(RME, 1, "\nmethod %s:\n", SHOW(m)); IRCode* code = m->get_code(); code->build_cfg(true); EXPECT_EQ(2, count_sgets(code->cfg())); code->clear_cfg(); } } auto copy_prop = new CopyPropagationPass(); copy_prop->m_config.static_finals = true; copy_prop->m_config.wide_registers = true; std::vector<Pass*> passes = { copy_prop }; PassManager manager(passes); manager.set_testing_mode(); Json::Value conf_obj = Json::nullValue; Scope external_classes; ConfigFiles dummy_cfg(conf_obj); manager.run_passes(stores, external_classes, dummy_cfg); TRACE(RME, 1, "Code after:\n"); for (const auto& cls : classes) { for (const auto& m : cls->get_vmethods()) { TRACE(RME, 1, "\nmethod %s:\n", SHOW(m)); IRCode* code = m->get_code(); code->build_cfg(true); if (strcmp(m->get_name()->c_str(), "remove") == 0) { EXPECT_EQ(1, count_sgets(code->cfg())); } else { EXPECT_EQ(2, count_sgets(code->cfg())); } code->clear_cfg(); } } }
TEST(ResultPropagationTest, useSwitch) { g_redex = new RedexContext(); const char* dexfile = std::getenv("dexfile"); EXPECT_NE(nullptr, dexfile); std::vector<DexStore> stores; DexMetadata dm; dm.set_id("classes"); DexStore root_store(dm); root_store.add_classes(load_classes_from_dex(dexfile)); DexClasses& classes = root_store.get_dexen().back(); stores.emplace_back(std::move(root_store)); std::vector<Pass*> passes = { new ResultPropagationPass(), }; PassManager manager(passes); manager.set_testing_mode(); Json::Value conf_obj = Json::nullValue; ConfigFiles dummy_cfg(conf_obj); manager.run_passes(stores, dummy_cfg); int num_test_classes = 0; const char* test_class_prefix = "Lcom/facebook/redextest/ResultPropagation$"; const char* test_method_prefix = "returns_"; for (const auto& cls : classes) { if (strncmp(cls->get_name()->c_str(), test_class_prefix, strlen(test_class_prefix)) == 0) { TRACE(RP, 1, "test class %s\n", cls->get_name()->c_str()); num_test_classes++; int num_tests_in_class = 0; for (const auto& m : cls->get_vmethods()) { const auto method_name = m->get_name()->c_str(); TRACE(RP, 1, " test method %s\n", method_name); EXPECT_EQ(strncmp(method_name, test_method_prefix, strlen(test_method_prefix)), 0); const auto suffix = method_name + strlen(test_method_prefix); boost::optional<ParamIndex> expected; if (strcmp(suffix, "none") != 0) { expected = atoi(suffix); } IRCode* code = m->get_code(); code->build_cfg(/* editable */ true); auto actual = find_return_param_index(code->cfg()); code->clear_cfg(); if (expected) { EXPECT_TRUE(actual); EXPECT_EQ(*actual, *expected); } else { EXPECT_FALSE(actual); } num_tests_in_class++; } EXPECT_EQ(num_tests_in_class, 1); } } EXPECT_EQ(num_test_classes, 6); }
TEST(ConstantPropagationTest1, constantpropagation) { g_redex = new RedexContext(); const char* dexfile = "gen/native/redex/test/integ/constant-propagation-test-dex/constant-propagation.dex"; if (access(dexfile, R_OK) != 0) { dexfile = std::getenv("dexfile"); ASSERT_NE(nullptr, dexfile); } std::vector<DexClasses> dexen; dexen.emplace_back(load_classes_from_dex(dexfile)); DexClasses& classes = dexen.back(); std::cout << "Loaded classes: " << classes.size() << std::endl; TRACE(MAIN, 2, "Code before:\n"); for(const auto& cls : classes) { TRACE(MAIN, 2, "Class %s\n", SHOW(cls)); for (const auto& dm : cls->get_dmethods()) { TRACE(MAIN, 2, "dmethod: %s\n", dm->get_name()->c_str()); if (strcmp(dm->get_name()->c_str(), "propagation") == 0) { TRACE(MAIN, 2, "dmethod: %s\n", SHOW(dm->get_code())); } } } std::vector<Pass*> passes = { new LocalDcePass(), new DelInitPass(), new RemoveEmptyClassesPass(), // TODO: add constant propagation and conditional pruning optimization }; std::vector<KeepRule> null_rules; PassManager manager(passes, null_rules); Json::Value conf_obj = Json::nullValue; ConfigFiles dummy_cfg(conf_obj); manager.run_passes(dexen, dummy_cfg); TRACE(MAIN, 2, "Code after:\n"); for(const auto& cls : classes) { TRACE(MAIN, 2, "Class %s\n", SHOW(cls)); for (const auto& dm : cls->get_dmethods()) { TRACE(MAIN, 2, "dmethod: %s\n", dm->get_name()->c_str()); if (strcmp(dm->get_name()->c_str(), "propagation") == 0) { TRACE(MAIN, 2, "dmethod: %s\n", SHOW(dm->get_code())); for (auto const instruction : dm->get_code()->get_instructions()) { //The logic will be reverted when the future //development of constant propagation optimization is done, i.e., //The code will be changed to ASSERT_TRUE(false) // if IF_EQZ or New Class Instance instruction is found if (instruction->opcode() == OPCODE_IF_EQZ || instruction->opcode() == OPCODE_NEW_INSTANCE) { ASSERT_TRUE(true); } } } } } }
TEST(ConstantPropagationTest1, constantpropagation) { g_redex = new RedexContext(); const char* dexfile = "gen/native/redex/test/integ/constant-propagation-test-dex/constant-propagation.dex"; if (access(dexfile, R_OK) != 0) { dexfile = std::getenv("dexfile"); ASSERT_NE(nullptr, dexfile); } std::vector<DexClasses> dexen; dexen.emplace_back(load_classes_from_dex(dexfile)); DexClasses& classes = dexen.back(); std::cout << "Loaded classes: " << classes.size() << std::endl; TRACE(CONSTP, 2, "Code before:\n"); for(const auto& cls : classes) { TRACE(CONSTP, 2, "Class %s\n", SHOW(cls)); if (filter_test_classes(cls->get_name()) < 2) { TRACE(CONSTP, 2, "Class %s\n", SHOW(cls)); for (const auto& dm : cls->get_dmethods()) { TRACE(CONSTP, 2, "dmethod: %s\n", dm->get_name()->c_str()); if (strcmp(dm->get_name()->c_str(), "propagation_1") == 0 || strcmp(dm->get_name()->c_str(), "propagation_2") == 0 || strcmp(dm->get_name()->c_str(), "propagation_3") == 0) { TRACE(CONSTP, 2, "dmethod: %s\n", SHOW(dm->get_code())); } } } } std::vector<Pass*> passes = { new ConstantPropagationPass(), new LocalDcePass() }; std::vector<KeepRule> null_rules; PassManager manager(passes, null_rules); Json::Value conf_obj = Json::nullValue; ConfigFiles dummy_cfg(conf_obj); dummy_cfg.using_seeds = true; manager.run_passes(dexen, dummy_cfg); TRACE(CONSTP, 2, "Code after:\n"); for(const auto& cls : classes) { TRACE(CONSTP, 2, "Class %s\n", SHOW(cls)); //ASSERT_NE(filter_test_classes(cls->get_name()), REMOVEDCLASS); if (filter_test_classes(cls->get_name()) == MAINCLASS) { for (const auto& dm : cls->get_dmethods()) { TRACE(CONSTP, 2, "dmethod: %s\n", dm->get_name()->c_str()); if (strcmp(dm->get_name()->c_str(), "propagation_1") == 0) { TRACE(CONSTP, 2, "dmethod: %s\n", SHOW(dm->get_code())); for (auto const instruction : dm->get_code()->get_instructions()) { ASSERT_NE(instruction->opcode(), OPCODE_IF_EQZ); ASSERT_NE(instruction->opcode(), OPCODE_NEW_INSTANCE); } } else if (strcmp(dm->get_name()->c_str(), "propagation_2") == 0) { TRACE(CONSTP, 2, "dmethod: %s\n", SHOW(dm->get_code())); for (auto const instruction : dm->get_code()->get_instructions()) { ASSERT_NE(instruction->opcode(), OPCODE_IF_EQZ); ASSERT_NE(instruction->opcode(), OPCODE_INVOKE_STATIC); } } else if(strcmp(dm->get_name()->c_str(), "propagation_3") == 0) { TRACE(CONSTP, 2, "dmethod: %s\n", SHOW(dm->get_code())); for (auto const instruction : dm->get_code()->get_instructions()) { //ASSERT_NE(instruction->opcode(), OPCODE_IF_EQZ); //ASSERT_NE(instruction->opcode(), OPCODE_INVOKE_STATIC); } } } } } }
TEST(EmptyClassesTest1, emptyclasses) { g_redex = new RedexContext(); const char* dexfile = "empty-classes-test-class.dex"; if (access(dexfile, R_OK) != 0) { dexfile = std::getenv("dexfile"); ASSERT_NE(nullptr, dexfile); } std::vector<DexClasses> dexen; dexen.emplace_back(load_classes_from_dex(dexfile)); DexClasses& classes = dexen.back(); size_t before = classes.size(); TRACE(EMPTY, 3, "Loaded classes: %ld\n", classes.size()); // Report the classes that were loaded through tracing. for (const auto& cls : classes) { TRACE(EMPTY, 3, "Input class: %s\n", cls->get_type()->get_name()->c_str()); } std::vector<Pass*> passes = { new DelInitPass(), new RemoveEmptyClassesPass(), }; std::vector<KeepRule> null_rules; auto const keep = { "Lcom/facebook/redextest/DoNotStrip;" }; const folly::dynamic conf_obj = folly::dynamic::object( "keep_annotations", folly::dynamic(keep.begin(), keep.end())); PassManager manager( passes, null_rules, conf_obj ); ConfigFiles dummy_cfg(conf_obj); manager.run_passes(dexen, dummy_cfg); size_t after = 0; std::set<std::string> remaining_classes; for (const auto& dex_classes : dexen) { for (const auto cls : dex_classes) { TRACE(EMPTY, 3, "Output class: %s\n", cls->get_type()->get_name()->c_str()); after++; remaining_classes.insert(SHOW(cls->get_type()->get_name())); } } TRACE(EMPTY, 2, "Removed %ld classes\n", before - after); ASSERT_EQ(0, remaining_classes.count("Lcom/facebook/redextest/EmptyClasses;")); ASSERT_EQ(0, remaining_classes.count("Lcom/facebook/redextest/InnerEmpty;")); ASSERT_EQ(0, remaining_classes.count("Lcom/facebook/redextest/InnerEmpty$InnerClass;")); ASSERT_EQ(1, remaining_classes.count("Lcom/facebook/redextest/InnerEmpty2;")); ASSERT_EQ(0, remaining_classes.count("Lcom/facebook/redextest/InnerEmpty2$InnerClass2;")); ASSERT_EQ(1, remaining_classes.count("Lcom/facebook/redextest/NotAnEmptyClass;")); ASSERT_EQ(1, remaining_classes.count("Lcom/facebook/redextest/NotAnEmptyClass2;")); ASSERT_EQ(1, remaining_classes.count("Lcom/facebook/redextest/NotAnEmptyClass3;")); ASSERT_EQ(1, remaining_classes.count("Lcom/facebook/redextest/NotAnEmptyClass4;")); ASSERT_EQ(1, remaining_classes.count("Lcom/facebook/redextest/NotAnEmptyClass5;")); ASSERT_EQ(1, remaining_classes.count("Lcom/facebook/redextest/NotAnEmptyClass5;")); ASSERT_EQ(1, remaining_classes.count("Lcom/facebook/redextest/YesNo;")); ASSERT_EQ(1, remaining_classes.count("Lcom/facebook/redextest/MyYesNo;")); ASSERT_EQ(1, remaining_classes.count("Lcom/facebook/redextest/EasilyDone;")); ASSERT_EQ(1, remaining_classes.count("Lcom/facebook/redextest/By2Or3;")); ASSERT_EQ(1, remaining_classes.count("Lcom/facebook/redextest/MyBy2Or3;")); ASSERT_EQ(1, remaining_classes.count("Lcom/facebook/redextest/WombatException;")); ASSERT_EQ(1, remaining_classes.count("Lcom/facebook/redextest/Wombat;")); ASSERT_EQ(1, remaining_classes.count("Lcom/facebook/redextest/EmptyButLaterExtended;")); ASSERT_EQ(1, remaining_classes.count("Lcom/facebook/redextest/Extender;")); ASSERT_EQ(1, remaining_classes.count("Lcom/facebook/redextest/NotUsedHere;")); ASSERT_EQ(1, remaining_classes.count("Lcom/facebook/redextest/DontKillMeNow;")); delete g_redex; }