Skip to main content

sqry_classpath/bytecode/
lambda.rs

1//! Lambda/method-reference target extraction from the `BootstrapMethods`
2//! attribute.
3//!
4//! JVM compilers (javac, kotlinc, scalac, etc.) compile lambda expressions and
5//! method references into `invokedynamic` instructions whose bootstrap method
6//! is [`java/lang/invoke/LambdaMetafactory.metafactory`][metafactory] (or
7//! `altMetafactory`). The third bootstrap argument (index 2) of such entries
8//! is a `CONSTANT_MethodHandle_info` that points to the **actual target
9//! method** being captured.
10//!
11//! This module extracts those targets from a parsed [`cafebabe::ClassFile`] and
12//! returns them as [`LambdaTargetStub`] records for inclusion in the class
13//! stub.
14//!
15//! [metafactory]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/invoke/LambdaMetafactory.html
16
17use cafebabe::attributes::AttributeData;
18use cafebabe::constant_pool::{
19    BootstrapArgument, MethodHandle, ReferenceKind as CafeReferenceKind,
20};
21
22use crate::stub::model::{LambdaTargetStub, ReferenceKind};
23
24use super::constants::class_name_to_fqn;
25
26// ---------------------------------------------------------------------------
27// Constants
28// ---------------------------------------------------------------------------
29
30/// The internal class name of `LambdaMetafactory`.
31const LAMBDA_METAFACTORY_CLASS: &str = "java/lang/invoke/LambdaMetafactory";
32
33/// The standard bootstrap method name for lambda expressions.
34const METAFACTORY_METHOD: &str = "metafactory";
35
36/// The alternative bootstrap method name for complex lambda expressions
37/// (serialisable lambdas, intersection-type target, etc.).
38const ALT_METAFACTORY_METHOD: &str = "altMetafactory";
39
40/// Index of the implementation method handle within the bootstrap arguments.
41/// For `LambdaMetafactory.metafactory`, the arguments are:
42///   0 — samMethodType  (`MethodType`)
43///   1 — implMethod     (`MethodHandle`) — **sometimes**
44///   2 — implMethod     (`MethodHandle`) — **standard position**
45///
46/// Per the JVM spec and `LambdaMetafactory` javadoc, argument index 2 is the
47/// implementation `MethodHandle`.
48const IMPL_METHOD_ARG_INDEX: usize = 2;
49
50// ---------------------------------------------------------------------------
51// Public API
52// ---------------------------------------------------------------------------
53
54/// Extract lambda/method-reference targets from a parsed class file.
55///
56/// Iterates class-level attributes, finds the `BootstrapMethods` attribute, and
57/// filters entries whose bootstrap method handle points to
58/// `LambdaMetafactory.metafactory` or `altMetafactory`. For each matching
59/// entry, the third bootstrap argument (a `MethodHandle`) is converted to a
60/// [`LambdaTargetStub`].
61///
62/// # Returns
63///
64/// A `Vec` of targets, potentially empty if no `BootstrapMethods` attribute
65/// exists or none of the entries are `LambdaMetafactory` invocations.
66///
67/// Non-`LambdaMetafactory` bootstrap entries are silently skipped. Malformed
68/// entries (e.g., fewer than 3 arguments, wrong argument type at index 2) are
69/// logged as warnings and skipped.
70#[must_use]
71#[allow(clippy::needless_continue)] // Continue clarifies control flow
72pub fn extract_lambda_targets(class: &cafebabe::ClassFile<'_>) -> Vec<LambdaTargetStub> {
73    let mut targets = Vec::new();
74
75    for attr in &class.attributes {
76        if let AttributeData::BootstrapMethods(entries) = &attr.data {
77            for (idx, entry) in entries.iter().enumerate() {
78                // Check if the bootstrap method points to LambdaMetafactory.
79                if !is_lambda_metafactory(&entry.method) {
80                    continue;
81                }
82
83                // Extract the implementation MethodHandle from argument index 2.
84                match extract_impl_handle(idx, &entry.arguments) {
85                    Some(stub) => targets.push(stub),
86                    #[allow(clippy::needless_continue)] // Continue at end of loop for clarity
87                    None => continue,
88                }
89            }
90        }
91    }
92
93    targets
94}
95
96// ---------------------------------------------------------------------------
97// Internals
98// ---------------------------------------------------------------------------
99
100/// Check whether a bootstrap method handle points to `LambdaMetafactory`.
101///
102/// The handle's class name must be `java/lang/invoke/LambdaMetafactory` and its
103/// method name must be `metafactory` or `altMetafactory`.
104fn is_lambda_metafactory(handle: &MethodHandle<'_>) -> bool {
105    handle.class_name.as_ref() == LAMBDA_METAFACTORY_CLASS
106        && (handle.member_ref.name.as_ref() == METAFACTORY_METHOD
107            || handle.member_ref.name.as_ref() == ALT_METAFACTORY_METHOD)
108}
109
110/// Extract the implementation `MethodHandle` from bootstrap arguments and
111/// convert it to a [`LambdaTargetStub`].
112///
113/// Returns `None` (with a warning log) if the arguments are too few or the
114/// third argument is not a `MethodHandle`.
115fn extract_impl_handle(
116    bootstrap_idx: usize,
117    arguments: &[BootstrapArgument<'_>],
118) -> Option<LambdaTargetStub> {
119    if arguments.len() <= IMPL_METHOD_ARG_INDEX {
120        log::warn!(
121            "BootstrapMethods entry {bootstrap_idx}: expected at least {} arguments, \
122             found {}; skipping",
123            IMPL_METHOD_ARG_INDEX + 1,
124            arguments.len(),
125        );
126        return None;
127    }
128
129    match &arguments[IMPL_METHOD_ARG_INDEX] {
130        BootstrapArgument::MethodHandle(handle) => {
131            #[allow(clippy::manual_let_else)] // Match-based error handling for clarity
132            let reference_kind = if let Some(kind) = convert_reference_kind(handle.kind) {
133                kind
134            } else {
135                log::warn!(
136                    "BootstrapMethods entry {bootstrap_idx}: \
137                     unsupported reference kind {:?}; skipping",
138                    handle.kind,
139                );
140                return None;
141            };
142
143            Some(LambdaTargetStub {
144                owner_fqn: class_name_to_fqn(handle.class_name.as_ref()),
145                method_name: handle.member_ref.name.to_string(),
146                method_descriptor: handle.member_ref.descriptor.to_string(),
147                reference_kind,
148            })
149        }
150        other => {
151            log::warn!(
152                "BootstrapMethods entry {bootstrap_idx}: expected MethodHandle at \
153                 argument index {IMPL_METHOD_ARG_INDEX}, found {kind}; skipping",
154                kind = bootstrap_arg_kind_name(other),
155            );
156            None
157        }
158    }
159}
160
161/// Convert a cafebabe [`CafeReferenceKind`] to our model [`ReferenceKind`].
162#[allow(clippy::unnecessary_wraps)] // Result for API consistency
163fn convert_reference_kind(kind: CafeReferenceKind) -> Option<ReferenceKind> {
164    Some(match kind {
165        CafeReferenceKind::GetField => ReferenceKind::GetField,
166        CafeReferenceKind::GetStatic => ReferenceKind::GetStatic,
167        CafeReferenceKind::PutField => ReferenceKind::PutField,
168        CafeReferenceKind::PutStatic => ReferenceKind::PutStatic,
169        CafeReferenceKind::InvokeVirtual => ReferenceKind::InvokeVirtual,
170        CafeReferenceKind::InvokeStatic => ReferenceKind::InvokeStatic,
171        CafeReferenceKind::InvokeSpecial => ReferenceKind::InvokeSpecial,
172        CafeReferenceKind::NewInvokeSpecial => ReferenceKind::NewInvokeSpecial,
173        CafeReferenceKind::InvokeInterface => ReferenceKind::InvokeInterface,
174    })
175}
176
177/// Return a human-readable name for a [`BootstrapArgument`] variant (for log
178/// messages).
179fn bootstrap_arg_kind_name(arg: &BootstrapArgument<'_>) -> &'static str {
180    match arg {
181        BootstrapArgument::LiteralConstant(_) => "LiteralConstant",
182        BootstrapArgument::ClassInfo(_) => "ClassInfo",
183        BootstrapArgument::MethodHandle(_) => "MethodHandle",
184        BootstrapArgument::MethodType(_) => "MethodType",
185    }
186}
187
188// ---------------------------------------------------------------------------
189// Tests
190// ---------------------------------------------------------------------------
191
192#[cfg(test)]
193mod tests {
194    use super::*;
195    use cafebabe::attributes::{AttributeData, AttributeInfo, BootstrapMethodEntry};
196    use cafebabe::constant_pool::{
197        BootstrapArgument, MemberKind, MethodHandle, NameAndType,
198        ReferenceKind as CafeReferenceKind,
199    };
200    use std::borrow::Cow;
201
202    // -- Test helpers ---------------------------------------------------------
203
204    /// Build a `MethodHandle` pointing to `LambdaMetafactory.metafactory`.
205    fn metafactory_handle<'a>() -> MethodHandle<'a> {
206        MethodHandle {
207            kind: CafeReferenceKind::InvokeStatic,
208            class_name: Cow::Borrowed(LAMBDA_METAFACTORY_CLASS),
209            member_kind: MemberKind::Method,
210            member_ref: NameAndType {
211                name: Cow::Borrowed(METAFACTORY_METHOD),
212                descriptor: Cow::Borrowed(
213                    "(Ljava/lang/invoke/MethodHandles$Lookup;\
214                     Ljava/lang/String;\
215                     Ljava/lang/invoke/MethodType;\
216                     Ljava/lang/invoke/MethodType;\
217                     Ljava/lang/invoke/MethodHandle;\
218                     Ljava/lang/invoke/MethodType;\
219                     )Ljava/lang/invoke/CallSite;",
220                ),
221            },
222        }
223    }
224
225    /// Build a `MethodHandle` pointing to `LambdaMetafactory.altMetafactory`.
226    fn alt_metafactory_handle<'a>() -> MethodHandle<'a> {
227        MethodHandle {
228            kind: CafeReferenceKind::InvokeStatic,
229            class_name: Cow::Borrowed(LAMBDA_METAFACTORY_CLASS),
230            member_kind: MemberKind::Method,
231            member_ref: NameAndType {
232                name: Cow::Borrowed(ALT_METAFACTORY_METHOD),
233                descriptor: Cow::Borrowed(
234                    "(Ljava/lang/invoke/MethodHandles$Lookup;\
235                     Ljava/lang/String;\
236                     Ljava/lang/invoke/MethodType;\
237                     [Ljava/lang/Object;\
238                     )Ljava/lang/invoke/CallSite;",
239                ),
240            },
241        }
242    }
243
244    /// Build a non-lambda bootstrap handle (e.g., `StringConcatFactory`).
245    fn string_concat_handle<'a>() -> MethodHandle<'a> {
246        MethodHandle {
247            kind: CafeReferenceKind::InvokeStatic,
248            class_name: Cow::Borrowed("java/lang/invoke/StringConcatFactory"),
249            member_kind: MemberKind::Method,
250            member_ref: NameAndType {
251                name: Cow::Borrowed("makeConcatWithConstants"),
252                descriptor: Cow::Borrowed(
253                    "(Ljava/lang/invoke/MethodHandles$Lookup;\
254                     Ljava/lang/String;\
255                     Ljava/lang/invoke/MethodType;\
256                     Ljava/lang/String;\
257                     [Ljava/lang/Object;\
258                     )Ljava/lang/invoke/CallSite;",
259                ),
260            },
261        }
262    }
263
264    /// Build the standard 3-argument list for a `LambdaMetafactory` entry.
265    ///
266    /// Arguments: [`MethodType(sam_descriptor)`, MethodType(instantiated),
267    /// MethodHandle(impl)].
268    fn lambda_bootstrap_args<'a>(
269        impl_kind: CafeReferenceKind,
270        impl_class: &'a str,
271        impl_name: &'a str,
272        impl_descriptor: &'a str,
273    ) -> Vec<BootstrapArgument<'a>> {
274        vec![
275            // arg 0: SAM method type
276            BootstrapArgument::MethodType(Cow::Borrowed("(Ljava/lang/Object;)Ljava/lang/Object;")),
277            // arg 1: instantiated method type
278            BootstrapArgument::MethodType(Cow::Borrowed("(Ljava/lang/String;)Ljava/lang/String;")),
279            // arg 2: implementation method handle
280            BootstrapArgument::MethodHandle(MethodHandle {
281                kind: impl_kind,
282                class_name: Cow::Borrowed(impl_class),
283                member_kind: MemberKind::Method,
284                member_ref: NameAndType {
285                    name: Cow::Borrowed(impl_name),
286                    descriptor: Cow::Borrowed(impl_descriptor),
287                },
288            }),
289        ]
290    }
291
292    /// Parse a real class file and extract lambda targets. This requires
293    /// building a `ClassFile` from bytes, which is the full integration path.
294    /// These unit tests use the component function directly with constructed
295    /// bootstrap entries instead.
296
297    // -- Test 1: No BootstrapMethods attribute → empty result -----------------
298
299    #[test]
300    fn no_bootstrap_methods_returns_empty() {
301        // Simulate a class with no BootstrapMethods attribute by calling the
302        // extraction logic directly on empty attribute lists.
303        let attrs: Vec<AttributeInfo<'_>> = vec![];
304        let targets = extract_lambda_targets_from_attrs(&attrs);
305        assert!(targets.is_empty(), "Expected empty targets");
306    }
307
308    // -- Test 2: Lambda target from stream().map(String::toUpperCase) ---------
309
310    #[test]
311    fn lambda_target_from_method_reference() {
312        let entries = vec![BootstrapMethodEntry {
313            method: metafactory_handle(),
314            arguments: lambda_bootstrap_args(
315                CafeReferenceKind::InvokeVirtual,
316                "java/lang/String",
317                "toUpperCase",
318                "()Ljava/lang/String;",
319            ),
320        }];
321
322        let attrs = vec![AttributeInfo {
323            name: Cow::Borrowed("BootstrapMethods"),
324            data: AttributeData::BootstrapMethods(entries),
325        }];
326
327        let targets = extract_lambda_targets_from_attrs(&attrs);
328
329        assert_eq!(targets.len(), 1);
330        assert_eq!(targets[0].owner_fqn, "java.lang.String");
331        assert_eq!(targets[0].method_name, "toUpperCase");
332        assert_eq!(targets[0].method_descriptor, "()Ljava/lang/String;");
333        assert_eq!(targets[0].reference_kind, ReferenceKind::InvokeVirtual);
334    }
335
336    // -- Test 3: Method reference target correctly identified -----------------
337
338    #[test]
339    fn method_reference_with_invoke_static() {
340        let entries = vec![BootstrapMethodEntry {
341            method: metafactory_handle(),
342            arguments: lambda_bootstrap_args(
343                CafeReferenceKind::InvokeStatic,
344                "java/lang/Integer",
345                "parseInt",
346                "(Ljava/lang/String;)I",
347            ),
348        }];
349
350        let attrs = vec![AttributeInfo {
351            name: Cow::Borrowed("BootstrapMethods"),
352            data: AttributeData::BootstrapMethods(entries),
353        }];
354
355        let targets = extract_lambda_targets_from_attrs(&attrs);
356
357        assert_eq!(targets.len(), 1);
358        assert_eq!(targets[0].owner_fqn, "java.lang.Integer");
359        assert_eq!(targets[0].method_name, "parseInt");
360        assert_eq!(targets[0].method_descriptor, "(Ljava/lang/String;)I");
361        assert_eq!(targets[0].reference_kind, ReferenceKind::InvokeStatic);
362    }
363
364    // -- Test 4: Non-LambdaMetafactory bootstrap entries skipped --------------
365
366    #[test]
367    fn non_lambda_metafactory_skipped() {
368        let entries = vec![BootstrapMethodEntry {
369            method: string_concat_handle(),
370            arguments: vec![BootstrapArgument::LiteralConstant(
371                cafebabe::constant_pool::LiteralConstant::String(Cow::Borrowed("\u{1}Hello \u{1}")),
372            )],
373        }];
374
375        let attrs = vec![AttributeInfo {
376            name: Cow::Borrowed("BootstrapMethods"),
377            data: AttributeData::BootstrapMethods(entries),
378        }];
379
380        let targets = extract_lambda_targets_from_attrs(&attrs);
381        assert!(
382            targets.is_empty(),
383            "Non-LambdaMetafactory should be skipped"
384        );
385    }
386
387    // -- Test 5: Multiple lambda targets in one class -------------------------
388
389    #[test]
390    fn multiple_lambda_targets() {
391        let entries = vec![
392            // Entry 0: String::toUpperCase method reference
393            BootstrapMethodEntry {
394                method: metafactory_handle(),
395                arguments: lambda_bootstrap_args(
396                    CafeReferenceKind::InvokeVirtual,
397                    "java/lang/String",
398                    "toUpperCase",
399                    "()Ljava/lang/String;",
400                ),
401            },
402            // Entry 1: StringConcatFactory (not lambda — should be skipped)
403            BootstrapMethodEntry {
404                method: string_concat_handle(),
405                arguments: vec![],
406            },
407            // Entry 2: Constructor reference (NewInvokeSpecial)
408            BootstrapMethodEntry {
409                method: metafactory_handle(),
410                arguments: lambda_bootstrap_args(
411                    CafeReferenceKind::NewInvokeSpecial,
412                    "java/util/ArrayList",
413                    "<init>",
414                    "()V",
415                ),
416            },
417            // Entry 3: altMetafactory — serialisable lambda
418            BootstrapMethodEntry {
419                method: alt_metafactory_handle(),
420                arguments: lambda_bootstrap_args(
421                    CafeReferenceKind::InvokeStatic,
422                    "com/example/Service",
423                    "lambda$process$0",
424                    "(Ljava/lang/Object;)V",
425                ),
426            },
427        ];
428
429        let attrs = vec![AttributeInfo {
430            name: Cow::Borrowed("BootstrapMethods"),
431            data: AttributeData::BootstrapMethods(entries),
432        }];
433
434        let targets = extract_lambda_targets_from_attrs(&attrs);
435
436        // 3 lambda entries (indices 0, 2, 3); index 1 is StringConcatFactory.
437        assert_eq!(
438            targets.len(),
439            3,
440            "Expected 3 lambda targets, got {}",
441            targets.len()
442        );
443
444        assert_eq!(targets[0].owner_fqn, "java.lang.String");
445        assert_eq!(targets[0].method_name, "toUpperCase");
446        assert_eq!(targets[0].reference_kind, ReferenceKind::InvokeVirtual);
447
448        assert_eq!(targets[1].owner_fqn, "java.util.ArrayList");
449        assert_eq!(targets[1].method_name, "<init>");
450        assert_eq!(targets[1].reference_kind, ReferenceKind::NewInvokeSpecial);
451
452        assert_eq!(targets[2].owner_fqn, "com.example.Service");
453        assert_eq!(targets[2].method_name, "lambda$process$0");
454        assert_eq!(targets[2].reference_kind, ReferenceKind::InvokeStatic);
455    }
456
457    // -- Test 6: Reference kind correctly mapped for all variants -------------
458
459    #[test]
460    fn reference_kind_mapping_exhaustive() {
461        let cafe_to_model = [
462            (CafeReferenceKind::GetField, ReferenceKind::GetField),
463            (CafeReferenceKind::GetStatic, ReferenceKind::GetStatic),
464            (CafeReferenceKind::PutField, ReferenceKind::PutField),
465            (CafeReferenceKind::PutStatic, ReferenceKind::PutStatic),
466            (
467                CafeReferenceKind::InvokeVirtual,
468                ReferenceKind::InvokeVirtual,
469            ),
470            (CafeReferenceKind::InvokeStatic, ReferenceKind::InvokeStatic),
471            (
472                CafeReferenceKind::InvokeSpecial,
473                ReferenceKind::InvokeSpecial,
474            ),
475            (
476                CafeReferenceKind::NewInvokeSpecial,
477                ReferenceKind::NewInvokeSpecial,
478            ),
479            (
480                CafeReferenceKind::InvokeInterface,
481                ReferenceKind::InvokeInterface,
482            ),
483        ];
484
485        for (cafe_kind, expected_model_kind) in &cafe_to_model {
486            let result = convert_reference_kind(*cafe_kind);
487            assert_eq!(
488                result,
489                Some(*expected_model_kind),
490                "Mapping failed for {cafe_kind:?}"
491            );
492        }
493    }
494
495    // -- Test 7: Malformed entry — too few arguments --------------------------
496
497    #[test]
498    fn too_few_arguments_skipped_with_warning() {
499        // Only 2 arguments instead of the required 3.
500        let entries = vec![BootstrapMethodEntry {
501            method: metafactory_handle(),
502            arguments: vec![
503                BootstrapArgument::MethodType(Cow::Borrowed(
504                    "(Ljava/lang/Object;)Ljava/lang/Object;",
505                )),
506                BootstrapArgument::MethodType(Cow::Borrowed(
507                    "(Ljava/lang/String;)Ljava/lang/String;",
508                )),
509            ],
510        }];
511
512        let attrs = vec![AttributeInfo {
513            name: Cow::Borrowed("BootstrapMethods"),
514            data: AttributeData::BootstrapMethods(entries),
515        }];
516
517        let targets = extract_lambda_targets_from_attrs(&attrs);
518        assert!(targets.is_empty(), "Malformed entry should be skipped");
519    }
520
521    // -- Test 8: Wrong argument type at index 2 -------------------------------
522
523    #[test]
524    fn wrong_argument_type_at_index_2_skipped() {
525        // Argument index 2 is a MethodType instead of a MethodHandle.
526        let entries = vec![BootstrapMethodEntry {
527            method: metafactory_handle(),
528            arguments: vec![
529                BootstrapArgument::MethodType(Cow::Borrowed(
530                    "(Ljava/lang/Object;)Ljava/lang/Object;",
531                )),
532                BootstrapArgument::MethodType(Cow::Borrowed(
533                    "(Ljava/lang/String;)Ljava/lang/String;",
534                )),
535                BootstrapArgument::MethodType(Cow::Borrowed("()V")),
536            ],
537        }];
538
539        let attrs = vec![AttributeInfo {
540            name: Cow::Borrowed("BootstrapMethods"),
541            data: AttributeData::BootstrapMethods(entries),
542        }];
543
544        let targets = extract_lambda_targets_from_attrs(&attrs);
545        assert!(
546            targets.is_empty(),
547            "Wrong type at index 2 should be skipped"
548        );
549    }
550
551    // -- Test 9: Interface method reference -----------------------------------
552
553    #[test]
554    fn interface_method_reference() {
555        let entries = vec![BootstrapMethodEntry {
556            method: metafactory_handle(),
557            arguments: lambda_bootstrap_args(
558                CafeReferenceKind::InvokeInterface,
559                "java/util/List",
560                "size",
561                "()I",
562            ),
563        }];
564
565        let attrs = vec![AttributeInfo {
566            name: Cow::Borrowed("BootstrapMethods"),
567            data: AttributeData::BootstrapMethods(entries),
568        }];
569
570        let targets = extract_lambda_targets_from_attrs(&attrs);
571
572        assert_eq!(targets.len(), 1);
573        assert_eq!(targets[0].owner_fqn, "java.util.List");
574        assert_eq!(targets[0].method_name, "size");
575        assert_eq!(targets[0].reference_kind, ReferenceKind::InvokeInterface);
576    }
577
578    // -- Test 10: FQN conversion from internal format -------------------------
579
580    #[test]
581    fn fqn_conversion_internal_to_dotted() {
582        let entries = vec![BootstrapMethodEntry {
583            method: metafactory_handle(),
584            arguments: lambda_bootstrap_args(
585                CafeReferenceKind::InvokeStatic,
586                "com/example/deeply/nested/ServiceImpl",
587                "handle",
588                "(Ljava/lang/Object;)V",
589            ),
590        }];
591
592        let attrs = vec![AttributeInfo {
593            name: Cow::Borrowed("BootstrapMethods"),
594            data: AttributeData::BootstrapMethods(entries),
595        }];
596
597        let targets = extract_lambda_targets_from_attrs(&attrs);
598
599        assert_eq!(targets.len(), 1);
600        assert_eq!(
601            targets[0].owner_fqn,
602            "com.example.deeply.nested.ServiceImpl"
603        );
604    }
605
606    // -- Test helper: extract from attributes without a full ClassFile ---------
607
608    /// Helper that mirrors `extract_lambda_targets` but operates on a bare
609    /// attribute slice so tests don't need to construct a full `ClassFile`.
610    fn extract_lambda_targets_from_attrs(attrs: &[AttributeInfo<'_>]) -> Vec<LambdaTargetStub> {
611        let mut targets = Vec::new();
612        for attr in attrs {
613            if let AttributeData::BootstrapMethods(entries) = &attr.data {
614                for (idx, entry) in entries.iter().enumerate() {
615                    if !is_lambda_metafactory(&entry.method) {
616                        continue;
617                    }
618                    if let Some(stub) = extract_impl_handle(idx, &entry.arguments) {
619                        targets.push(stub);
620                    }
621                }
622            }
623        }
624        targets
625    }
626}