Skip to main content

jdc_core/
emit.rs

1//! Java source emission from the statement/expression tree.
2
3use crate::types::{JavaType};
4
5use crate::ir::expr::{BinOp, ConcatPart, ConstVal, Expr, LambdaKind, TypeRef, UnOp};
6use crate::ctx::MethodBody;
7use crate::ir::stmt::{CaseGroup, Catch, Stmt};
8use crate::var::VarTable;
9
10pub struct Printer<'a> {
11    /// Front-end services: naming, nesting, type queries, nested bodies.
12    pub ctx: &'a dyn crate::ctx::Ctx,
13    pub vt: &'a VarTable,
14    out: String,
15    indent: usize,
16    /// Depth guard for recursive lambda printing.
17    lambda_depth: usize,
18    /// Names visible in enclosing lambda/method scopes BEYOND self.vt,
19    /// accumulated down the lambda print chain: a nested lambda's locals
20    /// must not redeclare ANY of them (jdk26 PackageSnippets
21    /// .fooToBarUnrolled: the depth-2 codeBuilder lambda's foreach `i$`
22    /// collided with the METHOD-level `i$` — the depth-1 rename only
23    /// consulted depth-1's vt, 已在方法中定义了变量 i$).
24    outer_names: Vec<String>,
25    /// True when the enclosing method returns boolean.
26    pub ret_bool: bool,
27    /// True while rendering the ROOT of an expression statement: a
28    /// signature-polymorphic call there must stay bare (`NEXT.compareAndSet
29    /// (b, n, p);`) — `(boolean) call();` is not a legal statement.
30    suppress_poly_cast: bool,
31    /// True while rendering the direct child of a cast expression: a cast
32    /// gives the diamond NO target type, so ` (T) new X<>(args)` infers
33    /// Object bounds and then fails the cast — while the bare/raw form
34    /// `(T) new X(args)` compiles (unchecked).
35    suppress_diamond: bool,
36    /// True while rendering a conditional expression's branches. Pre-Java-8
37    /// (major < 52) conditionals are NOT poly expressions: a diamond branch
38    /// (`c ? new HashMap<>(..) : new LinkedHashMap<>(..)`) infers each side
39    /// standalone to <Object,Object> and fails the assignment target (jdk7
40    /// HashSet.readObject 不兼容的类型 — the golden source carried explicit
41    /// <E,Object> args). Diamond branches print RAW there instead:
42    /// unchecked but always compilable.
43    in_cond: bool,
44    /// Instantiated SAM return type while rendering the Lambda directly
45    /// inside a cast to a generic functional interface: the lambda body's
46    /// returns may need `(R) value` witnesses (jdk17 Collectors
47    /// `(Function<I,R>) i -> i`).
48    lambda_sam_ret: Option<TypeRef>,
49    /// The method's generic signature return (functional-interface or
50    /// array form): a lambda in RETURN position has no cast node to hang
51    /// the SAM witness on, so the Return arm derives it from this.
52    ret_sam: Option<TypeRef>,
53    /// True when the enclosing method returns char: int constants in the
54    /// returned expression render as char literals (bytecode chars are
55    /// ints; `return cond ? 63 : 105;` in a char method is a lossy
56    /// conversion — source was `? '?' : 'i'`, jdk XML Parser x23).
57    pub ret_char: bool,
58    /// Narrowing-return markers: int-valued expressions in the return
59    /// need `(byte)`/`(short)` casts (jdk11 MemberName:
60    /// `byte normalVirtual = cond ? 5 : 9;`).
61    pub ret_byte: bool,
62    pub ret_short: bool,
63}
64
65impl<'a> Printer<'a> {
66    pub fn new(ctx: &'a dyn crate::ctx::Ctx, vt: &'a VarTable) -> Self {
67        Printer { ctx, vt, out: String::new(), indent: 0, lambda_depth: 0, outer_names: Vec::new(), ret_bool: false, suppress_poly_cast: false, suppress_diamond: false, in_cond: false, lambda_sam_ret: None, ret_sam: None, ret_char: false, ret_byte: false, ret_short: false }
68    }
69
70    pub fn with_ret_bool(mut self, b: bool) -> Self {
71        self.ret_bool = b;
72        self
73    }
74
75    pub fn with_ret_sam(mut self, t: Option<TypeRef>) -> Self {
76        self.ret_sam = t;
77        self
78    }
79    pub fn with_ret_char(mut self, b: bool) -> Self {
80        self.ret_char = b;
81        self
82    }
83
84    pub fn with_ret_narrow(mut self, byte: bool, short: bool) -> Self {
85        self.ret_byte = byte;
86        self.ret_short = short;
87        self
88    }
89
90    /// Printer starting at a fixed indentation level (method bodies at 1).
91    pub fn with_indent(mut self, indent: usize) -> Self {
92        self.indent = indent;
93        self
94    }
95
96    pub fn into_string(mut self, body: &Stmt) -> String {
97        let body = truncate_dead_ends(body);
98        self.stmt(&body);
99        self.out
100    }
101
102    /// Print an expression that appears in a boolean context; int 0/1
103    /// constants become true/false.
104    fn bool_ish_expr(x: &Expr) -> bool {
105        match x {
106            Expr::Const(ConstVal::Int(n)) => *n == 0 || *n == 1,
107            Expr::Cond { t, f, .. } => {
108                matches!(&**t, Expr::Const(ConstVal::Int(0 | 1)))
109                    && matches!(&**f, Expr::Const(ConstVal::Int(0 | 1)))
110            }
111            Expr::Un { op: crate::ir::expr::UnOp::Not, .. } | Expr::InstanceOf { .. } => true,
112            Expr::Bin { op, .. } => matches!(
113                op,
114                crate::ir::expr::BinOp::Eq
115                    | crate::ir::expr::BinOp::Ne
116                    | crate::ir::expr::BinOp::Lt
117                    | crate::ir::expr::BinOp::Ge
118                    | crate::ir::expr::BinOp::Gt
119                    | crate::ir::expr::BinOp::Le
120                    | crate::ir::expr::BinOp::RefEq
121                    | crate::ir::expr::BinOp::RefNe
122                    | crate::ir::expr::BinOp::LogAnd
123                    | crate::ir::expr::BinOp::LogOr
124            ),
125            other => other.type_ref().erased() == crate::types::JavaType::Boolean,
126        }
127    }
128
129    /// bool_ish_expr with VarTable resolution: booleanize retypes the vt
130    /// while embedded Local tys stay frozen, and a bitwise bin over
131    /// bool-ish sides is itself boolean (jdk26 LinkedTransferQueue
132    /// `(spin & !upc) == 0`).
133    fn bool_ish_vt(&self, x: &Expr) -> bool {
134        match x {
135            Expr::Local { var, .. } => {
136                matches!(self.vt.var(*var).ty.erased(), crate::types::JavaType::Boolean)
137            }
138            Expr::Bin { op, l, r, .. }
139                if matches!(
140                    op,
141                    crate::ir::expr::BinOp::And | crate::ir::expr::BinOp::Or | crate::ir::expr::BinOp::Xor
142                ) =>
143            {
144                self.bool_ish_vt(l) && self.bool_ish_vt(r)
145            }
146            _ => Self::bool_ish_expr(x),
147        }
148    }
149
150    pub fn expr_bool(&mut self, e: &Expr, out: &mut String) {
151        match e {
152            Expr::Const(ConstVal::Int(n)) => {
153                out.push_str(if *n != 0 { "true" } else { "false" });
154            }
155            // Bitwise ops over boolean-ish operands print as their
156            // logical form: javac computes `a != b` over booleans as
157            // IXOR of 0/1 ints (jdk17 DecimalFormat isNegative:
158            // `(c1 ? 1 : 0) ^ (c2 ? 1 : 0)` against a boolean local —
159            // "int无法转换为boolean").
160            Expr::Bin { op: bop, l, r, .. }
161                if matches!(
162                    bop,
163                    crate::ir::expr::BinOp::Xor | crate::ir::expr::BinOp::And | crate::ir::expr::BinOp::Or
164                ) && self.bool_ish_vt(l)
165                    && self.bool_ish_vt(r) =>
166            {
167                let sym = match bop {
168                    crate::ir::expr::BinOp::Xor => "^",
169                    crate::ir::expr::BinOp::And => "&",
170                    _ => "|",
171                };
172                // Parenthesize composite sides: expr_bool renders `||`/
173                // `&&` conditions bare and ^ binds LOOSER than && but
174                // TIGHTER than || (a || b && c ^ d would regroup).
175                for (i, side) in [l.as_ref(), r.as_ref()].iter().enumerate() {
176                    if i > 0 {
177                        out.push(' ');
178                        out.push_str(sym);
179                        out.push(' ');
180                    }
181                    let needs = matches!(
182                        side,
183                        Expr::Bin { .. } | Expr::Cond { .. } | Expr::Assign { .. }
184                    );
185                    if needs {
186                        out.push('(');
187                    }
188                    self.expr_bool(side, out);
189                    if needs {
190                        out.push(')');
191                    }
192                }
193                return;
194            }
195            Expr::Cond { c, t, f } => {
196                // `x ? true : false` → x ; `x ? false : true` → !x
197                let tb = const_bool(t);
198                let fb = const_bool(f);
199                match (tb, fb) {
200                    (Some(true), Some(false)) => self.expr_bool(c, out),
201                    (Some(false), Some(true)) => {
202                        // !(!x) collapses to x
203                        if let Expr::Un { op: crate::ir::expr::UnOp::Not, e: inner } = &**c {
204                            self.expr_bool(inner, out);
205                        } else {
206                            out.push('!');
207                            // expr_bool renders composite conditions bare:
208                            // `!` + `ssign != 45` would regroup as
209                            // `(!ssign) != 45` (jdk26 FloatingDecimal
210                            // "一元运算符 '!' 的操作数类型int错误").
211                            let needs = matches!(
212                                &**c,
213                                Expr::Bin { .. } | Expr::Cond { .. } | Expr::Assign { .. }
214                            );
215                            if needs {
216                                out.push('(');
217                            }
218                            self.expr_bool(c, out);
219                            if needs {
220                                out.push(')');
221                            }
222                        }
223                    }
224                    _ => {
225                        self.expr(c, 3, out);
226                        out.push_str(" ? ");
227                        self.expr_bool(t, out);
228                        out.push_str(" : ");
229                        self.expr_bool(f, out);
230                    }
231                }
232            }
233            _ => self.expr(e, 1, out),
234        }
235    }
236
237    // ---------------- statements ----------------
238
239    fn line(&mut self, s: &str) {
240        for _ in 0..self.indent {
241            self.out.push_str("    ");
242        }
243        self.out.push_str(s);
244        self.out.push('\n');
245    }
246
247    /// Render an int-typed expression in a byte/short target: constants
248    /// and ternary branches get the narrowing cast (javac's conditional
249    /// expression only narrows when the target type is known — a return
250    /// or local-decl of byte/short with int constants needs explicit
251    /// casts: `byte normalVirtual = !if() ? (byte) 5 : (byte) 9;`).
252    pub fn expr_narrow(&mut self, e: &Expr, ty: &crate::types::JavaType, out: &mut String) {
253        self.expr_narrow_f(e, ty, false, out)
254    }
255
256    /// `force_consts`: the enclosing conditional's condition is not a
257    /// compile-time constant, so the whole expression is not a constant
258    /// expression and javac's assignment-context narrowing does NOT apply
259    /// to in-range int constants either (byte getDirectionality: `return
260    /// (ch & 0xFFFE) == 0xFFFE ? -1 : 0` — 从int转换到byte可能会有损失;
261    /// the source branches are byte static finals the bytecode inlined).
262    fn expr_narrow_f(
263        &mut self,
264        e: &Expr,
265        ty: &crate::types::JavaType,
266        force_consts: bool,
267        out: &mut String,
268    ) {
269        match e {
270            Expr::Const(ConstVal::Int(n)) => {
271                let (lo, hi) = match ty {
272                    crate::types::JavaType::Byte => (-128i64, 127i64),
273                    crate::types::JavaType::Short => (-32768i64, 32767i64),
274                    _ => (i32::MIN as i64, i32::MAX as i64),
275                };
276                if force_consts || (*n as i64) < lo || (*n as i64) > hi {
277                    out.push('(');
278                    out.push_str(&self.type_name(&TypeRef::J(ty.clone())));
279                    out.push_str(") ");
280                }
281                out.push_str(&n.to_string());
282            }
283            Expr::Cond { c, t, f } => {
284                let force = force_consts || !matches!(&**c, Expr::Const(_));
285                self.expr(c, 3, out);
286                out.push_str(" ? ");
287                let prev = std::mem::replace(&mut self.in_cond, true);
288                self.expr_narrow_f(t, ty, force, out);
289                out.push_str(" : ");
290                self.expr_narrow_f(f, ty, force, out);
291                self.in_cond = prev;
292            }
293            // An int-typed local (typically a branch-merge stack temp)
294            // under a byte/short target needs the explicit narrowing cast
295            // (jdk17 InvokerBytecodeGenerator `return stack61;` —
296            // 从int转换到byte可能会有损失; the merged branches carried
297            // byte values the evidence widened to int).
298            Expr::Local { var, .. } => {
299                if matches!(
300                    (self.vt.var(*var).ty.erased(), ty),
301                    (crate::types::JavaType::Int, crate::types::JavaType::Byte)
302                        | (crate::types::JavaType::Int, crate::types::JavaType::Short)
303                ) {
304                    out.push('(');
305                    out.push_str(&self.type_name(&TypeRef::J(ty.clone())));
306                    out.push_str(") ");
307                }
308                self.expr(e, 1, out);
309            }
310            _ => self.expr(e, 1, out),
311        }
312    }
313
314    /// Call-argument variant of expr_narrow: invocation conversion never
315    /// narrows, so in-range int constants keep the explicit cast too, and
316    /// int conditionals get it per branch.
317    fn expr_narrow_arg(&mut self, e: &Expr, ty: &crate::types::JavaType, out: &mut String) {
318        match e {
319            Expr::Const(ConstVal::Int(n)) => {
320                out.push('(');
321                out.push_str(&self.type_name(&TypeRef::J(ty.clone())));
322                out.push_str(") ");
323                out.push_str(&n.to_string());
324            }
325            Expr::Cond { c, t, f } => {
326                self.expr(c, 3, out);
327                out.push_str(" ? ");
328                let prev = std::mem::replace(&mut self.in_cond, true);
329                self.expr_narrow_arg(t, ty, out);
330                out.push_str(" : ");
331                self.expr_narrow_arg(f, ty, out);
332                self.in_cond = prev;
333            }
334            _ => self.expr(e, 1, out),
335        }
336    }
337
338    /// Render an expression in a char-typed target: int constants become
339    /// char literals, ternary branches recurse (the lossy-conversion fix
340    /// for `char name2type(..) { return c ? 63 : 105; }`).
341    pub fn expr_char(&mut self, e: &Expr, out: &mut String) {
342        match e {
343            Expr::Const(ConstVal::Int(n)) => push_char_lit(out, *n),
344            Expr::Cond { c, t, f } => {
345                self.expr(c, 3, out);
346                out.push_str(" ? ");
347                let prev = std::mem::replace(&mut self.in_cond, true);
348                self.expr_char(t, out);
349                out.push_str(" : ");
350                self.expr_char(f, out);
351                self.in_cond = prev;
352            }
353            _ => self.expr(e, 1, out),
354        }
355    }
356
357    fn stmt(&mut self, s: &Stmt) {
358        match s {
359            Stmt::Block(v) => {
360                for x in v {
361                    self.stmt(x);
362                }
363            }
364            Stmt::ExprStmt(e) => {
365                let mut line = String::new();
366                if matches!(e, Expr::Method { .. }) {
367                    self.suppress_poly_cast = true;
368                }
369                self.expr(e, 0, &mut line);
370                self.suppress_poly_cast = false;
371                line.push(';');
372                self.line(&line);
373            }
374            Stmt::LocalDef { var, init, is_final, force_type } => {
375                let _ = force_type;
376                let info = self.vt.var(*var);
377                let mut line = String::new();
378                if *is_final {
379                    line.push_str("final ");
380                }
381                line.push_str(&self.type_name(&info.ty));
382                line.push(' ');
383                line.push_str(&info.name);
384                if let Some(e) = init {
385                    line.push_str(" = ");
386                    // A lambda initializing a generically-typed SAM local:
387                    // the declared type is the only record of the
388                    // instantiated SAM return (the indy's
389                    // instantiatedMethodType is erased — IntFunction<T[]>
390                    // records (I)[Object), so prime the lambda-body cast
391                    // from it (jdk11 ForEachOps: `IntFunction<T[]>
392                    // generator = size -> (T[]) new Object[size]` —
393                    // without the cast "lambda 表达式中的返回类型错误").
394                    if let (TypeRef::G(g @ crate::types::GenericType::Class(_)), Expr::Lambda(l)) =
395                        (&info.ty, e)
396                    {
397                        if self.lambda_sam_ret.is_none() {
398                            self.lambda_sam_ret = self.sam_ret_cast(g, &l.sam_name);
399                        }
400                    }
401                    // Assigning a concrete type to a type-variable local
402                    // needs the (erased-away) cast back in source form.
403                    let need_cast = matches!(
404                        &info.ty,
405                        TypeRef::G(crate::types::GenericType::TypeVar(_))
406                    ) && e.type_ref() != info.ty;
407                    if need_cast {
408                        line.push('(');
409                        line.push_str(&self.type_name(&info.ty));
410                        line.push_str(") ");
411                        self.expr(e, 14, &mut line);
412                    } else if info.ty.erased() == crate::types::JavaType::Boolean {
413                        self.expr_bool(e, &mut line);
414                    } else if info.ty.erased() == crate::types::JavaType::Char {
415                        self.expr_char(e, &mut line);
416                    } else if info.ty.erased() == crate::types::JavaType::Byte
417                        || info.ty.erased() == crate::types::JavaType::Short
418                    {
419                        let t = info.ty.erased();
420                        self.expr_narrow(e, &t, &mut line);
421                    } else {
422                        self.expr(e, 1, &mut line);
423                    }
424                }
425                line.push(';');
426                self.line(&line);
427            }
428            Stmt::Return(Some(e)) => {
429                let mut line = String::from("return ");
430                if self.ret_bool {
431                    self.expr_bool(e, &mut line);
432                } else if self.ret_char {
433                    self.expr_char(e, &mut line);
434                } else if self.ret_byte {
435                    self.expr_narrow(e, &crate::types::JavaType::Byte, &mut line);
436                } else if self.ret_short {
437                    self.expr_narrow(e, &crate::types::JavaType::Short, &mut line);
438                } else {
439                    // Lambda in return position of a generic method: the
440                    // SAM return witnesses the erased body value
441                    // (castingIdentity `return i -> (R) i`, toArray
442                    // `() -> (T[]) new Object[]{..}` — jdk17 Collectors).
443                    if let (Some(TypeRef::G(g)), Expr::Lambda(l)) = (&self.ret_sam, e) {
444                        self.lambda_sam_ret = match g {
445                            crate::types::GenericType::Class(_) => self.sam_ret_cast(g, &l.sam_name),
446                            crate::types::GenericType::Array(_) => Some(TypeRef::G(g.clone())),
447                            _ => None,
448                        };
449                    }
450                    self.expr(e, 1, &mut line);
451                    self.lambda_sam_ret = None;
452                }
453                line.push(';');
454                self.line(&line);
455            }
456            Stmt::Return(None) => self.line("return;"),
457            Stmt::Throw(e) => {
458                let mut line = String::from("throw ");
459                self.expr(e, 1, &mut line);
460                line.push(';');
461                self.line(&line);
462            }
463            Stmt::If { cond, then_stmt, else_stmt } => {
464                self.print_if(cond, then_stmt, else_stmt.as_deref(), "");
465            }
466            Stmt::While { cond, body } => {
467                let mut head = String::from("while (");
468                if matches!(cond, Expr::Const(ConstVal::Int(1))) {
469                    head.push_str("true");
470                } else {
471                    self.expr_bool(cond, &mut head);
472                }
473                head.push(')');
474                self.block_stmt(&head, body);
475            }
476            Stmt::DoWhile { body, cond } => {
477                self.line("do {");
478                self.indent += 1;
479                self.stmt(body);
480                self.indent -= 1;
481                let mut tail = String::from("} while (");
482                self.expr_bool(cond, &mut tail);
483                tail.push_str(");");
484                self.line(&tail);
485            }
486            Stmt::For { init, cond, update, body } => {
487                let mut head = String::from("for (");
488                for (i, s) in init.iter().enumerate() {
489                    if i > 0 {
490                        head.push_str(", ");
491                    }
492                    self.stmt_inline(s, &mut head);
493                }
494                head.push_str("; ");
495                if let Some(c) = cond {
496                    self.expr_bool(c, &mut head);
497                }
498                head.push_str("; ");
499                for (i, u) in update.iter().enumerate() {
500                    if i > 0 {
501                        head.push_str(", ");
502                    }
503                    self.expr(u, 1, &mut head);
504                }
505                head.push(')');
506                self.block_stmt(&head, body);
507            }
508            Stmt::ForEach { var, iterable, is_array, body } => {
509                let _ = is_array;
510                let info = self.vt.var(*var);
511                let mut head = String::from("for (");
512                head.push_str(&self.type_name(&info.ty));
513                head.push(' ');
514                head.push_str(&info.name);
515                head.push_str(" : ");
516                self.expr(iterable, 1, &mut head);
517                head.push(')');
518                self.block_stmt(&head, body);
519            }
520            Stmt::Switch { selector, cases, default, on_string } => {
521                let _ = on_string;
522                let mut head = String::from("switch (");
523                self.expr(selector, 1, &mut head);
524                head.push(')');
525                self.line(&format!("{} {{", head));
526                self.indent += 1;
527                for c in cases {
528                    // MULTIPLE pattern labels in one arm cannot share a
529                    // colon body: neither stacked (`case P1 x:` `case P2
530                    // y:` — 贯穿到模式非法) nor comma-joined (`case P1 x,
531                    // P2 y:` — javac still demands a break between pattern
532                    // labels) compile. Duplicate the body per label — the
533                    // source's arrow-form multi-pattern arm
534                    // (`case A _, B _ -> r;`, jdk26 ParserVerifier
535                    // valueSize) in colon form.
536                    if c.raw_labels.len() > 1
537                        && c.labels.is_empty()
538                        && c.string_labels.is_empty()
539                        && c.enum_labels.is_empty()
540                    {
541                        for l in &c.raw_labels {
542                            match &c.guard {
543                                Some(g) => {
544                                    let mut gtxt = String::new();
545                                    self.expr(g, 1, &mut gtxt);
546                                    self.line(&format!("case {} when {}:", l, gtxt));
547                                }
548                                None => self.line(&format!("case {}:", l)),
549                            }
550                            self.line("{");
551                            self.indent += 1;
552                            for st in &c.body {
553                                self.stmt(st);
554                            }
555                            self.indent -= 1;
556                            self.line("}");
557                        }
558                        continue;
559                    }
560                    for l in &c.enum_labels {
561                        self.line(&format!("case {}:", l));
562                    }
563                    if let Some(g) = &c.guard {
564                        // Folded pattern guard: the desugared restart shape
565                        // (`if (guard) body else { state=N; continue; }`)
566                        // restored to `case T v when guard:` (jdk26
567                        // DecimalFormat — duplicate type-pattern labels are
568                        // "此 case 标签由前一个 case 标签支配" without it).
569                        let mut gtxt = String::new();
570                        self.expr(g, 1, &mut gtxt);
571                        let joined = c
572                            .raw_labels
573                            .iter()
574                            .map(|l| format!("{} when {}", l, gtxt))
575                            .collect::<Vec<_>>()
576                            .join(", ");
577                        self.line(&format!("case {}:", joined));
578                    } else if !c.raw_labels.is_empty() {
579                        // Pattern labels of one arm MUST be comma-joined:
580                        // stacked `case P1 x:` / `case P2 y:` lines are a
581                        // fall-through between patterns ("从模式贯穿非法",
582                        // jdk26 ParserVerifier `case OfConstant _,
583                        // OfClass _ -> 2`).
584                        self.line(&format!("case {}:", c.raw_labels.join(", ")));
585                    }
586                    for l in &c.labels {
587                        self.line(&format!("case {}:", l));
588                    }
589                    for l in &c.string_labels {
590                        self.line(&format!("case \"{}\":", escape_string(l)));
591                    }
592                    // Braces give each case its own scope: pattern-switch
593                    // restorations reuse binding names across cases.
594                    self.line("{");
595                    self.indent += 1;
596                    for st in &c.body {
597                        self.stmt(st);
598                    }
599                    self.indent -= 1;
600                    self.line("}");
601                }
602                if let Some(d) = default {
603                    self.line("default:");
604                    self.indent += 1;
605                    self.stmt(d);
606                    self.indent -= 1;
607                }
608                self.indent -= 1;
609                self.line("}");
610            }
611            Stmt::Try { body, catches, finally } => {
612                self.line("try {");
613                self.indent += 1;
614                self.stmt(body);
615                self.indent -= 1;
616                for c in catches {
617                    let exc_name = if c.exc.is_empty() {
618                        "Throwable".to_string()
619                    } else {
620                        c.exc.iter().map(|e| self.shorten(e)).collect::<Vec<_>>().join(" | ")
621                    };
622                    let var_name = if c.var == u32::MAX {
623                        "ignored".to_string()
624                    } else {
625                        c.var_name.clone().unwrap_or_else(|| self.vt.var(c.var).name.clone())
626                    };
627                    self.line(&format!("}} catch ({} {}) {{", exc_name, var_name));
628                    self.indent += 1;
629                    self.stmt(&c.body);
630                    self.indent -= 1;
631                }
632                if let Some(f) = finally {
633                    self.line("} finally {");
634                    self.indent += 1;
635                    self.stmt(f);
636                    self.indent -= 1;
637                }
638                self.line("}");
639            }
640            Stmt::TryWithResources { resources, body, catches, finally } => {
641                let mut res: Vec<String> = Vec::new();
642                for r in resources {
643                    if let Stmt::LocalDef { var, init, .. } = r {
644                        let info = self.vt.var(*var);
645                        let mut t = String::new();
646                        t.push_str(&self.type_name(&info.ty));
647                        t.push(' ');
648                        t.push_str(&info.name);
649                        if let Some(e) = init {
650                            t.push_str(" = ");
651                            self.expr(e, 1, &mut t);
652                        }
653                        res.push(t);
654                    } else {
655                        // The resource is not a declaration: a reused
656                        // variable was rewritten to a plain assignment by
657                        // dedupe_declarations, and `try (x = expr)` is not
658                        // Java. Print it as the statement it is, BEFORE the
659                        // try (resource init runs before the body anyway) —
660                        // emitting nothing turned the header into the
661                        // illegal `try () {`.
662                        self.stmt(r);
663                    }
664                }
665                if res.is_empty() && catches.is_empty() && finally.is_none() {
666                    // Vestigial try-with-resources: the resource was
667                    // hoisted into a plain assignment above and its close
668                    // is a sibling statement, so nothing is left to head
669                    // the statement — a bare `try {` does not compile
670                    // ("'try' without 'catch', 'finally' or resource
671                    // declarations"). A handler-less try IS a block.
672                    self.line("{");
673                } else if res.is_empty() {
674                    self.line("try {");
675                } else {
676                    self.line(&format!("try ({}) {{", res.join("; ")));
677                }
678                self.indent += 1;
679                self.stmt(body);
680                self.indent -= 1;
681                for c in catches {
682                    let exc_name = if c.exc.is_empty() {
683                        "Throwable".to_string()
684                    } else {
685                        c.exc.iter().map(|e| self.shorten(e)).collect::<Vec<_>>().join(" | ")
686                    };
687                    let var_name = if c.var == u32::MAX {
688                        "ignored".to_string()
689                    } else {
690                        c.var_name.clone().unwrap_or_else(|| self.vt.var(c.var).name.clone())
691                    };
692                    self.line(&format!("}} catch ({} {}) {{", exc_name, var_name));
693                    self.indent += 1;
694                    self.stmt(&c.body);
695                    self.indent -= 1;
696                }
697                if let Some(f) = finally {
698                    self.line("} finally {");
699                    self.indent += 1;
700                    self.stmt(f);
701                    self.indent -= 1;
702                }
703                self.line("}");
704            }
705            Stmt::Assert { cond, msg } => {
706                let mut line = String::from("assert ");
707                // The assert-condition is boolean-typed: the synthesized
708                // `assert false` carries Const Int(0) — plain expr would
709                // print `assert 0` (int无法转换为boolean, jdk17 URI).
710                self.expr_bool(cond, &mut line);
711                if let Some(m) = msg {
712                    line.push_str(" : ");
713                    self.expr(m, 1, &mut line);
714                }
715                line.push(';');
716                self.line(&line);
717            }
718            Stmt::Synchronized { lock, body } => {
719                let mut head = String::from("synchronized (");
720                self.expr(lock, 1, &mut head);
721                head.push(')');
722                self.block_stmt(&head, body);
723            }
724            Stmt::TernaryValue { e } => {
725                let mut line = String::from("/* ternary */ ");
726                self.expr(e, 1, &mut line);
727                line.push(';');
728                self.line(&line);
729            }
730            Stmt::MonitorEnter(e) => {
731                let mut line = String::from("/* monitorenter */ ");
732                self.expr(e, 1, &mut line);
733                line.push(';');
734                self.line(&line);
735            }
736            Stmt::MonitorExit(e) => {
737                let mut line = String::from("/* monitorexit */ ");
738                self.expr(e, 1, &mut line);
739                line.push(';');
740                self.line(&line);
741            }
742            Stmt::ClassDecl { header, body, .. } => {
743                let kw = if header.starts_with("record ")
744                    || header.starts_with("interface ")
745                    || header.starts_with("enum ")
746                {
747                    String::new()
748                } else {
749                    "class ".to_string()
750                };
751                self.line(&format!("{}{} {{", kw, header));
752                for line in body.lines() {
753                    if line.trim().is_empty() {
754                        self.out.push('\n');
755                    } else {
756                        for _ in 0..self.indent + 1 {
757                            self.out.push_str("    ");
758                        }
759                        self.out.push_str(line.trim_start());
760                        self.out.push('\n');
761                    }
762                }
763                self.line("}");
764            }
765            Stmt::Labeled { label, body } => {
766                // label prefix on the following statement's line
767                let save = self.out.len();
768                self.stmt(body);
769                // prepend "label: " before the first line just emitted
770                let indent_str = "    ".repeat(self.indent);
771                let emitted = self.out[save..].to_string();
772                self.out.truncate(save);
773                if let Some(rest) = emitted.strip_prefix(&indent_str) {
774                    self.out.push_str(&indent_str);
775                    self.out.push_str(label);
776                    self.out.push_str(": ");
777                    self.out.push_str(rest);
778                } else {
779                    self.out.push_str(&emitted);
780                }
781            }
782            Stmt::Break(lbl) => match lbl {
783                Some(l) => self.line(&format!("break {};", l)),
784                None => self.line("break;"),
785            },
786            Stmt::Continue(lbl) => match lbl {
787                Some(l) => self.line(&format!("continue {};", l)),
788                None => self.line("continue;"),
789            },
790            Stmt::Label(id) => {
791                // Emitted by breaking the following statement; standalone
792                // labels are invalid before non-statements, so use a marker.
793                self.line(&format!("L{}: ;", id));
794            }
795            Stmt::Goto(id) => self.line(&format!("break L{};", id)),
796            Stmt::Comment(c) => self.line(&format!("// {}", c)),
797        }
798    }
799
800    /// `head {` + body + `}`.
801    fn block_stmt(&mut self, head: &str, body: &Stmt) {
802        self.line(&format!("{} {{", head));
803        self.indent += 1;
804        self.stmt(body);
805        self.indent -= 1;
806        self.line("}");
807    }
808
809    /// Print an if statement; `opener` is "" for the first if and
810    /// "} else " when continuing an else-if chain on the closing line.
811    fn print_if(&mut self, cond: &Expr, then_stmt: &Stmt, else_stmt: Option<&Stmt>, opener: &str) {
812        let mut head = String::from(opener);
813        head.push_str("if (");
814        self.expr_bool(cond, &mut head);
815        head.push_str(") {");
816        self.line(&head);
817        self.indent += 1;
818        self.stmt(then_stmt);
819        self.indent -= 1;
820        match else_stmt {
821            Some(Stmt::If { cond: c2, then_stmt: t2, else_stmt: e2 }) => {
822                self.print_if(c2, t2, e2.as_deref(), "} else ");
823            }
824            Some(other) => {
825                self.line("} else {");
826                self.indent += 1;
827                self.stmt(other);
828                self.indent -= 1;
829                self.line("}");
830            }
831            None => self.line("}"),
832        }
833    }
834
835    fn stmt_inline(&self, s: &Stmt, out: &mut String) {
836        match s {
837            Stmt::ExprStmt(e) => {
838                let mut p = self.sub();
839                if matches!(e, Expr::Method { .. }) {
840                    p.suppress_poly_cast = true;
841                }
842                p.expr(e, 1, out);
843            }
844            Stmt::LocalDef { var, init, .. } => {
845                let info = self.vt.var(*var);
846                out.push_str(&self.type_name(&info.ty));
847                out.push(' ');
848                out.push_str(&info.name);
849                if let Some(e) = init {
850                    out.push_str(" = ");
851                    let mut p = self.sub();
852                    p.expr(e, 1, out);
853                }
854            }
855            other => {
856                let mut p = self.sub();
857                p.stmt(other);
858                out.push_str(p.out.trim_end());
859            }
860        }
861    }
862
863    fn sub(&self) -> Printer<'a> {
864        Printer {
865            ctx: self.ctx,
866            vt: self.vt,
867            out: String::new(),
868            indent: 0,
869            lambda_depth: self.lambda_depth,
870            outer_names: self.outer_names.clone(),
871            ret_bool: self.ret_bool,
872            ret_char: self.ret_char,
873            ret_byte: self.ret_byte,
874            ret_short: self.ret_short,
875            suppress_poly_cast: self.suppress_poly_cast,
876            suppress_diamond: self.suppress_diamond,
877            in_cond: self.in_cond,
878            lambda_sam_ret: self.lambda_sam_ret.clone(),
879            ret_sam: self.ret_sam.clone(),
880        }
881    }
882
883    // ---------------- expressions ----------------
884
885    pub fn expr(&mut self, e: &Expr, outer_prec: u8, out: &mut String) {
886        let parens = e.needs_parens(outer_prec, false);
887        if parens {
888            out.push('(');
889        }
890        match e {
891            Expr::Const(c) => self.const_val(c, out),
892            Expr::Raw(t) if t == "\u{3}" => out.push_str("this"),
893            Expr::Raw(t) => out.push_str(t),
894            Expr::RawT(t, _) => out.push_str(t),
895            Expr::Local { var, .. } => {
896                let info = self.vt.var(*var);
897                out.push_str(&info.name);
898            }
899            Expr::This => out.push_str("this"),
900            Expr::New { cls, args, ty, .. } => {
901                let no_diamond = self.suppress_diamond;
902                self.suppress_diamond = false;
903                // Explicit type arguments pinned by the AST passes
904                // (diamond_explicit_args_from_call_args): print them
905                // instead of the diamond.
906                let explicit = match ty {
907                    TypeRef::G(crate::types::GenericType::Class(cs))
908                        if cs
909                            .parts
910                            .last()
911                            .map(|p| !p.args.is_empty())
912                            .unwrap_or(false)
913                            && crate::typeutil::classsig_internal(cs) == *cls =>
914                    {
915                        let rendered: Vec<String> = cs
916                            .parts
917                            .last()
918                            .unwrap()
919                            .args
920                            .iter()
921                            .map(|a| self.generic_name(a))
922                            .collect();
923                        Some(format!("<{}>", rendered.join(", ")))
924                    }
925                    _ => None,
926                };
927                // A wildcard-typed ctor argument dooms diamond inference
928                // (the inference variable gets an equality constraint from
929                // the capture and a different bound from the context —
930                // "cannot infer type arguments for Entry<>", jdk11
931                // Hashtable `tab[i] = new Entry<>(hash, key, value, e)`
932                // with e: Entry<?,?>; the source declared e as Entry<K,V>
933                // via an unchecked cast that leaves no bytecode trace).
934                // The raw form is always applicable (unchecked).
935                let args_wildcard = args.iter().any(|a| {
936                    matches!(a.type_ref(), TypeRef::G(ref g)
937                        if crate::typeutil::g_has_wildcard(g))
938                });
939                let diamond: std::borrow::Cow<str> = match &explicit {
940                    Some(x) => x.as_str().into(),
941                    // Pre-Java-8 conditional branches are not poly
942                    // expressions: the diamond cannot see the assignment
943                    // target through the `?:` and infers <Object,Object>
944                    // (jdk7 HashSet.readObject map-ternary 不兼容的类型).
945                    // Raw is unchecked but always compilable.
946                    None if no_diamond || args_wildcard
947                        || (self.in_cond && self.ctx.source_level() < 52) =>
948                    {
949                        "".into()
950                    }
951                    None => self.diamond_for(cls).into(),
952                };
953                let diamond = diamond.as_ref();
954                if let Some(local) = cls.strip_prefix('\u{2}') {
955                    out.push_str("new ");
956                    out.push_str(local);
957                    out.push_str(diamond);
958                    out.push('(');
959                    // Local records/classes keep bytecode-arity ctors:
960                    // typed rendering turns an int constant into the
961                    // char/boolean literal the ctor expects (jdk17
962                    // MessageFormat `new Qchar(39, quoted)`).
963                    // Nested local-class method printing runs under a
964                    // TAKEN LOCAL_CLASS_INTERNALS registry — resolve a
965                    // self-construction (`new BitSetSpliterator(..)` inside
966                    // BitSetSpliterator.trySplit) through the printer's
967                    // own class when the registry misses. Local-class
968                    // ctors capture the enclosing instance as a leading
969                    // param that the printed new-site omits; the this$0
970                    // fallback in ctor_param_types lines the formals up
971                    // (jdk11 BitSet$1BitSetSpliterator(BitSet,int,int,int,
972                    // boolean) — the boolean formal rendered its arg `0`).
973                    let internal = self.ctx.local_class_internal(local).or_else(|| {
974                        let tail =
975                            self.ctx.class_name().rsplit('$').next().unwrap_or("");
976                        let simple =
977                            tail.trim_start_matches(|c: char| c.is_ascii_digit());
978                        if !simple.is_empty() && simple == local {
979                            return Some(self.ctx.class_name().to_string());
980                        }
981                        // Probe the nested-name space of the printing
982                        // context (javac numbers method-local classes
983                        // `$1Name`): the new-site needs the real class to
984                        // resolve its ctor formals.
985                        let base = format!("{}${}", self.ctx.class_name(), local);
986                        if self.ctx.has_class(&base) {
987                            return Some(base);
988                        }
989                        for d in 0..10 {
990                            let cand = format!("{}${}{}", self.ctx.class_name(), d, local);
991                            if self.ctx.has_class(&cand) {
992                                return Some(cand);
993                            }
994                        }
995                        None
996                    });
997                    match internal
998                        .as_deref()
999                        .and_then(|i| self.ctor_param_types(i, 0, args.len(), args))
1000                    {
1001                        Some(pt) => self.args_typed(args, &pt, out),
1002                        None => self.args(args, out),
1003                    }
1004                    out.push(')');
1005                } else if self.is_member_inner(cls)
1006                    && !args.is_empty()
1007                    && !matches!(args[0], Expr::This)
1008                {
1009                    // `outerExpr.new Inner(rest...)` — the first ctor arg
1010                    // is the synthetic outer instance when the class has a
1011                    // this$0 field, or when the ctor forwards it straight
1012                    // to super (no field of its own; is_member_inner
1013                    // detected it). Either way the qualified-new form
1014                    // supplies the outer implicitly, so skip args[0].
1015                    self.expr(&args[0], 15, out);
1016                    out.push_str(".new ");
1017                    out.push_str(&inner_simple(cls));
1018                    out.push_str(diamond);
1019                    out.push('(');
1020                    match self.ctor_param_types(cls, 1, args.len() - 1, &args[1..]) {
1021                        Some(pt) => self.args_typed(&args[1..], &pt, out),
1022                        None => self.args(&args[1..], out),
1023                    }
1024                    out.push(')');
1025                } else {
1026                    out.push_str("new ");
1027                    let member_this = self.is_member_inner(cls)
1028                        && !args.is_empty()
1029                        && matches!(args[0], Expr::This);
1030                    let shown = if member_this {
1031                        // `new Inner()` from inside the outer class
1032                        inner_simple(cls)
1033                    } else {
1034                        self.shorten(cls)
1035                    };
1036                    out.push_str(&shown);
1037                    out.push_str(diamond);
1038                    out.push('(');
1039                    if member_this {
1040                        match self.ctor_param_types(cls, 1, args.len() - 1, &args[1..]) {
1041                            Some(pt) => self.args_typed(&args[1..], &pt, out),
1042                            None => self.args(&args[1..], out),
1043                        }
1044                    } else {
1045                        // Pinned generic instantiation: prime lambda args
1046                        // from the ctor's instantiated Signature formals.
1047                        let sam_rets: Vec<Option<TypeRef>> = match ty {
1048                            TypeRef::G(crate::types::GenericType::Class(cs))
1049                                if explicit.is_some()
1050                                    && crate::typeutil::classsig_internal(cs) == *cls =>
1051                            {
1052                                let pinned = cs.parts.last().map(|p| p.args.clone());
1053                                let formals = self.ctx.ctor_formals_by_arity(cls, args.len());
1054                                // The raw ctor formals carry the class's
1055                                // own typevars — substitute the pinned
1056                                // instantiation before reading the SAM
1057                                // return (an unsubstituted R printed `(R)`
1058                                // — 找不到符号).
1059                                let class_params = self.ctx.class_type_params(cls);
1060                                args.iter()
1061                                    .enumerate()
1062                                    .map(|(i, a)| {
1063                                        if !matches!(a, Expr::Lambda(_)) {
1064                                            return None;
1065                                        }
1066                                        let f = formals.as_ref()?.get(i)?;
1067                                        let pinned = pinned.as_ref()?;
1068                                        if pinned.len() != class_params.len() {
1069                                            return None;
1070                                        }
1071                                        let inst = crate::typeutil::subst_typevars(
1072                                            f,
1073                                            &class_params,
1074                                            pinned,
1075                                        );
1076                                        let Expr::Lambda(l) = a else { return None };
1077                                        self.sam_ret_cast(&inst, &l.sam_name)
1078                                    })
1079                                    .collect()
1080                            }
1081                            _ => Vec::new(),
1082                        };
1083                        match self.ctor_param_types(cls, 0, args.len(), args) {
1084                            Some(pt) => self.args_typed_sam(args, &pt, &sam_rets, out),
1085                            None => self.args(args, out),
1086                        }
1087                    }
1088                    out.push(')');
1089                }
1090            }
1091            Expr::NewArray { elem, dims, trailing_dims, init } => {
1092                out.push_str("new ");
1093                out.push_str(&self.type_name(elem));
1094                match init {
1095                    Some(vals) => {
1096                        for _ in 0..=*trailing_dims {
1097                            out.push_str("[]");
1098                        }
1099                        out.push_str(" {");
1100                        // boolean[]/char[] initializers hold int constants
1101                        // in bytecode (iconst + bastore/castore): render
1102                        // them as true/false and char literals (jdk11
1103                        // PKCS9Attribute `new boolean[] {0, 0, 1, ..}` x18).
1104                        let elem_bool = elem.erased() == crate::types::JavaType::Boolean;
1105                        let elem_char = elem.erased() == crate::types::JavaType::Char;
1106                        for (i, v) in vals.iter().enumerate() {
1107                            if i > 0 {
1108                                out.push_str(", ");
1109                            }
1110                            if elem_bool {
1111                                self.expr_bool(v, out);
1112                            } else if elem_char {
1113                                self.expr_char(v, out);
1114                            } else {
1115                                self.expr(v, 1, out);
1116                            }
1117                        }
1118                        out.push('}');
1119                    }
1120                    None => {
1121                        for d in dims {
1122                            out.push('[');
1123                            self.expr(d, 1, out);
1124                            out.push(']');
1125                        }
1126                        for _ in 0..*trailing_dims {
1127                            out.push_str("[]");
1128                        }
1129                    }
1130                }
1131            }
1132            Expr::NewMultiArray { ty, dims } => {
1133                // Strip the dims.len() leading array levels from the type.
1134                let mut base = ty.erased();
1135                for _ in 0..dims.len() {
1136                    match base {
1137                        crate::types::JavaType::Array(inner) => base = *inner,
1138                        _ => break,
1139                    }
1140                }
1141                out.push_str("new ");
1142                out.push_str(&self.type_name(&TypeRef::J(base)));
1143                for d in dims {
1144                    out.push('[');
1145                    self.expr(d, 1, out);
1146                    out.push(']');
1147                }
1148            }
1149            Expr::Field { owner, cls, name, is_static, .. } => {
1150                // javac reserves `$assertionsDisabled`; the declaration and
1151                // all references are emitted under a private alias.
1152                let name: &str = if name == "$assertionsDisabled" {
1153                    crate::analysis::ASSERT_FIELD
1154                } else {
1155                    name
1156                };
1157                if name == crate::analysis::ASSERT_FIELD {
1158                    // Assert guards print bare: the field is declared on
1159                    // the enclosing emitted class or inlined body; the
1160                    // bytecode owner is often a synthetic holder class
1161                    // (`ConstantGroup$1`) whose shorten() fallback is
1162                    // `Object` — a qualified read can never resolve.
1163                    out.push_str(name);
1164                } else if name == "length" && cls.is_empty() {
1165                    if let Some(o) = owner {
1166                        self.expr(o, 15, out);
1167                    }
1168                    out.push_str(".length");
1169                } else if let Some(o) = owner {
1170                    if matches!(o.as_ref(), Expr::Raw(t) if t == "\u{3}") {
1171                        // Outer anonymous class member: unqualified lexical
1172                        // resolution (an anon outer has no nameable this).
1173                        out.push_str(name);
1174                    } else if self.private_super_field(cls, name, o.as_ref()) {
1175                        // A private field of a SUPERCLASS is not inherited
1176                        // into the subclass's member scope: `this.algorithm`
1177                        // inside Delegate (extends MessageDigest, algorithm
1178                        // private) is "private access" even though the
1179                        // source comment says it all — jdk11
1180                        // MessageDigest.Delegate.clone casts
1181                        // `((MessageDigest)this).algorithm`.
1182                        out.push_str("((");
1183                        out.push_str(&self.shorten(cls));
1184                        out.push_str(") ");
1185                        if matches!(o.as_ref(), Expr::This) {
1186                            out.push_str("this");
1187                        } else {
1188                            self.expr(o, 14, out);
1189                        }
1190                        out.push_str(").");
1191                        out.push_str(name);
1192                    } else {
1193                        self.expr(o, 15, out);
1194                        out.push('.');
1195                        out.push_str(name);
1196                    }
1197                } else if *is_static {
1198                    // A same-class static read prints bare — unless a local
1199                    // of this method shadows the field name: javac resolves
1200                    // the bare name as the LOCAL (jdk17 Socket.setImpl's
1201                    // `SocketImplFactory factory = factory;` self-reference
1202                    // — 可能尚未初始化变量factory x2 sites). Qualify with the
1203                    // class name to reach the field.
1204                    let shadowed_by_local = cls == &self.ctx.class_name()
1205                        && self.vt.vars.iter().any(|v| v.name == name);
1206                    // ENCLOSING-class static fields read from a nested
1207                    // class print bare too (lexical scope) — and MUST when
1208                    // the class name itself is shadowed by a member of the
1209                    // same name: jdk26 HPKE$Impl reads the outer
1210                    // `byte[] HPKE`/PSK_ID_HASH/SECRET/EXP/KEY constants;
1211                    // `HPKE.PSK_ID_HASH` resolves the qualifier as the
1212                    // byte[] FIELD (variables obscure type names in
1213                    // expression names) — 找不到符号 变量 PSK_ID_HASH x6.
1214                    let enclosing_static = cls != &self.ctx.class_name()
1215                        && self.ctx.class_name().starts_with(&format!("{}$", cls))
1216                        && !self.vt.vars.iter().any(|v| v.name == name)
1217                        && {
1218                            let mut cur = self.ctx.class_name().to_string();
1219                            let mut shadow = false;
1220                            while cur != *cls {
1221                                // Inherited fields shadow too (superclass
1222                                // chain — see the method-side twin).
1223                                let mut sup = Some(cur.clone());
1224                                while let Some(c) = sup.take() {
1225                                    {
1226                                        if self.ctx.declares_field(&c, name) {
1227                                            shadow = true;
1228                                            break;
1229                                        }
1230                                        sup = self.ctx.super_name(&c);
1231                                    }
1232                                }
1233                                if shadow {
1234                                    break;
1235                                }
1236                                match cur.rfind('$') {
1237                                    Some(i) => cur.truncate(i),
1238                                    None => break,
1239                                }
1240                            }
1241                            !shadow
1242                        };
1243                    if !enclosing_static
1244                        && (cls != &self.ctx.class_name() || shadowed_by_local)
1245                    {
1246                        out.push_str(&self.shorten(cls));
1247                        out.push('.');
1248                    }
1249                    out.push_str(name);
1250                } else if self.private_super_field(
1251                    cls,
1252                    name,
1253                    &Expr::This,
1254                ) {
1255                    out.push_str("((");
1256                    out.push_str(&self.shorten(cls));
1257                    out.push_str(") this).");
1258                    out.push_str(name);
1259                } else {
1260                    out.push_str("this.");
1261                    out.push_str(name);
1262                }
1263            }
1264            Expr::Method { owner, cls, name, desc, args, is_static, is_special, is_super, type_args, .. } => {
1265                // Signature-polymorphic calls need the descriptor return cast
1266                // in source form (see classdec::polymorphic_ret_cast) — except
1267                // at the root of an expression statement, where the bare call
1268                // is the only legal form. Consume the flag so a poly call
1269                // NESTED in the args still gets its cast.
1270                let bare_stmt_root = self.suppress_poly_cast;
1271                self.suppress_poly_cast = false;
1272                if !bare_stmt_root {
1273                    if let Some(t) = self.ctx.polymorphic_ret_cast(cls, name, desc) {
1274                        out.push('(');
1275                        out.push_str(&self.type_name(&crate::ir::expr::TypeRef::J(t)));
1276                        out.push_str(") ");
1277                    }
1278                }
1279                if name == "<init>" && *is_special {
1280                    // super(...) / this(...)
1281                    let is_super_form = *is_super || cls != &self.ctx.class_name();
1282                    // Qualified super for a STATIC class extending an
1283                    // INNER superclass: javac synthesizes the enclosing
1284                    // instance as the ctor's first param, and the source
1285                    // form is `outer.super(rest)` (jdk11/17/26
1286                    // BoundMethodHandle.SpeciesData extends
1287                    // ClassSpecializer<..>.SpeciesData — plain
1288                    // super(outer, key) is "需要包含..的封闭实例" x3 trees).
1289                    let qualified_outer = if is_super_form
1290                        && !args.is_empty()
1291                        && cls.contains('$')
1292                        && !self.ctx.class_has_this0(self.ctx.class_name())
1293                    {
1294                        let enclosing = cls.rsplit_once('$').map(|(o, _)| o.to_string());
1295                        match enclosing {
1296                            Some(enc)
1297                                if self.ctx.class_has_this0(cls)
1298                                    && self.ctx.is_subtype_of(&args[0].type_ref().erased(), &enc) =>
1299                            {
1300                                Some(enc)
1301                            }
1302                            _ => None,
1303                        }
1304                    } else {
1305                        None
1306                    };
1307                    if qualified_outer.is_some() {
1308                        self.expr(&args[0], 15, out);
1309                        out.push_str(".super(");
1310                    } else if is_super_form {
1311                        out.push_str("super(");
1312                    } else {
1313                        out.push_str("this(");
1314                    }
1315                    // Typed rendering: boolean/char params must not receive
1316                    // raw 0/1 int constants (`this(refKind, false)`). The
1317                    // methodref's descriptor `desc.args` is the EXACT invoked
1318                    // ctor signature, so prefer it over `ctor_param_types`
1319                    // (which matches by arg count and picks the wrong
1320                    // overload when a class has several ctors of equal arity,
1321                    // e.g. jdk26 Thread(ThreadGroup,Runnable,String,long,
1322                    // boolean) vs (...,long,Thread[])).
1323                    let _ = is_super_form;
1324                    // Upcast restoration only for SELF delegation: this(..)
1325                    // overload resolution binds the arg's static type (a
1326                    // JarEntry arg would recurse into JarEntry(JarEntry)
1327                    // instead of the descriptor's JarEntry(ZipEntry)).
1328                    // super(..) resolution is unambiguous through the
1329                    // generic formals — casting there broke FindOps
1330                    // `super(parent, spliterator)` (K := FindTask<..>
1331                    // accepts the arg fine; the raw erasure cast does not).
1332                    if qualified_outer.is_some() {
1333                        if desc.args.len() == args.len() && desc.args.len() >= 1 {
1334                            self.args_typed(&args[1..], &desc.args[1..], out);
1335                        } else {
1336                            self.args(&args[1..], out);
1337                        }
1338                    } else if desc.args.len() == args.len() {
1339                        if is_super_form {
1340                            self.args_typed(args, &desc.args, out);
1341                        } else {
1342                            self.args_delegation(args, &desc.args, out);
1343                        }
1344                    } else if !is_super_form {
1345                        // desc is the EXACT invoked ctor signature and the
1346                        // args are outer-stripped: args_delegation aligns
1347                        // by tail (jdk11 ZipFileInflaterInputStream —
1348                        // ctor_param_types' arity match would pick the
1349                        // wrong same-arity overload and cast against
1350                        // shifted formals, (ZipFile) zfin x3).
1351                        self.args_delegation(args, &desc.args, out);
1352                    } else {
1353                        match self.ctor_param_types(cls, 0, args.len(), args) {
1354                            Some(pt) if pt.len() == args.len() => {
1355                                self.args_typed(args, &pt, out)
1356                            }
1357                            _ => self.args(args, out),
1358                        }
1359                    }
1360                    out.push(')');
1361                } else {
1362                    if *is_super {
1363                        // invokespecial on `this` targeting another class:
1364                        // a superclass method call. Interface targets are
1365                        // qualified `Iface.super.m()` (JDK8 default-method
1366                        // invocations from implementors).
1367                        let itf = self.ctx.is_interface(cls);
1368                        if itf {
1369                            out.push_str(&self.shorten(cls));
1370                            out.push('.');
1371                        }
1372                        out.push_str("super.");
1373                    } else if let Some(o) = owner {
1374                        if let Expr::Lambda(l) = o.as_ref() {
1375                            // A lambda RECEIVER needs the source's SAM cast
1376                            // (`((BooleanSupplier) () -> ..).getAsBoolean()`
1377                            // — a bare lambda cannot own a call: 此处不应为
1378                            // lambda 表达式, jdk26 Proxy assert).
1379                            if !l.sam_cls.is_empty() {
1380                                out.push_str("((");
1381                                out.push_str(&self.shorten(&l.sam_cls));
1382                                out.push_str(") ");
1383                                self.expr(o, 14, out);
1384                                out.push_str(").");
1385                            } else {
1386                                self.expr(o, 15, out);
1387                                out.push('.');
1388                            }
1389                        } else if matches!(o.as_ref(), Expr::Raw(t) if t == "\u{3}") {
1390                            // Outer anonymous member call: unqualified
1391                            // lexical resolution.
1392                        } else if self.needs_owner_cast(cls, name, desc, o) {
1393                            // Private / cross-package members are NOT
1394                            // inherited by the receiver's static type:
1395                            // the source cast to the declaring class
1396                            // (`((ZipFile) jar).getManifestNum()`) leaves
1397                            // no bytecode trace when the receiver is
1398                            // already a subtype.
1399                            out.push_str("((");
1400                            out.push_str(&self.shorten(cls));
1401                            out.push_str(") ");
1402                            self.expr(o, 14, out);
1403                            out.push_str(").");
1404                        } else {
1405                            self.expr(o, 15, out);
1406                            out.push('.');
1407                        }
1408                    } else if *is_static && (cls != &self.ctx.class_name() || !type_args.is_empty())
1409                    {
1410                        // A static member of an ENCLOSING class resolves
1411                        // through lexical scope unqualified — and the
1412                        // qualified form is a TRAP when a field/local is
1413                        // named after the class: expression-name
1414                        // resolution prefers variables, so jdk26
1415                        // HPKE$Impl's `private static final byte[] HPKE`
1416                        // hijacked `HPKE.usePSK(psk)` into a member
1417                        // lookup on the array (找不到符号 方法
1418                        // usePSK(SecretKey) 位置: 类型为byte[]的变量
1419                        // HPKE). Keep the qualifier when an own or
1420                        // intermediate class declares a same-named
1421                        // method (real member shadowing) or when type
1422                        // witnesses need an explicit receiver.
1423                        let enclosing_static = type_args.is_empty()
1424                            && self.ctx.class_name().starts_with(&format!("{}$", cls))
1425                            && {
1426                                let mut cur = self.ctx.class_name().to_string();
1427                                let mut shadow = false;
1428                                while cur != *cls {
1429                                    // Inherited members shadow too: the
1430                                    // lexical member scope of a class
1431                                    // includes its whole superclass chain
1432                                    // (jdk26 GaloisCounterMode$GCMDecrypt
1433                                    // extends GCMEngine, whose own
1434                                    // implGCMCrypt(ByteBuffer,..) hijacks
1435                                    // the bare 9-arg call to the OUTER
1436                                    // static — 无法将方法应用到给定类型).
1437                                    let mut sup = Some(cur.clone());
1438                                    while let Some(c) = sup.take() {
1439                                        {
1440                                            if self.ctx.declares_method_named(&c, name) {
1441                                                shadow = true;
1442                                                break;
1443                                            }
1444                                            sup = self.ctx.super_name(&c);
1445                                        }
1446                                    }
1447                                    if shadow {
1448                                        break;
1449                                    }
1450                                    match cur.rfind('$') {
1451                                        Some(i) => cur.truncate(i),
1452                                        None => break,
1453                                    }
1454                                }
1455                                !shadow
1456                            };
1457                        if !enclosing_static {
1458                            out.push_str(&self.shorten(cls));
1459                            out.push('.');
1460                        }
1461                    } else if !type_args.is_empty() {
1462                        // Type witnesses require an explicit receiver.
1463                        out.push_str("this.");
1464                    }
1465                    if !type_args.is_empty() {
1466                        out.push('<');
1467                        out.push_str(&type_args.join(", "));
1468                        out.push('>');
1469                    }
1470                    out.push_str(name);
1471                    out.push('(');
1472                    // Lambda args of a GENERIC call: prime each one's
1473                    // body-return cast from the instantiated formal's SAM
1474                    // return (jdk17 AbstractPipeline.opEvaluateParallelLazy:
1475                    // `i -> (E_OUT[]) new Object[i]` — the erasure-equal
1476                    // cast leaves no bytecode trace and without it the
1477                    // IntFunction<E_OUT[]> formal starves: Object[]无法
1478                    // 转换为E_OUT[]).
1479                    let sam_rets: Vec<Option<TypeRef>> = {
1480                        let mut v: Vec<Option<TypeRef>> =
1481                            std::iter::repeat_with(|| None).take(args.len()).collect();
1482                        if self.ctx.is_generic_call(e) {
1483                            if let Some((formals, mtvars)) = self.ctx.generic_call_formals(e) {
1484                                if formals.len() == args.len() {
1485                                    for (i, (a, f)) in
1486                                        args.iter().zip(formals.iter()).enumerate()
1487                                    {
1488                                        // A raw-SAM-cast wrapper still
1489                                        // exposes the lambda (the overload
1490                                        // disambiguation cast; the primed
1491                                        // body return types the lambda
1492                                        // precisely).
1493                                        let a_inner = match a {
1494                                            Expr::Cast { e: ce, .. } => ce.as_ref(),
1495                                            other => other,
1496                                        };
1497                                        if let Expr::Lambda(l) = a_inner {
1498                                            // A formal still carrying the
1499                                            // callee's own method typevars
1500                                            // is not denotable at this
1501                                            // call site.
1502                                            if crate::typeutil::g_mentions_any(f, &mtvars) {
1503                                                continue;
1504                                            }
1505                                            v[i] = self.sam_ret_cast(f, &l.sam_name);
1506                                        }
1507                                    }
1508                                }
1509                            }
1510                        }
1511                        v
1512                    };
1513                    if sam_rets.iter().any(|x| x.is_some()) {
1514                        self.args_typed_sam(args, &desc.args, &sam_rets, out);
1515                    } else {
1516                        self.args_typed(args, &desc.args, out);
1517                    }
1518                    out.push(')');
1519                }
1520            }
1521            Expr::ArrayIndex { array, index } => {
1522                self.expr(array, 15, out);
1523                out.push('[');
1524                // Array indexes are int by definition: a long-typed index
1525                // local (slot-sharing/merge typed the foreach lowering's
1526                // counter long — jdk26 Files.copy `options[i]`,
1527                // 从long转换到int可能会有损失) needs the narrowing cast.
1528                let idx_ty = match &**index {
1529                    Expr::Local { var, .. } => self.vt.var(*var).ty.erased(),
1530                    other => other.type_ref().erased(),
1531                };
1532                if matches!(idx_ty, crate::types::JavaType::Long) {
1533                    out.push_str("(int) ");
1534                }
1535                self.expr(index, 1, out);
1536                out.push(']');
1537            }
1538            Expr::Cast { ty, e } => {
1539                // `(T) (Serializable) lambda` is invalid Java: the source
1540                // form of a serializable-lambda cast is the intersection
1541                // `(T & Serializable) lambda`.
1542                if let Expr::Cast { ty: inner_ty, e: inner_e } = &**e {
1543                    let is_serializable = |t: &crate::ir::expr::TypeRef| {
1544                        t.erased() == crate::types::JavaType::Object("java/io/Serializable".into())
1545                    };
1546                    if matches!(&**inner_e, Expr::Lambda(_)) && is_serializable(inner_ty) {
1547                        out.push('(');
1548                        out.push_str(&self.type_name(ty));
1549                        out.push_str(" & ");
1550                        out.push_str(&self.type_name(inner_ty));
1551                        out.push_str(") ");
1552                        self.expr(inner_e, 14, out);
1553                        return;
1554                    }
1555                }
1556                // CLASS-argument generic casts only: a TYPEVAR cast is
1557                // load-bearing (`(T) new SimpleEntry<>(..)` — jdk11
1558                // IdentityHashMap.toArray: the diamond infers from the
1559                // ctor args and the unchecked (T) makes the T[] store
1560                // legal; dropping it left the diamond to infer from the
1561                // array target T — 无法推断SimpleEntry<>的类型参数).
1562                if let (TypeRef::G(crate::types::GenericType::Class(_)), Expr::New { cls, .. }) =
1563                    (ty, &**e)
1564                {
1565                    if !self.diamond_for(cls).is_empty() {
1566                        // Synthetic generics-only cast around a diamond new:
1567                        // DROP the cast and keep the diamond. A generic-typed
1568                        // cast is always jcdc's own witness (a real checkcast
1569                        // carries only erasure), and every cast form loses
1570                        // here: with the diamond the cast starves inference
1571                        // (`(PendingFuture<Void,A>) new PendingFuture<>(..)`
1572                        // infers Object bounds and fails the cast), and the
1573                        // bare/raw form makes the whole creation raw so
1574                        // method-ref/lambda arguments stop type-checking
1575                        // (jdk17 Collectors `(Collector<T,?,C>) new
1576                        // CollectorImpl(.., Collection::add, ..)`). Cast-free
1577                        // `return new CollectorImpl<>(..)` / `x = new
1578                        // PendingFuture<>(..)` target-types the diamond —
1579                        // the source shape.
1580                        self.expr(e, outer_prec, out);
1581                        return;
1582                    }
1583                }
1584                out.push('(');
1585                out.push_str(&self.type_name(ty));
1586                out.push_str(") ");
1587                // Inconvertible direct casts from mis-joined stack-merge
1588                // vars (jdk26 AnnotationReader: `(SupertypeTarget)
1589                // var3_341` with var3 typed Iterator — sealed targets
1590                // reject the cross-cast): relay through Object, which is
1591                // always legal and semantically identical (the runtime
1592                // value IS the target type).
1593                if self.needs_object_relay(ty, e) {
1594                    out.push_str("(Object) ");
1595                }
1596                if matches!(&**e, Expr::New { .. }) {
1597                    self.suppress_diamond = true;
1598                }
1599                if let (TypeRef::G(g), Expr::Lambda(l)) = (ty, &**e) {
1600                    self.lambda_sam_ret = self.sam_ret_cast(g, &l.sam_name);
1601                }
1602                self.expr(e, 14, out);
1603                self.suppress_diamond = false;
1604                self.lambda_sam_ret = None;
1605            }
1606            Expr::InstanceOf { e, ty } => {
1607                self.expr(e, 10, out);
1608                out.push_str(" instanceof ");
1609                out.push_str(&self.type_name(ty));
1610            }
1611            Expr::Un { op, e } => {
1612                out.push_str(match op {
1613                    UnOp::Neg => "-",
1614                    UnOp::Not => "!",
1615                    UnOp::BitNot => "~",
1616                });
1617                self.expr(e, 14, out);
1618            }
1619            Expr::Bin { op, l, r, .. } => {
1620                // Boolean context special cases: `b == 0` → `!b`. A
1621                // Local's embedded type can be stale (booleanize retypes
1622                // the VarTable), and a boolified bitwise bin over
1623                // bool-ish operands is boolean too (jdk26
1624                // LinkedTransferQueue `(spin & !upc) == 0` — the frozen-Int
1625                // And with an int-form ternary side printed
1626                // `spin & (!upc ? 1 : 0)`: boolean & int).
1627                let lt = match &**l {
1628                    Expr::Local { var, .. } => self.vt.var(*var).ty.erased(),
1629                    other => other.type_ref().erased(),
1630                };
1631                let l_bool = lt == JavaType::Boolean;
1632                let l_boolish = l_bool || self.bool_ish_vt(l);
1633                if matches!(op, BinOp::Eq | BinOp::Ne | BinOp::RefEq | BinOp::RefNe)
1634                    && l_boolish
1635                {
1636                    if let Expr::Const(ConstVal::Int(n)) = &**r {
1637                        let polarity = matches!(op, BinOp::Eq | BinOp::RefEq);
1638                        let want_true = (*n != 0) == polarity;
1639                        if !want_true {
1640                            out.push('!');
1641                        }
1642                        if l_bool {
1643                            self.expr(l, 14, out);
1644                        } else {
1645                            // Composite boolean (a bitwise bin): parenthesize
1646                            // so a leading `!` binds the whole value.
1647                            out.push('(');
1648                            self.expr_bool(l, out);
1649                            out.push(')');
1650                        }
1651                        if parens {
1652                            out.push(')');
1653                        }
1654                        return;
1655                    }
1656                }
1657                // Mixed boolean/int equality on non-constants (stack-merge
1658                // vars of an assert desugaring: jdk26 VirtualThread
1659                // `stack38 == stack39` with stack38 boolean, stack39 int
1660                // 0/1 — "boolean和int不可比较"): normalize the int side
1661                // through `!= 0`.
1662                {
1663                    let rt = r.type_ref().erased();
1664                    if matches!(op, BinOp::Eq | BinOp::Ne)
1665                        && ((lt == crate::types::JavaType::Boolean
1666                            && rt == crate::types::JavaType::Int)
1667                            || (lt == crate::types::JavaType::Int
1668                                && rt == crate::types::JavaType::Boolean))
1669                    {
1670                        for (i, side) in [l, r].iter().enumerate() {
1671                            if i > 0 {
1672                                out.push(' ');
1673                                out.push_str(op.symbol());
1674                                out.push(' ');
1675                            }
1676                            if side.type_ref().erased() == crate::types::JavaType::Boolean {
1677                                self.expr_bool(side, out);
1678                            } else {
1679                                out.push('(');
1680                                self.expr(side, 1, out);
1681                                out.push_str(" != 0)");
1682                            }
1683                        }
1684                        if parens {
1685                            out.push(')');
1686                        }
1687                        return;
1688                    }
1689                }
1690                let p = op.precedence();
1691                self.expr(l, p, out);
1692                out.push(' ');
1693                out.push_str(op.symbol());
1694                out.push(' ');
1695                self.expr(r, p + 1, out);
1696            }
1697            Expr::Cond { c, t, f } => {
1698                self.expr(c, 3, out);
1699                out.push_str(" ? ");
1700                let prev = std::mem::replace(&mut self.in_cond, true);
1701                self.expr(t, 2, out);
1702                out.push_str(" : ");
1703                self.expr(f, 2, out);
1704                self.in_cond = prev;
1705            }
1706            Expr::Assign { target, op, value } => {
1707                // The local's VarTable type is authoritative: booleanize
1708                // flips vt types after the embedded Local tys were frozen
1709                // (Security `boolean var4_122 = 1` printed the int form —
1710                // "int无法转换为boolean").
1711                let tgt_er = match &**target {
1712                    Expr::Local { var, .. } => self.vt.var(*var).ty.erased(),
1713                    other => other.type_ref().erased(),
1714                };
1715                self.expr(target, 1, out);
1716                out.push(' ');
1717                out.push_str(op.symbol());
1718                out.push(' ');
1719                if matches!(op, crate::ir::expr::AssignOp::Plain) {
1720                    // Narrow-target assignments render int constants in the
1721                    // target's form (jdk11/17/26 xml Parser: `mESt = ch !=
1722                    // 116 ? 512 : 60` against `private char mESt` —
1723                    // "从int转换到char可能会有损失").
1724                    match tgt_er {
1725                        crate::types::JavaType::Boolean => self.expr_bool(value, out),
1726                        crate::types::JavaType::Char => self.expr_char(value, out),
1727                        crate::types::JavaType::Byte => {
1728                            self.expr_narrow(value, &crate::types::JavaType::Byte, out)
1729                        }
1730                        crate::types::JavaType::Short => {
1731                            self.expr_narrow(value, &crate::types::JavaType::Short, out)
1732                        }
1733                        _ => self.expr(value, 1, out),
1734                    }
1735                } else {
1736                    self.expr(value, 1, out);
1737                }
1738            }
1739            Expr::PreIncDec { e, delta, .. } => {
1740                out.push_str(if *delta > 0 { "++" } else { "--" });
1741                self.expr(e, 14, out);
1742            }
1743            Expr::PostIncDec { e, delta, .. } => {
1744                self.expr(e, 14, out);
1745                out.push_str(if *delta > 0 { "++" } else { "--" });
1746            }
1747            Expr::Lambda(l) => self.lambda(l, out),
1748            Expr::AnonNew { base, args, body, .. } => {
1749                out.push_str("new ");
1750                let base_s = self.type_name(base);
1751                out.push_str(&base_s);
1752                out.push('(');
1753                // Render the ctor args through the base class's matching
1754                // <init> descriptor: an int 0/1 at a boolean parameter
1755                // must print as false/true (jdk11 Console's anonymous
1756                // PrintWriter subclass — `new PrintWriter(out, 1)` finds
1757                // no ctor). Pick the arity-matching ctor whose param
1758                // erasures best fit the actual arg types.
1759                let base_internal = match base {
1760                    TypeRef::J(crate::types::JavaType::Object(n)) => Some(n.clone()),
1761                    TypeRef::G(crate::types::GenericType::Class(cs)) => {
1762                        Some(crate::typeutil::classsig_internal(cs))
1763                    }
1764                    _ => None,
1765                };
1766                // Overload choice for the ctor args (int 0/1 at a boolean
1767                // formal must print false/true) reads the base class's own
1768                // `<init>` set: front-end metadata.
1769                let ctor_params = base_internal
1770                    .and_then(|bi| self.ctx.ctor_param_types(bi.as_str(), 0, args.len(), args));
1771                match ctor_params {
1772                    Some(pt) => self.args_typed(args, &pt, out),
1773                    None => self.args(args, out),
1774                }
1775                out.push_str(") {\n");
1776                for line in body.lines() {
1777                    for _ in 0..self.indent + 1 {
1778                        out.push_str("    ");
1779                    }
1780                    out.push_str(line.trim_start_matches(' '));
1781                    out.push('\n');
1782                }
1783                for _ in 0..self.indent {
1784                    out.push_str("    ");
1785                }
1786                out.push('}');
1787            }
1788            Expr::StringConcat(parts) => {
1789                let mut first = true;
1790                // Java semantics: the concatenation must start with a String.
1791                let needs_prefix = !matches!(parts.first(), Some(ConcatPart::Const(_)));
1792                if needs_prefix {
1793                    out.push_str("\"\"");
1794                    first = false;
1795                }
1796                for p in parts {
1797                    // Right-hand operands of `+` need parens at equal
1798                    // precedence to keep arithmetic grouping (`"t" + (x+1)`).
1799                    let is_first = first;
1800                    if !first {
1801                        out.push_str(" + ");
1802                    }
1803                    first = false;
1804                    match p {
1805                        ConcatPart::Const(s) => {
1806                            out.push('"');
1807                            out.push_str(&escape_string(s));
1808                            out.push('"');
1809                        }
1810                        ConcatPart::Str(e) => self.expr(e, if is_first { 12 } else { 13 }, out),
1811                    }
1812                }
1813                if parts.is_empty() {
1814                    out.push_str("\"\"");
1815                }
1816            }
1817            Expr::Invokedynamic { name, args, bsm_text, .. } => {
1818                if name.starts_with('\u{0}') {
1819                    out.push_str("/*bad-cmp*/0");
1820                } else {
1821                    out.push_str("/* invokedynamic ");
1822                    out.push_str(name);
1823                    out.push(' ');
1824                    out.push_str(bsm_text);
1825                    out.push_str(" */ (");
1826                    self.args(args, out);
1827                    out.push(')');
1828                }
1829            }
1830        }
1831        if parens {
1832            out.push(')');
1833        }
1834    }
1835
1836    fn args(&mut self, args: &[Expr], out: &mut String) {
1837        for (i, a) in args.iter().enumerate() {
1838            if i > 0 {
1839                out.push_str(", ");
1840            }
1841            self.expr(a, 1, out);
1842        }
1843    }
1844
1845    /// Print call args applying boolean-parameter constant adjustment.
1846    /// Delegation (this(..)/super(..)) args: typed rendering PLUS the
1847    /// elided-upcast restoration when the arg's static type differs from
1848    /// the descriptor formal — overload resolution binds the arg's type,
1849    /// not the descriptor (jdk17 JarEntry copy ctor: `this(je)` resolves
1850    /// recursively to JarEntry(JarEntry); the source `this((ZipEntry) je)`
1851    /// upcast is elided from bytecode because je <: ZipEntry is provable).
1852    fn args_delegation(
1853        &mut self,
1854        args: &[Expr],
1855        param_types: &[crate::types::JavaType],
1856        out: &mut String,
1857    ) {
1858        // Inner-class ctor descriptors carry the synthetic outer param
1859        // while the expression args are outer-stripped: align the formals
1860        // with the TRAIL of the args, like apply_ctor_param_casts (jdk11
1861        // ZipFileInflaterInputStream this(zfin, res, res.getInflater(),
1862        // size) was cast against the shifted descriptor formals —
1863        // (ZipFile) zfin, (ZipFileInputStream) res... x3 inconvertible).
1864        let off = param_types.len().saturating_sub(args.len());
1865        for (i, a) in args.iter().enumerate() {
1866            if i > 0 {
1867                out.push_str(", ");
1868            }
1869            match param_types.get(off + i) {
1870                Some(pt @ crate::types::JavaType::Object(_)) => {
1871                    let have = a.type_ref().erased();
1872                    // Generically-typed args (typevars/parameterized) are
1873                    // already source-typed: casting them to the descriptor
1874                    // erasure breaks convertibility to typevar formals
1875                    // (jdk17 StreamSpliterators SliceSpliterator.OfPrimitive
1876                    // this((Spliterator.OfPrimitive) s, ..) against a
1877                    // T_SPLITR formal — OfPrimitive无法转换为T_SPLITR).
1878                    if &have != pt
1879                        && !matches!(a.type_ref(), TypeRef::G(_))
1880                        && !matches!(
1881                            a,
1882                            Expr::Cast { .. }
1883                                | Expr::Const(_)
1884                                | Expr::Lambda(_)
1885                                | Expr::New { .. }
1886                                | Expr::AnonNew { .. }
1887                        )
1888                    {
1889                        out.push('(');
1890                        out.push_str(&self.type_name(&TypeRef::J(pt.clone())));
1891                        out.push_str(") ");
1892                        self.expr(a, 14, out);
1893                        continue;
1894                    }
1895                    self.expr(a, 1, out);
1896                }
1897                Some(crate::types::JavaType::Boolean) => self.expr_bool(a, out),
1898                Some(crate::types::JavaType::Char) => self.expr_char(a, out),
1899                Some(pt @ (crate::types::JavaType::Byte | crate::types::JavaType::Short)) => {
1900                    self.expr_narrow_arg(a, pt, out)
1901                }
1902                _ => self.expr(a, 1, out),
1903            }
1904        }
1905    }
1906
1907    fn args_typed(&mut self, args: &[Expr], param_types: &[crate::types::JavaType], out: &mut String) {
1908        self.args_typed_sam(args, param_types, &[], out)
1909    }
1910
1911    /// args_typed with per-argument SAM-return priming: a lambda actual at
1912    /// a parameterized-SAM formal of a pinned generic ctor gets its body
1913    /// return cast from the formal's instantiated SAM return (jdk11/17/26
1914    /// Collectors.toUnmodifiable*'s finisher `list -> (List<T>)
1915    /// List.of(list.toArray())` — the erased instantiatedMethodType says
1916    /// only List, so the generic cast is recoverable solely from the
1917    /// diamond's resolved formal).
1918    fn args_typed_sam(
1919        &mut self,
1920        args: &[Expr],
1921        param_types: &[crate::types::JavaType],
1922        sam_rets: &[Option<TypeRef>],
1923        out: &mut String,
1924    ) {
1925        for (i, a) in args.iter().enumerate() {
1926            if i > 0 {
1927                out.push_str(", ");
1928            }
1929            match param_types.get(i) {
1930                Some(crate::types::JavaType::Boolean) => self.expr_bool(a, out),
1931                // Char/narrow params: expr_char/expr_narrow render both
1932                // constants AND conditional branches in the target form
1933                // (jdk17 BasicAuthentication `super(!isProxy ? 115 : 112,
1934                // ..)` — the char param needs `!isProxy ? 's' : 'p'`;
1935                // a plain int ternary is "条件表达式中的类型错误").
1936                Some(crate::types::JavaType::Char) => self.expr_char(a, out),
1937                // Byte/short parameters: an int literal argument needs an
1938                // explicit cast (invocation conversion never narrows, even
1939                // for in-range constants) — and so do the branches of an
1940                // int conditional (jdk CompressedResourceHeader
1941                // `buffer.put(isTerminal ? (byte) 1 : (byte) 0)` — the
1942                // byte casts fold away in bytecode; a bare int ternary is
1943                // "对于put(int), 找不到合适的方法" x3 trees).
1944                Some(crate::types::JavaType::Byte) | Some(crate::types::JavaType::Short) => {
1945                    self.expr_narrow_arg(a, &param_types[i].clone(), out);
1946                }
1947                _ => {
1948                    let lam_under_cast = matches!(a, Expr::Lambda(_))
1949                        || matches!(a, Expr::Cast { e: ce, .. } if matches!(&**ce, Expr::Lambda(_)));
1950                    match sam_rets.get(i).and_then(|x| x.as_ref()) {
1951                        Some(t) if lam_under_cast => {
1952                            let prev = self.lambda_sam_ret.replace(t.clone());
1953                            self.expr(a, 1, out);
1954                            self.lambda_sam_ret = prev;
1955                        }
1956                        _ => self.expr(a, 1, out),
1957                    }
1958                }
1959            }
1960        }
1961    }
1962
1963    fn const_val(&self, c: &ConstVal, out: &mut String) {
1964        match c {
1965            ConstVal::Int(i) => out.push_str(&i.to_string()),
1966            ConstVal::Long(l) => {
1967                if *l == i64::MIN {
1968                    out.push_str("-9223372036854775808L");
1969                } else {
1970                    out.push_str(&format!("{}L", l));
1971                }
1972            }
1973            ConstVal::Float(f) => out.push_str(&format_float(*f as f64, true)),
1974            ConstVal::Double(d) => out.push_str(&format_float(*d, false)),
1975            ConstVal::Str(s) => {
1976                out.push('"');
1977                out.push_str(&escape_string(s));
1978                out.push('"');
1979            }
1980            ConstVal::Null => out.push_str("null"),
1981            ConstVal::ClassLit(t) => {
1982                out.push_str(&self.type_name(t));
1983                out.push_str(".class");
1984            }
1985        }
1986    }
1987
1988    fn lambda(&mut self, l: &crate::ir::expr::LambdaExpr, out: &mut String) {
1989        // A `lambda$...` implementation method in the current class is a
1990        // compiler-generated lambda body, never a real method reference —
1991        // emit the body inline even when classified as a reference (the
1992        // method itself is hidden from the output).
1993        let synthetic_self_lambda =
1994            l.impl_name.starts_with("lambda$") && l.impl_owner == self.ctx.class_name();
1995        match l.kind {
1996            LambdaKind::MethodRef if !synthetic_self_lambda => {
1997                // Determine receiver form.
1998                if l.impl_name == "<init>" {
1999                    out.push_str(&self.shorten(&l.impl_owner));
2000                    out.push_str("::new");
2001                    return;
2002                }
2003                if let Some(recv) = &l.ref_receiver {
2004                    out.push_str(&self.shorten(recv));
2005                    out.push_str("::");
2006                    out.push_str(&l.impl_name);
2007                } else if !l.captures.is_empty() {
2008                    // bound receiver: first capture is the instance
2009                    // A method-ref/lambda receiver needs its functional
2010                    // type spelled out: `A::new::get` parses as a
2011                    // QUALIFIED reference and is rejected — the source form
2012                    // is `((Supplier<A>) A::new)::get`.
2013                    let cap = &l.captures[0];
2014                    let func_ty = match cap {
2015                        Expr::Lambda(l2) => Some(l2.sam_cls.clone()),
2016                        Expr::Cast { ty, e } if matches!(**e, Expr::Lambda(_)) => Some(ty.erased().to_descriptor()),
2017                        _ => None,
2018                    };
2019                    if let Some(f) = func_ty {
2020                        out.push_str("((");
2021                        out.push_str(&self.shorten(&f));
2022                        out.push_str(") ");
2023                        self.expr(cap, 1, out);
2024                        out.push(')');
2025                    } else {
2026                        self.expr(cap, 15, out);
2027                    }
2028                    out.push_str("::");
2029                    out.push_str(&l.impl_name);
2030                } else {
2031                    out.push_str(&self.shorten(&l.impl_owner));
2032                    out.push_str("::");
2033                    out.push_str(&l.impl_name);
2034                }
2035            }
2036            _ => {
2037                if self.lambda_depth >= 8 {
2038                    out.push_str("/*nested-lambda*/null");
2039                    return;
2040                }
2041                // The nested-body preparation — decompiling the impl
2042                // method and running the front-end's idiom recovery
2043                // (anonymous inlining, capture snapshots, enum switches,
2044                // witness casts, outer-this substitution, label pruning,
2045                // ...) — belongs to the front-end: that is where
2046                // machine-specific desugaring lives. Ask it for a finished
2047                // statement tree and print that.
2048                // The nested-body preparation — decompiling the impl method
2049                // and running the front-end's idiom recovery (anonymous
2050                // inlining, capture snapshots, enum switches, witness casts,
2051                // outer-this substitution, label pruning, ...) — belongs to
2052                // the front-end: that is where machine-specific desugaring
2053                // lives. Ask it for a finished statement tree and print that.
2054                let nested = self.ctx.nested_method(l, self.vt);
2055                // params: prefer the impl method's own parameter names so the
2056                // printed parameter list matches the body's references
2057                // (instance lambda impls carry only the SAM params).
2058                let mut pnames = l.param_names.clone();
2059                if let Some(MethodBody { vt, .. }) = &nested {
2060                    let mut vt_params: Vec<String> = vt
2061                        .vars
2062                        .iter()
2063                        .filter(|v| v.is_param && v.name != "this")
2064                        .map(|v| v.name.clone())
2065                        .collect();
2066                    if vt_params.len() == pnames.len() {
2067                        pnames = vt_params;
2068                    } else if vt_params.len() > pnames.len() {
2069                        // The impl method's signature is (captures..., SAM
2070                        // params...): take the TRAILING SAM slice so the
2071                        // printed parameter list matches the body's variable
2072                        // references (ConcurrentMap.replaceAll printed
2073                        // `(x0, x1) -> { .. replace(k, v, ..) }` — undefined
2074                        // symbols; the captured `function` occupied the first
2075                        // impl slot, breaking the exact-length alignment).
2076                        let n = pnames.len();
2077                        pnames = vt_params.split_off(vt_params.len() - n);
2078                    }
2079                }
2080                let single = pnames.len() == 1;
2081                if !single {
2082                    out.push('(');
2083                }
2084                for (i, n) in pnames.iter().enumerate() {
2085                    if i > 0 {
2086                        out.push_str(", ");
2087                    }
2088                    out.push_str(n);
2089                }
2090                if !single {
2091                    out.push(')');
2092                }
2093                out.push_str(" -> ");
2094                let sam_wrap = self.lambda_sam_ret.take();
2095                match nested {
2096                    Some(MethodBody { mut body, mut vt, .. }) => {
2097                        // Single-return body → expression lambda.
2098                        let mut single_expr = match &body {
2099                            Stmt::Return(Some(e)) => Some(e.clone()),
2100                            Stmt::Block(v) if v.len() == 1 => match &v[0] {
2101                                Stmt::Return(Some(e)) => Some(e.clone()),
2102                                _ => None,
2103                            },
2104                            _ => None,
2105                        };
2106                        // Witness the instantiated SAM return on the body's
2107                        // returns: the impl method is erased, so `i -> i`
2108                        // under `(Function<I,R>)` fails inference without
2109                        // `(R) i`.
2110                        fn sam_ok(t: &TypeRef, e: &Expr) -> bool {
2111                            !matches!(e, Expr::Cast { .. } | Expr::Const(_))
2112                                && e.type_ref() != *t
2113                        }
2114                        fn wrap_returns(s: &mut Stmt, t: &TypeRef) {
2115                            match s {
2116                                Stmt::Return(Some(e)) => {
2117                                    if sam_ok(t, e) {
2118                                        let v = std::mem::replace(e, Expr::This);
2119                                        *e = Expr::Cast { ty: t.clone(), e: Box::new(v) };
2120                                    }
2121                                }
2122                                Stmt::Block(v) => v.iter_mut().for_each(|x| wrap_returns(x, t)),
2123                                Stmt::If { then_stmt, else_stmt, .. } => {
2124                                    wrap_returns(then_stmt, t);
2125                                    if let Some(x) = else_stmt {
2126                                        wrap_returns(x, t);
2127                                    }
2128                                }
2129                                Stmt::While { body, .. }
2130                                | Stmt::DoWhile { body, .. }
2131                                | Stmt::ForEach { body, .. }
2132                                | Stmt::Labeled { body, .. }
2133                                | Stmt::Synchronized { body, .. } => wrap_returns(body, t),
2134                                Stmt::For { init, body, .. } => {
2135                                    init.iter_mut().for_each(|i| wrap_returns(i, t));
2136                                    wrap_returns(body, t);
2137                                }
2138                                Stmt::Switch { cases, default, .. } => {
2139                                    for c in cases.iter_mut() {
2140                                        for st in c.body.iter_mut() {
2141                                            wrap_returns(st, t);
2142                                        }
2143                                    }
2144                                    if let Some(d) = default {
2145                                        wrap_returns(d, t);
2146                                    }
2147                                }
2148                                Stmt::Try { body, catches, finally } => {
2149                                    wrap_returns(body, t);
2150                                    for c in catches.iter_mut() {
2151                                        wrap_returns(&mut c.body, t);
2152                                    }
2153                                    if let Some(f) = finally {
2154                                        wrap_returns(f, t);
2155                                    }
2156                                }
2157                                _ => {}
2158                            }
2159                        }
2160                        if let Some(t) = &sam_wrap {
2161                            if let Some(e) = &mut single_expr {
2162                                if sam_ok(t, e) {
2163                                    let v = std::mem::replace(e, Expr::This);
2164                                    *e = Expr::Cast { ty: t.clone(), e: Box::new(v) };
2165                                } else if let TypeRef::G(_) = t {
2166                                    // The body already carries the erasure
2167                                    // checkcast: retype it to the generic
2168                                    // SAM return (jdk26
2169                                    // CopyOnWriteArrayList.toArray `i ->
2170                                    // (T[]) new Object[i]` printed
2171                                    // `(Object[])` — Object[]无法转换为T[]
2172                                    // against IntFunction<T[]>).
2173                                    if let Expr::Cast { ty: ct, e: ce } = e {
2174                                        if ct.erased() == t.erased()
2175                                            && !matches!(&**ce, Expr::Cast { .. })
2176                                        {
2177                                            *ct = t.clone();
2178                                        }
2179                                    }
2180                                }
2181                            } else {
2182                                wrap_returns(&mut body, t);
2183                            }
2184                        } else if let Some(inst) = &l.inst_sam_desc {
2185                            if crate::dbg_flag!("JCDC_DBG_LAMRET") {
2186                                eprintln!(
2187                                    "LAMRET impl={}.{} inst_ret={:?} single={}",
2188                                    l.impl_owner,
2189                                    l.impl_name,
2190                                    inst.ret,
2191                                    single_expr.is_some()
2192                                );
2193                            }
2194                            // The invokedynamic's instantiatedMethodType
2195                            // records the lambda's inferred return at the
2196                            // call site; a source upcast can be elided from
2197                            // the bytecode when provable (jdk17
2198                            // JrtFileSystem.iteratorOf: `(Path)` around
2199                            // path.resolve(..) inside map(child -> ...) —
2200                            // JrtPath <: Path needs no checkcast, but
2201                            // without the cast map infers Stream<JrtPath>
2202                            // and .iterator() fails the Iterator<Path>
2203                            // return). Restore it when the expression body
2204                            // is typed a strict subtype of the recorded
2205                            // return.
2206                            fn upcast_ok(
2207                                e: &Expr,
2208                                target: &crate::types::JavaType,
2209                                ctx: &dyn crate::ctx::Ctx,
2210                            ) -> bool {
2211                                let crate::types::JavaType::Object(t) = target else {
2212                                    return false;
2213                                };
2214                                // A parameterized (generic) value whose
2215                                // erasure IS the target reaches it unchecked
2216                                // (jdk17 CompletionStage exceptionallyCompose:
2217                                // the cond branch fn.apply(ex) types as the
2218                                // capture ? extends CompletionStage<T>; the
2219                                // erased impl return CompletionStage needs no
2220                                // cast, and printing one breaks the outer
2221                                // chain's inference).
2222                                if let TypeRef::G(g0) = e.type_ref() {
2223                                    if let crate::types::GenericType::Class(cs) = &g0 {
2224                                        if crate::typeutil::classsig_internal(cs) == *t {
2225                                            return true;
2226                                        }
2227                                    }
2228                                }
2229                                let crate::types::JavaType::Object(e0) = e.type_ref().erased() else {
2230                                    return false;
2231                                };
2232                                if &e0 == t {
2233                                    return false;
2234                                }
2235                                let mut queue = vec![e0];
2236                                let mut seen = std::collections::HashSet::new();
2237                                while let Some(cur) = queue.pop() {
2238                                    if !seen.insert(cur.clone()) {
2239                                        continue;
2240                                    }
2241                                    if !ctx.has_class(&cur) {
2242                                        continue;
2243                                    }
2244                                    for (sup, _) in ctx.class_supers_args(&cur, &[]) {
2245                                        if &sup == t {
2246                                            return true;
2247                                        }
2248                                        queue.push(sup);
2249                                    }
2250                                }
2251                                false
2252                            }
2253                            let want = TypeRef::J(inst.ret.clone());
2254                            // The sam_wrap prime (declared local / pinned
2255                            // ctor formal / generic-call formal) carries the
2256                            // GENERIC SAM return the erased
2257                            // instantiatedMethodType lost: upgrade an
2258                            // existing erasure-equal cast on the body (jdk26
2259                            // CopyOnWriteArrayList.toArray's `i -> (T[]) new
2260                            // Object[i]` printed `(Object[])` — the real
2261                            // checkcast erases T[] and Object[] alike;
2262                            // Object[]无法转换为T[] against
2263                            // IntFunction<T[]>).
2264                            let g_want: Option<TypeRef> = sam_wrap.as_ref().and_then(|t| {
2265                                if let TypeRef::G(g) = t {
2266                                    if TypeRef::G(g.clone()).erased() == inst.ret {
2267                                        return Some(t.clone());
2268                                    }
2269                                }
2270                                None
2271                            });
2272                            if let Some(e) = &mut single_expr {
2273                                if crate::dbg_flag!("JCDC_DBG_LAMRET") {
2274                                    eprintln!(
2275                                        "LAMRET2 inst_ret={:?} expr_ty={:?} sam_ok={} upcast_ok={} g_want={:?}",
2276                                        inst.ret,
2277                                        e.type_ref(),
2278                                        sam_ok(&want, e),
2279                                        upcast_ok(e, &inst.ret, self.ctx),
2280                                        g_want
2281                                    );
2282                                }
2283                                let mut upgraded = false;
2284                                if let Some(gw) = &g_want {
2285                                    if let Expr::Cast { ty, e: ce } = e {
2286                                        if ty.erased() == inst.ret
2287                                            && !matches!(&**ce, Expr::Cast { .. })
2288                                        {
2289                                            *ty = gw.clone();
2290                                            upgraded = true;
2291                                        }
2292                                    }
2293                                }
2294                                if !upgraded && sam_ok(&want, e) && upcast_ok(e, &inst.ret, self.ctx) {
2295                                    let v = std::mem::replace(e, Expr::This);
2296                                    *e = Expr::Cast { ty: want, e: Box::new(v) };
2297                                }
2298                            }
2299                        }
2300                        // A nested lambda body shares the ENCLOSING
2301                        // lambda's scope rules: its own locals must not
2302                        // redeclare names visible there (jdk26
2303                        // MethodHandleProxies.createTemplate — the clb
2304                        // lambda's foreach-desugared `i$`/`mi` collided
2305                        // with the nested cob lambda's own `i$`/`mi`,
2306                        // 已在方法中定义了变量 x2). Rename the inner
2307                        // impl's non-param locals on collision; the
2308                        // method-level twin is handled by
2309                        // disambiguate_lambda_locals.
2310                        {
2311                            let mut outer_names: std::collections::HashSet<&str> = self
2312                                .vt
2313                                .vars
2314                                .iter()
2315                                .map(|v| v.name.as_str())
2316                                .collect();
2317                            outer_names
2318                                .extend(self.outer_names.iter().map(|n| n.as_str()));
2319                            let mut used: std::collections::HashSet<String> =
2320                                vt.vars.iter().map(|v| v.name.clone()).collect();
2321                            for v in vt.vars.iter_mut() {
2322                                if v.is_param || !outer_names.contains(v.name.as_str()) {
2323                                    continue;
2324                                }
2325                                let base = v.name.clone();
2326                                let mut k = 1;
2327                                loop {
2328                                    let cand = format!("{}${}", base, k);
2329                                    if !used.contains(&cand)
2330                                        && !outer_names.contains(cand.as_str())
2331                                    {
2332                                        used.insert(cand.clone());
2333                                        v.name = cand;
2334                                        break;
2335                                    }
2336                                    k += 1;
2337                                }
2338                            }
2339                        }
2340                        let mut sub = Printer {
2341                            ctx: self.ctx,
2342                            vt: &vt,
2343                            out: String::new(),
2344                            indent: self.indent,
2345                            lambda_depth: self.lambda_depth + 1,
2346                            outer_names: {
2347                                let mut n = self.outer_names.clone();
2348                                n.extend(self.vt.vars.iter().map(|v| v.name.clone()));
2349                                n
2350                            },
2351                            // The SAM's return type governs the impl
2352                            // body's renders: boolean SAMs must print
2353                            // `return true;` not the JVM-int `return 1;`
2354                            // (jdk26 Gatherers.fold's ofGreedy lambda).
2355                            ret_bool: l.sam_desc.ret == crate::types::JavaType::Boolean,
2356                            ret_char: l.sam_desc.ret == crate::types::JavaType::Char,
2357                            ret_byte: l.sam_desc.ret == crate::types::JavaType::Byte,
2358                            ret_short: l.sam_desc.ret == crate::types::JavaType::Short,
2359                            suppress_poly_cast: false,
2360                            suppress_diamond: false,
2361                            // A lambda body is a fresh expression context:
2362                            // the enclosing conditional's pre-8 non-poly
2363                            // rule does not propagate into it (the lambda
2364                            // is a standalone method body).
2365                            in_cond: false,
2366                            lambda_sam_ret: None,
2367                            ret_sam: None,
2368                        };
2369                        if let Some(e) = single_expr {
2370                            if l.sam_desc.ret == crate::types::JavaType::Boolean {
2371                                sub.expr_bool(&e, out);
2372                            } else {
2373                                sub.expr(&e, 1, out);
2374                            }
2375                        } else {
2376                            out.push_str("{\n");
2377                            sub.indent += 1;
2378                            sub.stmt(&body);
2379                            let text = sub.out;
2380                            out.push_str(&text);
2381                            for _ in 0..self.indent {
2382                                out.push_str("    ");
2383                            }
2384                            out.push('}');
2385                        }
2386                    }
2387                    None => {
2388                        out.push_str("/* lambda body in ");
2389                        out.push_str(&l.impl_owner);
2390                        out.push('.');
2391                        out.push_str(&l.impl_name);
2392                        out.push_str(" */ {}");
2393                    }
2394                }
2395            }
2396        }
2397    }
2398
2399    // ---------------- names & types ----------------
2400
2401    /// True if `cls` is a member inner class (declares this$0) per the pool.
2402    /// Parameter types of the `<init>` matching `skip + n` descriptor args,
2403    /// returning the tail after `skip` leading synthetic params. Used to
2404    /// print `new` arguments with boolean/char constant adjustment.
2405    fn ctor_param_types(
2406        &self,
2407        cls: &str,
2408        skip: usize,
2409        n: usize,
2410        args: &[Expr],
2411    ) -> Option<Vec<crate::types::JavaType>> {
2412        // Overload/parameter recovery from the class's own `<init>` set is
2413        // container metadata: the front-end answers it.
2414        self.ctx.ctor_param_types(cls, skip, n, args)
2415    }
2416
2417    fn is_member_inner(&self, cls: &str) -> bool {
2418        // Front-end metadata question: synthetic outer field, or an enclosing
2419        // instance forwarded to super?
2420        if self.ctx.class_has_this0(cls) {
2421            return true;
2422        }
2423        if !cls.contains('$') || self.ctx.nested_is_static(cls) {
2424            return false;
2425        }
2426        self.ctx.outer_param_via_super(cls)
2427    }
2428
2429    /// Instantiated SAM return for a cast to a generic functional
2430    /// interface (`(Function<I,R>) lambda`): resolves the interface's SAM
2431    /// method Signature return against the cast's type arguments. Only
2432    /// meaningful when the result still contains a type variable — the
2433    /// erased impl body then needs `(R) value` witnesses on its returns
2434    /// (javac: "return type I cannot be converted to R" for `i -> i`).
2435    fn sam_ret_cast(&self, g: &crate::types::GenericType, sam_name: &str) -> Option<TypeRef> {
2436        // Resolving a functional interface's SAM return against a cast's type
2437        // arguments reads the interface's method signatures: front-end
2438        // metadata.
2439        self.ctx.sam_ret_cast(g, sam_name)
2440    }
2441
2442
2443    /// `<>` when `new cls(...)` should carry a diamond: the class is
2444    /// generic (class-level Signature with type params) and THIS file is
2445    /// Java 7+. A bare generic new compiles under old-style inference
2446    /// (standalone, not target-typed), which collapses to Object when
2447    /// implicitly-typed lambda args are involved — jdk11 Collectors.
2448    /// summingInt's `new CollectorImpl<>(() -> new int[1], (a, t) -> ...)`
2449    /// fails as bare `new CollectorImpl(...)` ("array required, but found
2450    /// Object", 72 errors in the concurrent-family closure). Bytecode
2451    /// cannot distinguish diamond from bare, so always prefer the diamond
2452    /// for generic classes.
2453    fn diamond_for(&self, cls: &str) -> &'static str {
2454        if self.ctx.source_level() < 51 {
2455            return "";
2456        }
2457        if self.ctx.class_declares_generics(cls) {
2458            "<>"
2459        } else {
2460            ""
2461        }
2462    }
2463
2464    /// Shorten an internal class name for emission: java.lang.* and same
2465    /// package use simple names; others fully qualified dotted.
2466    /// True when `cls.name` is a PRIVATE instance field and the owner
2467    /// expression's compile-time type is a strict subclass of cls: the
2468    /// field is not in the subclass's member scope, so the source must
2469    /// cast the owner to the declaring class
2470    /// (`((MessageDigest)this).algorithm` — jdk11 Delegate.clone x3,
2471    /// Signature.clone).
2472    fn private_super_field(&self, cls: &str, name: &str, o: &Expr) -> bool {
2473        if matches!(o, Expr::Raw(_) | Expr::RawT(..)) {
2474            return false;
2475        }
2476        let owner_ct = if matches!(o, Expr::This) {
2477            self.ctx.class_name().to_string()
2478        } else {
2479            match o.type_ref().erased() {
2480                crate::types::JavaType::Object(n) => n,
2481                _ => return false,
2482            }
2483        };
2484        if owner_ct == cls {
2485            return false;
2486        }
2487        let Some(flags) = self.ctx.field_flags(cls, name) else {
2488            return false;
2489        };
2490        if flags.contains(crate::types::FieldAccessFlags::STATIC)
2491            || !flags.contains(crate::types::FieldAccessFlags::PRIVATE)
2492        {
2493            return false;
2494        }
2495        // cls must be a strict superclass of the owner's compile-time type.
2496        let mut cur = owner_ct;
2497        for _ in 0..64 {
2498            let Some(sup) = self.ctx.super_name(&cur) else { return false };
2499            if sup == cls {
2500                return true;
2501            }
2502            cur = sup;
2503        }
2504        false
2505    }
2506
2507    fn needs_owner_cast(
2508        &self,
2509        cls: &str,
2510        name: &str,
2511        desc: &crate::types::MethodDescriptor,
2512        o: &Expr,
2513    ) -> bool {
2514        // Raw-rendered owners (qualified `HashMap.this`, substituted
2515        // capture text) carry no trustworthy static type — and casting
2516        // them to a RAW class erases the whole generic member chain
2517        // (`((HashMap) HashMap.this).<T>keysToArray(..)` made the call
2518        // raw: "Object[]无法转换为T[]").
2519        if matches!(o, Expr::Raw(_) | Expr::RawT(..) | Expr::This) {
2520            return false;
2521        }
2522        let crate::types::JavaType::Object(on) = o.type_ref().erased() else {
2523            return false;
2524        };
2525        if on == cls {
2526            return false;
2527        }
2528        let want = format!(
2529            "({}){}",
2530            desc.args.iter().map(|t| t.to_descriptor()).collect::<String>(),
2531            desc.ret.to_descriptor()
2532        );
2533        let Some(acc) = self.ctx.method_flags(cls, name, &want) else {
2534            return false;
2535        };
2536        use crate::types::MethodAccessFlags as M;
2537        if acc.contains(M::PRIVATE) {
2538            return true;
2539        }
2540        if !acc.contains(M::PUBLIC) && !acc.contains(M::PROTECTED) {
2541            fn pkg(n: &str) -> &str {
2542                n.rsplit_once('/').map(|(p, _)| p).unwrap_or("")
2543            }
2544            return pkg(&on) != pkg(cls);
2545        }
2546        false
2547    }
2548
2549    fn needs_object_relay(&self, ty: &TypeRef, e: &Expr) -> bool {
2550        if matches!(e, Expr::Cast { .. } | Expr::Const(_)) {
2551            return false;
2552        }
2553        let (
2554            crate::types::JavaType::Object(from),
2555            crate::types::JavaType::Object(to),
2556        ) = (e.type_ref().erased(), ty.erased())
2557        else {
2558            return false;
2559        };
2560        if from == to || from == "java/lang/Object" || to == "java/lang/Object" {
2561            return false;
2562        }
2563        if !self.ctx.has_class(&from) || !self.ctx.has_class(&to) {
2564            return false;
2565        }
2566        if self.ctx.is_subtype_of(&e.type_ref().erased(), &to)
2567            || self.ctx.is_subtype_of(&ty.erased(), &from)
2568        {
2569            return false;
2570        }
2571        let both_classes = !self.ctx.is_interface(&from) && !self.ctx.is_interface(&to);
2572        both_classes || self.ctx.is_sealed(&from) || self.ctx.is_sealed(&to)
2573    }
2574
2575    pub fn shorten(&self, internal: &str) -> String {
2576        if internal.is_empty() {
2577            return String::new();
2578        }
2579        // Literal-$ top-level class (in pool, no InnerClasses nesting
2580        // evidence): the $ is part of the SOURCE name (jextract-generated
2581        // errno_h$shared) — never dot it into a nested qualifier. Same rule
2582        // whenever dotting could not parse: `DolTest2$$dollah$$` has empty
2583        // segments, and `$` is a legal identifier character while `A..b` is
2584        // not.
2585        let dot_safe = |simple: &str| -> bool {
2586            simple.split('$').skip(1).all(|seg| {
2587                !seg.is_empty()
2588                    && seg.chars().next().map(|c| !c.is_ascii_digit()).unwrap_or(false)
2589            })
2590        };
2591        let simple_here = internal.rsplit('/').next().unwrap_or(internal).to_string();
2592        let keep_dollar_pool =
2593            internal.contains('$') && self.ctx.has_class(internal) && self.ctx.find_outer(internal).is_none();
2594        let keep_dollar = keep_dollar_pool || (internal.contains('$') && !dot_safe(&simple_here));
2595        // Anonymous class types (all-digit last segment) have no source
2596        // name: print the base interface/superclass instead.
2597        if let Some(last) = internal.rsplit('$').next() {
2598            if !last.is_empty() && last.chars().all(|c| c.is_ascii_digit()) {
2599                if let Some((ifaces, sup)) = self.ctx.class_bases(internal) {
2600                    if let Some(n) = ifaces.first() {
2601                        return self.shorten(n);
2602                    }
2603                    if let Some(sup) = sup {
2604                        if sup != "java/lang/Object" {
2605                            return self.shorten(&sup);
2606                        }
2607                    }
2608                    return "Object".to_string();
2609                }
2610            }
2611        }
2612        // Local classes are emitted with their simple source name.
2613        if let Some(last) = internal.rsplit('$').next() {
2614            if !keep_dollar_pool
2615                && !last.is_empty()
2616                && !last.chars().all(|c| c.is_ascii_digit())
2617                && internal.contains('$')
2618            {
2619                // Nested-class names print as Outer.Inner, except local
2620                // classes whose outer chain includes digits (e.g. Foo$1Bar).
2621                let mut segs = internal.split('$');
2622                if let Some(first) = segs.next() {
2623                    let rest: Vec<&str> = segs.collect();
2624                    if rest.iter().any(|r| r.starts_with(|c: char| c.is_ascii_digit())) {
2625                        // Local/synthetic class (digit-led segment): javac
2626                        // encodes a method-local class as `Outer$1Name`; its
2627                        // source name is the digit prefix stripped, declared
2628                        // `class Name` at its use site and referenced only
2629                        // within that scope. Printing the binary name leaves
2630                        // an unresolvable symbol (ClassSpecializer$Factory$1Var).
2631                        if let Some(last) = internal.rsplit('$').next() {
2632                            let stripped = last.trim_start_matches(|c: char| c.is_ascii_digit());
2633                            if !stripped.is_empty()
2634                                && !stripped.chars().next().unwrap().is_ascii_digit()
2635                            {
2636                                return stripped.to_string();
2637                            }
2638                        }
2639                        return internal.replace('/', ".");
2640                    }
2641                    let _ = first;
2642                    let _ = last;
2643                }
2644            }
2645        }
2646        let dotted = internal.replace('/', ".");
2647        // A simple type name shadowed by an in-scope VARIABLE (a field of
2648        // this class or a method local) cannot qualify a static access:
2649        // javac resolves the leading name as the variable first
2650        // (无法取消引用int — jdk26 VerificationType declares
2651        // `private static final int Integer` and the source spells
2652        // java.lang.Integer.toHexString in full). Keep the fully-qualified
2653        // form whenever the leading segment collides.
2654        let shadowed = |first: &str| {
2655            self.ctx.declares_field(self.ctx.class_name(), first)
2656                || self.vt.vars.iter().any(|v| v.name == first)
2657        };
2658        if internal.starts_with("java/lang/") && !internal[10..].contains('/') {
2659            let simple = if keep_dollar {
2660                internal[10..].to_string()
2661            } else {
2662                internal[10..].replace('$', ".")
2663            };
2664            let first = simple.split('.').next().unwrap_or(simple.as_str());
2665            if !shadowed(first) {
2666                return simple;
2667            }
2668            return dotted.replace('$', ".");
2669        }
2670        if pkg_of(internal) == pkg_of(&self.ctx.class_name()) {
2671            let simple = if keep_dollar {
2672                internal.rsplit('/').next().unwrap_or(internal).to_string()
2673            } else {
2674                internal.rsplit('/').next().unwrap_or(internal).replace('$', ".")
2675            };
2676            let first = simple.split('.').next().unwrap_or(simple.as_str());
2677            if !shadowed(first) {
2678                return simple;
2679            }
2680        }
2681        if keep_dollar {
2682            dotted
2683        } else {
2684            dotted.replace('$', ".")
2685        }
2686    }
2687
2688    pub fn type_name(&self, ty: &TypeRef) -> String {
2689        match ty {
2690            TypeRef::J(t) => self.java_type_name(t),
2691            TypeRef::G(g) => {
2692                // Render generic signature with shortened class names.
2693                self.generic_name(g)
2694            }
2695        }
2696    }
2697
2698    fn java_type_name(&self, t: &JavaType) -> String {
2699        match t {
2700            JavaType::Object(n) => self.shorten(n),
2701            JavaType::Array(inner) => format!("{}[]", self.java_type_name(inner)),
2702            other => other.to_java(false),
2703        }
2704    }
2705
2706    fn generic_name(&self, g: &crate::types::GenericType) -> String {
2707        use crate::types::{GenericType, WildcardBound};
2708        match g {
2709            GenericType::Primitive(c) => prim_name(*c).to_string(),
2710            GenericType::Class(cs) => {
2711                // The signature parser splits a literal-$ top-level name
2712                // (jextract-generated errno_h$shared) into nested-looking
2713                // parts [errno_h, shared]; rejoin and, when the pool
2714                // proves the class is NOT nested, render the binary name
2715                // through shorten (same-package simple form keeps the $).
2716                if cs.parts.len() > 1
2717                    && cs.parts.iter().skip(1).all(|p| p.args.is_empty())
2718                {
2719                    let joined = cs
2720                        .parts
2721                        .iter()
2722                        .map(|p| p.name.as_str())
2723                        .collect::<Vec<_>>()
2724                        .join("$");
2725                    let full = if cs.package.is_empty() {
2726                        joined
2727                    } else {
2728                        format!("{}/{}", cs.package, joined)
2729                    };
2730                    let literal = self.ctx.has_class(&full) && self.ctx.find_outer(&full).is_none();
2731                    if literal {
2732                        return self.shorten(&full);
2733                    }
2734                }
2735                let mut s = String::new();
2736                if !cs.package.is_empty() {
2737                    let full = format!("{}/{}", cs.package, cs.parts.first().map(|p| p.name.as_str()).unwrap_or(""));
2738                    s.push_str(&self.shorten(&full));
2739                } else {
2740                    s.push_str(&cs.parts.first().map(|p| p.name.clone()).unwrap_or_default());
2741                }
2742                if let Some(first) = cs.parts.first() {
2743                    if !first.args.is_empty() {
2744                        s.push('<');
2745                        for (i, a) in first.args.iter().enumerate() {
2746                            if i > 0 {
2747                                s.push_str(", ");
2748                            }
2749                            s.push_str(&self.generic_name(a));
2750                        }
2751                        s.push('>');
2752                    }
2753                }
2754                for p in cs.parts.iter().skip(1) {
2755                    let digit_led = p.name.chars().next().map(|c| c.is_ascii_digit()).unwrap_or(false);
2756                    let stripped = p.name.trim_start_matches(|c: char| c.is_ascii_digit());
2757                    if digit_led && !stripped.is_empty() && p.args.is_empty() {
2758                        // javac's local-class encoding `Outer$1Name`: the
2759                        // source name is the prefix-stripped simple name,
2760                        // referenced WITHOUT qualification (there is no
2761                        // legal `DocLint.1Pair` — `> or ',' expected`).
2762                        s = stripped.to_string();
2763                        continue;
2764                    }
2765                    s.push('.');
2766                    s.push_str(&p.name);
2767                    if !p.args.is_empty() {
2768                        s.push('<');
2769                        for (i, a) in p.args.iter().enumerate() {
2770                            if i > 0 {
2771                                s.push_str(", ");
2772                            }
2773                            s.push_str(&self.generic_name(a));
2774                        }
2775                        s.push('>');
2776                    }
2777                }
2778                s
2779            }
2780            GenericType::Array(inner) => format!("{}[]", self.generic_name(inner)),
2781            GenericType::TypeVar(n) => n.clone(),
2782            GenericType::Wildcard(w) => match w {
2783                WildcardBound::Any => "?".to_string(),
2784                WildcardBound::Extends(t) => format!("? extends {}", self.generic_name(t)),
2785                WildcardBound::Super(t) => format!("? super {}", self.generic_name(t)),
2786            },
2787        }
2788    }
2789}
2790
2791fn inner_simple(cls: &str) -> String {
2792    let last = cls.rsplit('/').next().unwrap_or(cls);
2793    last.rsplit('$').next().unwrap_or(last).to_string()
2794}
2795
2796fn push_char_lit(out: &mut String, n: i32) {
2797    let c = (n as u16) as u32;
2798    match char::from_u32(c) {
2799        Some('\'') => out.push_str("'\\''"),
2800        Some('\\') => out.push_str("'\\\\'"),
2801        Some('\n') => out.push_str("'\\n'"),
2802        Some('\r') => out.push_str("'\\r'"),
2803        Some('\t') => out.push_str("'\\t'"),
2804        Some(ch) if ch.is_control() => out.push_str(&format!("'\\u{:04x}'", c)),
2805        Some(ch) => {
2806            out.push('\'');
2807            out.push(ch);
2808            out.push('\'');
2809        }
2810        None => out.push_str(&format!("'\\u{:04x}'", c)),
2811    }
2812}
2813
2814fn const_bool(e: &Expr) -> Option<bool> {
2815    match e {
2816        Expr::Const(ConstVal::Int(n)) => Some(*n != 0),
2817        _ => None,
2818    }
2819}
2820
2821fn pkg_of(n: &str) -> &str {
2822    n.rfind('/').map(|i| &n[..i]).unwrap_or("")
2823}
2824
2825fn prim_name(c: char) -> &'static str {
2826    match c {
2827        'V' => "void",
2828        'Z' => "boolean",
2829        'B' => "byte",
2830        'C' => "char",
2831        'S' => "short",
2832        'I' => "int",
2833        'F' => "float",
2834        'J' => "long",
2835        'D' => "double",
2836        _ => "?",
2837    }
2838}
2839
2840pub fn escape_string(s: &str) -> String {
2841    let mut out = String::with_capacity(s.len());
2842    for c in s.chars() {
2843        match c {
2844            '"' => out.push_str("\\\""),
2845            '\\' => out.push_str("\\\\"),
2846            '\n' => out.push_str("\\n"),
2847            '\r' => out.push_str("\\r"),
2848            '\t' => out.push_str("\\t"),
2849            '\u{0}' => out.push_str("\\0"),
2850            '\u{8}' => out.push_str("\\b"),
2851            '\u{C}' => out.push_str("\\f"),
2852            c if (c as u32) < 0x20 || (c as u32) == 0x7F => {
2853                out.push_str(&format!("\\u{:04x}", c as u32));
2854            }
2855            c => out.push(c),
2856        }
2857    }
2858    out
2859}
2860
2861pub fn escape_char(c: char) -> String {
2862    match c {
2863        '\'' => "\\'".to_string(),
2864        '\\' => "\\\\".to_string(),
2865        '\n' => "\\n".to_string(),
2866        '\r' => "\\r".to_string(),
2867        '\t' => "\\t".to_string(),
2868        '\0' => "\\0".to_string(),
2869        c if (c as u32) < 0x20 || (c as u32) == 0x7F => format!("\\u{:04x}", c as u32),
2870        c => c.to_string(),
2871    }
2872}
2873
2874pub fn format_float(v: f64, is_float: bool) -> String {
2875    let suffix = if is_float { "f" } else { "" };
2876    // Arithmetic literals, not `Double.NaN`/`POSITIVE_INFINITY` field refs:
2877    // inside java.lang.Double/Float themselves a field reference would be
2878    // a self-reference initializing the very field being printed.
2879    if v.is_nan() {
2880        return if is_float { "(0.0f / 0.0f)".into() } else { "(0.0 / 0.0)".into() };
2881    }
2882    if v.is_infinite() {
2883        let one = if is_float { "1.0f" } else { "1.0" };
2884        let zero = if is_float { "0.0f" } else { "0.0" };
2885        return if v < 0.0 {
2886            format!("(-{} / {})", one, zero)
2887        } else {
2888            format!("({} / {})", one, zero)
2889        };
2890    }
2891    let mut s = format!("{}", v);
2892    if !s.contains('.') && !s.contains('e') && !s.contains('E') {
2893        s.push_str(".0");
2894    }
2895    // Java doesn't accept exponent forms like "1e-7"? It does: 1e-7 is a valid
2896    // double literal. But "inf"/"nan" handled above.
2897    format!("{}{}", s, suffix)
2898}
2899
2900
2901// ---------------------------------------------------------------------------
2902// Unreachable-code truncation (final statement tree)
2903// ---------------------------------------------------------------------------
2904
2905/// Drop statements that follow a `while (true)` which cannot complete
2906/// normally (no `break` in its body binds to it): javac rejects them as
2907/// 无法访问的语句, and the structurers only emitted them as shared-tail
2908/// copies whose real arrivals all render INSIDE the loop arms (jdk17/26
2909/// Pattern.clazz's for(;;){switch}: case-93's `return prev/negate` tail
2910/// landed after the never-completing loop). Runs on the FINAL tree (after
2911/// switch restoration and goto resolution) so the break inventory is
2912/// exactly what gets emitted; every unknown shape stays conservative
2913/// (assumes the loop can complete).
2914fn truncate_dead_ends(s: &Stmt) -> Stmt {
2915    match s {
2916        Stmt::Block(v) => {
2917            let mut out: Vec<Stmt> = Vec::with_capacity(v.len());
2918            for x in v {
2919                if !out.is_empty() && dead_end_infinite_while(out.last().unwrap()) {
2920                    if crate::dbg_flag!("JCDC_DBG_GOTO") {
2921                        eprintln!(
2922                            "DEADEND truncate {} unreachable stmt(s)",
2923                            v.len() - out.len()
2924                        );
2925                    }
2926                    break;
2927                }
2928                out.push(truncate_dead_ends(x));
2929            }
2930            Stmt::Block(out)
2931        }
2932        Stmt::If { cond, then_stmt, else_stmt } => Stmt::If {
2933            cond: cond.clone(),
2934            then_stmt: Box::new(truncate_dead_ends(then_stmt)),
2935            else_stmt: else_stmt.as_deref().map(truncate_dead_ends).map(Box::new),
2936        },
2937        Stmt::While { cond, body } => Stmt::While {
2938            cond: cond.clone(),
2939            body: Box::new(truncate_dead_ends(body)),
2940        },
2941        Stmt::DoWhile { body, cond } => Stmt::DoWhile {
2942            body: Box::new(truncate_dead_ends(body)),
2943            cond: cond.clone(),
2944        },
2945        Stmt::For { init, cond, update, body } => Stmt::For {
2946            init: init.iter().map(truncate_dead_ends).collect(),
2947            cond: cond.clone(),
2948            update: update.clone(),
2949            body: Box::new(truncate_dead_ends(body)),
2950        },
2951        Stmt::ForEach { var, iterable, is_array, body } => Stmt::ForEach {
2952            var: *var,
2953            iterable: iterable.clone(),
2954            is_array: *is_array,
2955            body: Box::new(truncate_dead_ends(body)),
2956        },
2957        Stmt::Switch { selector, cases, default, on_string } => Stmt::Switch {
2958            selector: selector.clone(),
2959            cases: cases
2960                .iter()
2961                .map(|c| CaseGroup {
2962                    labels: c.labels.clone(),
2963                    string_labels: c.string_labels.clone(),
2964                    enum_labels: c.enum_labels.clone(),
2965                    raw_labels: c.raw_labels.clone(),
2966                    guard: c.guard.clone(),
2967                    body: c.body.iter().map(truncate_dead_ends).collect(),
2968                })
2969                .collect(),
2970            default: default.as_deref().map(truncate_dead_ends).map(Box::new),
2971            on_string: *on_string,
2972        },
2973        Stmt::Try { body, catches, finally } => Stmt::Try {
2974            body: Box::new(truncate_dead_ends(body)),
2975            catches: catches
2976                .iter()
2977                .map(|c| Catch {
2978                    exc: c.exc.clone(),
2979                    var: c.var,
2980                    var_name: c.var_name.clone(),
2981                    body: Box::new(truncate_dead_ends(&c.body)),
2982                })
2983                .collect(),
2984            finally: finally.as_deref().map(truncate_dead_ends).map(Box::new),
2985        },
2986        Stmt::TryWithResources { resources, body, catches, finally } => {
2987            Stmt::TryWithResources {
2988                resources: resources.iter().map(truncate_dead_ends).collect(),
2989                body: Box::new(truncate_dead_ends(body)),
2990                catches: catches
2991                    .iter()
2992                    .map(|c| Catch {
2993                        exc: c.exc.clone(),
2994                        var: c.var,
2995                        var_name: c.var_name.clone(),
2996                        body: Box::new(truncate_dead_ends(&c.body)),
2997                    })
2998                    .collect(),
2999                finally: finally.as_deref().map(truncate_dead_ends).map(Box::new),
3000            }
3001        }
3002        Stmt::Synchronized { lock, body } => Stmt::Synchronized {
3003            lock: lock.clone(),
3004            body: Box::new(truncate_dead_ends(body)),
3005        },
3006        Stmt::Labeled { label, body } => Stmt::Labeled {
3007            label: label.clone(),
3008            body: Box::new(truncate_dead_ends(body)),
3009        },
3010        other => other.clone(),
3011    }
3012}
3013
3014/// True when `s` is a `while (true)` (labeled or not) whose body holds
3015/// no `break` binding to it — it can never complete normally.
3016pub(crate) fn dead_end_infinite_while(s: &Stmt) -> bool {
3017    let (label, body, cond) = match s {
3018        Stmt::While { cond, body } => (None, body.as_ref(), cond),
3019        Stmt::Labeled { label, body } => match &**body {
3020            Stmt::While { cond, body } => (Some(label.as_str()), body.as_ref(), cond),
3021            _ => return false,
3022        },
3023        _ => return false,
3024    };
3025    if !matches!(cond, Expr::Const(ConstVal::Int(1))) {
3026        return false;
3027    }
3028    // JLS 14.14: `while (true)` completes normally iff its body holds a
3029    // reachable break that exits IT: an unlabeled break not captured by
3030    // an inner breakable construct, or `break L` where L is THIS loop's
3031    // own label. A labeled break to any OTHER label — inner (Pattern
3032    // .clazz's `break L4`) or OUTER (jdk11/26 ThreadPoolExecutor
3033    // .addWorker's `break retry` inside the inner for(;;)) — completes
3034    // the while ABRUPTLY: javac's flow model marks the statements after
3035    // such a loop unreachable (the w26 `continue;` after the inner
3036    // while(true) was a latent 无法访问的语句 masked by alphabetically
3037    // earlier census errors).
3038    !has_break_exiting(body, label, 0)
3039}
3040
3041fn has_break_exiting(s: &Stmt, lbl: Option<&str>, depth: usize) -> bool {
3042    match s {
3043        Stmt::Break(None) => depth == 0,
3044        // Only this loop's OWN label completes it; any other labeled
3045        // break exits abruptly (JLS 14.14/14.17).
3046        Stmt::Break(Some(l)) => Some(l.as_str()) == lbl,
3047        Stmt::Continue(_) | Stmt::Return(_) | Stmt::Throw(_) => false,
3048        Stmt::Goto(_) => true,
3049        Stmt::Block(v) => v.iter().any(|x| has_break_exiting(x, lbl, depth)),
3050        Stmt::If { then_stmt, else_stmt, .. } => {
3051            has_break_exiting(then_stmt, lbl, depth)
3052                || else_stmt
3053                    .as_deref()
3054                    .map(|e| has_break_exiting(e, lbl, depth))
3055                    .unwrap_or(false)
3056        }
3057        Stmt::While { body, .. }
3058        | Stmt::DoWhile { body, .. }
3059        | Stmt::For { body, .. }
3060        | Stmt::ForEach { body, .. } => has_break_exiting(body, lbl, depth + 1),
3061        Stmt::Switch { cases, default, .. } => {
3062            cases
3063                .iter()
3064                .any(|c| c.body.iter().any(|x| has_break_exiting(x, lbl, depth + 1)))
3065                || default
3066                    .as_deref()
3067                    .map(|d| has_break_exiting(d, lbl, depth + 1))
3068                    .unwrap_or(false)
3069        }
3070        Stmt::Try { body, catches, finally }
3071        | Stmt::TryWithResources { body, catches, finally, .. } => {
3072            has_break_exiting(body, lbl, depth)
3073                || catches.iter().any(|c| has_break_exiting(&c.body, lbl, depth))
3074                || finally
3075                    .as_deref()
3076                    .map(|f| has_break_exiting(f, lbl, depth))
3077                    .unwrap_or(false)
3078        }
3079        Stmt::Synchronized { body, .. } => has_break_exiting(body, lbl, depth),
3080        Stmt::Labeled { body, .. } => has_break_exiting(body, lbl, depth),
3081        // Plain leaf statements never contain a break.
3082        Stmt::ExprStmt(_)
3083        | Stmt::LocalDef { .. }
3084        | Stmt::Assert { .. }
3085        | Stmt::TernaryValue { .. }
3086        | Stmt::MonitorEnter(_)
3087        | Stmt::MonitorExit(_)
3088        | Stmt::Comment(_) => false,
3089        // Opaque shapes (raw labels, inline class decls, anything new):
3090        // keep truncation off when an escape cannot be ruled out.
3091        _ => true,
3092    }
3093}
3094