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, ¶m_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