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