@@ -135,6 +135,9 @@ DexClass* find_canonical_class(
135135struct SingletonDeduplicationResult {
136136 // Map from original INSTANCE field to the canonical's renamed field.
137137 std::unordered_map<DexFieldRef*, DexFieldRef*> field_redirect_map;
138+ // Map from non-canonical lambda type to the canonical's type.
139+ // Used to redirect type references (invoke-virtual, check-cast, etc.).
140+ std::unordered_map<const DexType*, DexType*> type_redirect_map;
138141 size_t lambdas_deduplicated = 0 ;
139142 size_t duplicate_group_count = 0 ;
140143};
@@ -186,6 +189,7 @@ SingletonDeduplicationResult process_singleton_duplicates(
186189 DexField* instance_field = get_singleton_field (lambda_cls, instance_name);
187190 always_assert (instance_field != nullptr );
188191 result.field_redirect_map [instance_field] = canonical_instance;
192+ result.type_redirect_map [lambda_cls->get_type ()] = canonical->get_type ();
189193 result.lambdas_deduplicated ++;
190194 }
191195
@@ -334,18 +338,30 @@ void KotlinLambdaDeduplicationPass::run_pass(DexStoresVector& stores,
334338 m_min_duplicate_group_size);
335339
336340 // Step 4: Process duplicate groups.
337- const auto singleton_result = process_singleton_duplicates (
341+ auto singleton_result = process_singleton_duplicates (
338342 singleton_tracker, class_to_dex_idx, m_min_duplicate_group_size);
339- const auto non_singleton_result = process_non_singleton_duplicates (
343+ auto non_singleton_result = process_non_singleton_duplicates (
340344 non_singleton_tracker, class_to_dex_idx, m_min_duplicate_group_size);
341345
342346 // Step 5: Rewrite all usages.
347+ // - For both singleton and non-singleton lambdas: redirect all type
348+ // references (new-instance, check-cast, etc.) and method references
349+ // (invoke-virtual, invoke-interface, etc.) on non-canonical lambda types
350+ // to the canonical. This is necessary because IntraDexClassMergingPass
351+ // runs after this pass and may merge the canonical and non-canonical
352+ // lambdas into different shape types. Any unrewritten reference to a
353+ // non-canonical type would then point to the wrong shape.
343354 // - For singleton lambdas: redirect sget on INSTANCE fields.
344- // - For non-singleton lambdas: redirect new-instance and invoke-direct
345- // <init>.
355+ // - For non-singleton lambdas: additionally redirect invoke-direct <init>.
346356 // We count singleton and non-singleton rewrites separately. For non-singleton
347357 // lambdas, each usage consists of new-instance + invoke-direct, so we only
348358 // count new-instance rewrites to get the usage count.
359+
360+ // Build a combined type redirect map covering both singleton and
361+ // non-singleton non-canonical lambda types.
362+ auto type_redirect_map = std::move (singleton_result.type_redirect_map );
363+ type_redirect_map.merge (non_singleton_result.type_redirect_map );
364+
349365 struct RewriteCounts {
350366 size_t singleton = 0 ;
351367 size_t non_singleton = 0 ;
@@ -358,8 +374,7 @@ void KotlinLambdaDeduplicationPass::run_pass(DexStoresVector& stores,
358374 const auto rewrite_counts = walk::parallel::methods<RewriteCounts>(
359375 scope,
360376 [&field_redirect_map = std::as_const (singleton_result.field_redirect_map ),
361- &type_redirect_map =
362- std::as_const (non_singleton_result.type_redirect_map ),
377+ &type_redirect_map = std::as_const (type_redirect_map),
363378 &ctor_redirect_map = std::as_const (
364379 non_singleton_result.ctor_redirect_map )](DexMethod* meth) {
365380 RewriteCounts counts;
@@ -368,6 +383,14 @@ void KotlinLambdaDeduplicationPass::run_pass(DexStoresVector& stores,
368383 return counts;
369384 }
370385
386+ // Skip methods belonging to non-canonical lambda classes. Their code
387+ // is dead after dedup (all usage sites have been redirected), and
388+ // rewriting self-referencing method calls (e.g., the bridge invoke
389+ // calling this.invoke()) would create type inconsistencies.
390+ if (type_redirect_map.contains (meth->get_class ())) {
391+ return counts;
392+ }
393+
371394 always_assert (code->cfg_built ());
372395 auto & cfg = code->cfg ();
373396
@@ -383,21 +406,42 @@ void KotlinLambdaDeduplicationPass::run_pass(DexStoresVector& stores,
383406 insn->set_field (it->second );
384407 counts.singleton ++;
385408 }
386- } else if (opcode == OPCODE_NEW_INSTANCE) {
387- // Redirect new-instance (non-singleton lambdas).
409+ continue ;
410+ }
411+
412+ // Redirect type operands: new-instance, check-cast, instance-of,
413+ // const-class, new-array (non-singleton lambdas).
414+ if (insn->has_type ()) {
388415 const auto * type = insn->get_type ();
389416 if (const auto it = type_redirect_map.find (type);
390417 it != type_redirect_map.end ()) {
391418 insn->set_type (it->second );
392- counts.non_singleton ++;
419+ if (opcode == OPCODE_NEW_INSTANCE) {
420+ counts.non_singleton ++;
421+ }
393422 }
394- } else if (opcode == OPCODE_INVOKE_DIRECT) {
395- // Redirect invoke-direct <init> (non-singleton lambdas).
396- // Don't count this - new-instance already counted the usage.
423+ }
424+
425+ // Redirect method ref operands.
426+ if (insn->has_method ()) {
397427 auto * method_ref = insn->get_method ();
398- if (const auto it = ctor_redirect_map.find (method_ref);
399- it != ctor_redirect_map.end ()) {
400- insn->set_method (it->second );
428+ // For invoke-direct, first try the constructor redirect map.
429+ if (opcode == OPCODE_INVOKE_DIRECT) {
430+ if (const auto it = ctor_redirect_map.find (method_ref);
431+ it != ctor_redirect_map.end ()) {
432+ insn->set_method (it->second );
433+ continue ;
434+ }
435+ }
436+ // Redirect by declaring type (covers invoke-virtual,
437+ // invoke-interface, invoke-direct on private methods, etc.).
438+ const auto * declaring_type = method_ref->get_class ();
439+ if (const auto it = type_redirect_map.find (declaring_type);
440+ it != type_redirect_map.end ()) {
441+ auto * redirected = DexMethod::get_method (
442+ it->second , method_ref->get_name (), method_ref->get_proto ());
443+ always_assert (redirected != nullptr );
444+ insn->set_method (redirected);
401445 }
402446 }
403447 }
0 commit comments