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