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