From 9e697002a27c2b8b87ce062b03e91fce35a69e3f Mon Sep 17 00:00:00 2001 From: Ivy Tang Date: Mon, 6 Jan 2025 15:17:10 -0800 Subject: [PATCH] Support DCG components Summary: DCG components are just getters with `component` in the name. Patch their return when the field they're getting is annotated Reviewed By: thezhangwei Differential Revision: D67367181 fbshipit-source-id: 2e66dd49fd0be9f04a21d08a22228ac62f632484 --- .../TypedefAnnoCheckerPass.cpp | 38 ++++++++++++++----- .../TypedefAnnoCheckerPass.h | 2 + 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/opt/typedef-anno-checker/TypedefAnnoCheckerPass.cpp b/opt/typedef-anno-checker/TypedefAnnoCheckerPass.cpp index 46dcf414769..fe7ef9cb430 100644 --- a/opt/typedef-anno-checker/TypedefAnnoCheckerPass.cpp +++ b/opt/typedef-anno-checker/TypedefAnnoCheckerPass.cpp @@ -31,6 +31,8 @@ constexpr const char* OBJ_REF_CLS = "Lkotlin/jvm/internal/Ref$ObjectRef;"; constexpr const char* OBJ_REF_FIELD = "Lkotlin/jvm/internal/Ref$ObjectRef;.element:Ljava/lang/Object;"; +constexpr const char* DATA_CLASS_COMPONENT = "component"; + namespace { bool is_int(const type_inference::TypeEnvironment& env, reg_t reg) { @@ -465,13 +467,20 @@ void patch_return_anno_from_get(type_inference::TypeInference& inference, opcode::is_an_sget(insn->opcode())); auto name = caller->get_simple_deobfuscated_name(); auto last_dollar = name.find('$'); - if (last_dollar == std::string::npos) { - return; - } - last_dollar++; - if (!(last_dollar < name.size() && name[last_dollar] >= '0' && - name[last_dollar] <= '9')) { - return; + + // the caller methods are either data class components, named "componentX", + // or they're Java field accessors, which have the pattern access$XXX, where + // X is an integer + if (!boost::starts_with(caller->get_simple_deobfuscated_name(), + DATA_CLASS_COMPONENT)) { + if (last_dollar == std::string::npos) { + return; + } + last_dollar++; + if (!(last_dollar < name.size() && name[last_dollar] >= '0' && + name[last_dollar] <= '9')) { + return; + } } auto field_ref = insn->get_field(); auto field_anno = type_inference::get_typedef_anno_from_member( @@ -479,9 +488,7 @@ void patch_return_anno_from_get(type_inference::TypeInference& inference, if (field_anno != boost::none) { // Patch missing return annotations from accessed fields - auto res = caller->attach_annotation_set(std::make_unique( - *field_ref->as_def()->get_anno_set())); - always_assert(res); + add_annotations(caller, field_ref->as_def()->get_anno_set()); } } @@ -644,6 +651,7 @@ void SynthAccessorPatcher::patch_kotlin_property_private_getter(DexMethod* m) { void SynthAccessorPatcher::run(const Scope& scope) { walk::parallel::methods(scope, [this](DexMethod* m) { patch_kotlin_annotated_property_getter_setter(m); + patch_data_class_component(m); if (is_synthetic_accessor(m) || is_synthetic_bridge(m)) { patch_accessors(m); } @@ -675,6 +683,16 @@ void SynthAccessorPatcher::run(const Scope& scope) { }); } +void SynthAccessorPatcher::patch_data_class_component(DexMethod* m) { + if (type_class(m->get_class())->get_super_class()->str() == + "Lcom/facebook/kotlin/compilerplugins/dataclassgenerate/superclass/" + "DataClassSuper;" && + boost::starts_with(m->get_simple_deobfuscated_name(), + DATA_CLASS_COMPONENT)) { + patch_accessors(m); + } +} + void SynthAccessorPatcher::patch_ctor_params_from_synth_cls_fields( DexClass* cls) { bool has_annotated_fields = false; diff --git a/opt/typedef-anno-checker/TypedefAnnoCheckerPass.h b/opt/typedef-anno-checker/TypedefAnnoCheckerPass.h index 70678597ee1..9175f00ea93 100644 --- a/opt/typedef-anno-checker/TypedefAnnoCheckerPass.h +++ b/opt/typedef-anno-checker/TypedefAnnoCheckerPass.h @@ -120,6 +120,8 @@ class SynthAccessorPatcher { void patch_local_var_lambda(DexMethod* method); + void patch_data_class_component(DexMethod* method); + void collect_annos_from_default_method( DexMethod* method, std::vector>&