github.com/Konstantin8105/c4go@v0.0.0-20240505174241-768bb1c65a51/transpiler/unary.go (about) 1 // This file contains functions for transpiling unary operator expressions. 2 3 package transpiler 4 5 import ( 6 "fmt" 7 "strconv" 8 "strings" 9 10 "github.com/Konstantin8105/c4go/ast" 11 "github.com/Konstantin8105/c4go/program" 12 "github.com/Konstantin8105/c4go/types" 13 "github.com/Konstantin8105/c4go/util" 14 15 goast "go/ast" 16 "go/token" 17 ) 18 19 func transpileUnaryOperatorInc(n *ast.UnaryOperator, p *program.Program, operator token.Token) ( 20 expr goast.Expr, eType string, preStmts []goast.Stmt, postStmts []goast.Stmt, err error) { 21 defer func() { 22 if err != nil { 23 err = fmt.Errorf("cannot transpileUnaryOperatorInc. err = %v", err) 24 } 25 if eType == "" { 26 eType = "EmptyTypeInUnaryOperatorInc" 27 } 28 }() 29 30 if !(operator == token.INC || operator == token.DEC) { 31 err = fmt.Errorf("not acceptable operator '%v'", operator) 32 return 33 } 34 35 // for values 36 if v, ok := n.Children()[0].(*ast.DeclRefExpr); ok && 37 !types.IsPointer(v.Type, p) { 38 switch n.Operator { 39 case "++": 40 return &goast.BinaryExpr{ 41 X: util.NewIdent(v.Name), 42 Op: token.ADD_ASSIGN, 43 Y: &goast.BasicLit{Kind: token.INT, Value: "1"}, 44 }, n.Type, nil, nil, nil 45 case "--": 46 return &goast.BinaryExpr{ 47 X: util.NewIdent(v.Name), 48 Op: token.SUB_ASSIGN, 49 Y: &goast.BasicLit{Kind: token.INT, Value: "1"}, 50 }, n.Type, nil, nil, nil 51 } 52 } 53 54 // for other case 55 op := "+" 56 if operator == token.DEC { 57 op = "-" 58 } 59 60 if len(n.ChildNodes) != 1 { 61 err = fmt.Errorf("not enought ChildNodes: %d", len(n.ChildNodes)) 62 return 63 } 64 65 if !types.IsPointer(n.Type, p) { 66 67 binaryOperator := "+=" 68 if operator == token.DEC { 69 binaryOperator = "-=" 70 } 71 72 return transpileBinaryOperator(&ast.BinaryOperator{ 73 Type: n.Type, 74 Operator: binaryOperator, 75 ChildNodes: []ast.Node{ 76 n.ChildNodes[0], 77 &ast.IntegerLiteral{ 78 Type: "int", 79 Value: "1", 80 }, 81 }, 82 }, p, false) 83 } 84 85 // from: 86 // *w++ 87 // to: 88 // func () []byte { 89 // defer func(){ 90 // *w = *w + 1 // binary 91 // }() 92 // tempVar := *w 93 // return tempVar 94 // } 95 varName := "tempVarUnary" 96 97 v, vType, _, _, _ := transpileToExpr(n.ChildNodes[0], p, false) 98 incExpr, _, newPre, newPost, err := transpileBinaryOperator(&ast.BinaryOperator{ 99 Type: n.Type, 100 Operator: "=", 101 ChildNodes: []ast.Node{ 102 n.ChildNodes[0], 103 &ast.BinaryOperator{ 104 Type: n.Type, 105 Operator: op, 106 ChildNodes: append(n.ChildNodes, &ast.IntegerLiteral{ 107 Type: "int", 108 Value: "1", 109 }), 110 }, 111 }, 112 }, p, false) 113 preStmts, postStmts = combinePreAndPostStmts(preStmts, postStmts, newPre, newPost) 114 if err != nil { 115 return 116 } 117 118 var exprResolveType string 119 exprResolveType, err = types.ResolveType(p, vType) 120 if err != nil { 121 return 122 } 123 124 expr = util.NewAnonymousFunction( 125 // body : 126 append(preStmts, &goast.AssignStmt{ 127 Lhs: []goast.Expr{util.NewIdent(varName)}, 128 Tok: token.DEFINE, 129 Rhs: []goast.Expr{v}, 130 }), 131 // defer : 132 []goast.Stmt{ 133 &goast.ExprStmt{X: incExpr}, 134 }, 135 // return : 136 util.NewIdent(varName), 137 exprResolveType) 138 139 eType = n.Type 140 141 return 142 } 143 144 func transpileUnaryOperatorNot(n *ast.UnaryOperator, p *program.Program) ( 145 goast.Expr, string, []goast.Stmt, []goast.Stmt, error) { 146 e, eType, preStmts, postStmts, err := atomicOperation(n.Children()[0], p) 147 if err != nil { 148 return nil, "", nil, nil, err 149 } 150 151 // UnaryOperator <> 'int' prefix '!' 152 // `-ImplicitCastExpr <> 'int (*)(int, double)' <LValueToRValue> 153 // `-DeclRefExpr <> 'int (*)(int, double)' lvalue Var 0x2be4e80 'T' 'int (*)(int, double)' 154 if util.IsFunction(eType) { 155 return &goast.BinaryExpr{ 156 X: e, 157 Op: token.EQL, // == 158 Y: goast.NewIdent("nil"), 159 }, "bool", preStmts, postStmts, nil 160 } 161 162 // specific case: 163 // 164 // UnaryOperator 'int' prefix '!' 165 // `-ParenExpr 'int' 166 // `-BinaryOperator 'int' '=' 167 // |-DeclRefExpr 'int' lvalue Var 0x3329b60 'y' 'int' 168 // `-ImplicitCastExpr 'int' <LValueToRValue> 169 // `-DeclRefExpr 'int' lvalue Var 0x3329ab8 'p' 'int' 170 if par, ok := e.(*goast.ParenExpr); ok { 171 if bi, ok := par.X.(*goast.BinaryExpr); ok { 172 if bi.Op == token.ASSIGN { // = 173 preStmts = append(preStmts, &goast.ExprStmt{ 174 X: bi, 175 }) 176 e = bi.X 177 } 178 } 179 } 180 181 // null in C is zero 182 if eType == types.NullPointer { 183 e = &goast.BasicLit{ 184 Kind: token.INT, 185 Value: "0", 186 } 187 eType = "int" 188 } 189 190 if eType == "bool" { 191 return util.NewUnaryExpr(e, token.NOT), "bool", preStmts, postStmts, nil 192 } 193 194 if strings.HasSuffix(eType, "*") { 195 // `!pointer` has to be converted to `pointer == nil` 196 return &goast.BinaryExpr{ 197 X: e, 198 Op: token.EQL, 199 Y: util.NewIdent("nil"), 200 }, "bool", preStmts, postStmts, nil 201 } 202 203 t, err := types.ResolveType(p, eType) 204 p.AddMessage(p.GenerateWarningMessage(err, n)) 205 206 if t == "[]byte" { 207 return util.NewUnaryExpr( 208 util.NewCallExpr("noarch.CStringIsNull", e), token.NOT, 209 ), "bool", preStmts, postStmts, nil 210 } 211 212 // only if added "stdbool.h" 213 if p.IncludeHeaderIsExists("stdbool.h") { 214 if t == "_Bool" { 215 t = "int32" 216 e = util.NewCallExpr("int32", e) 217 } 218 } 219 220 p.AddImport("github.com/Konstantin8105/c4go/noarch") 221 222 eType = "bool" 223 224 return util.NewCallExpr("noarch.Not", e), 225 eType, preStmts, postStmts, nil 226 } 227 228 // transpileUnaryOperatorAmpersant - operator ampersant & 229 // Example of AST: 230 // 231 // UnaryOperator 'int (*)[5]' prefix '&' 232 // `-DeclRefExpr 'int [5]' lvalue Var 0x2d0fb20 'arr' 'int [5]' 233 // 234 // UnaryOperator 'char **' prefix '&' 235 // `-DeclRefExpr 'char *' lvalue Var 0x39b95f0 'line' 'char *' 236 // 237 // UnaryOperator 'float *' prefix '&' 238 // `-DeclRefExpr 'float' lvalue Var 0x409e2a0 't' 'float' 239 func transpileUnaryOperatorAmpersant(n *ast.UnaryOperator, p *program.Program) ( 240 expr goast.Expr, eType string, preStmts []goast.Stmt, postStmts []goast.Stmt, err error) { 241 defer func() { 242 if err != nil { 243 err = fmt.Errorf("cannot transpileUnaryOperatorAmpersant : err = %v", err) 244 } 245 }() 246 247 expr, eType, preStmts, postStmts, err = transpileToExpr(n.Children()[0], p, false) 248 if err != nil { 249 return 250 } 251 if expr == nil { 252 err = fmt.Errorf("expr is nil") 253 return 254 } 255 256 if util.IsFunction(eType) { 257 return 258 } 259 260 if util.IsLastArray(eType) { 261 // In : eType = 'int [5]' 262 // Out: eType = 'int *' 263 f := strings.Index(eType, "[") 264 e := strings.Index(eType, "]") 265 if e == len(eType)-1 { 266 eType = eType[:f] + "*" 267 } else { 268 eType = eType[:f] + "*" + eType[e+1:] 269 } 270 return 271 } 272 273 if ind, ok := expr.(*goast.IndexExpr); ok { 274 // from : 275 // 276 // 0 *ast.IndexExpr { 277 // 1 . X: *ast.Ident { 278 // 3 . . Name: "b" 279 // 4 . } 280 // 6 . Index: *ast.BasicLit { ... } 281 // 12 } 282 // 283 // to: 284 // 285 // 88 0: *ast.SliceExpr { 286 // 89 . X: *ast.Ident { 287 // 91 . . Name: "b" 288 // 93 . } 289 // 95 . Low: *ast.BasicLit { ... } 290 // 99 . } 291 // 102 } 292 expr = &goast.SliceExpr{ 293 X: ind.X, 294 Low: ind.Index, 295 Slice3: false, 296 } 297 eType = n.Type 298 return 299 } 300 301 // In : eType = 'int' 302 // Out: eType = 'int *' 303 // FIXME: This will need to use a real slice to reference the original 304 // value. 305 resolvedType, err := types.ResolveType(p, eType) 306 if err != nil { 307 p.AddMessage(p.GenerateWarningMessage(err, n)) 308 return 309 } 310 311 // We now have a pointer to the original type. 312 eType += " *" 313 314 p.AddImport("unsafe") 315 316 // UnaryOperator 'float *' prefix '&' 317 // `-DeclRefExpr 'float' lvalue Var 0x409e2a0 't' 'float' 318 if e, ok := ConvertValueToPointer(n.Children(), p); ok { 319 expr = e 320 return 321 } 322 323 expr = CreateSliceFromReference(resolvedType, expr) 324 325 return 326 } 327 328 // pointerParts - separate to pointer and value. 329 // - change type for all nodes to `int` 330 // 331 // BinaryOperator <col:13, col:57> 'int *' '-' 332 // |-BinaryOperator <col:13, col:40> 'int *' '+' 333 // | |-BinaryOperator <col:13, col:32> 'int *' '+' 334 // | | |-BinaryOperator <col:13, col:21> 'int *' '+' 335 // | | | |-BinaryOperator <col:13, col:17> 'int' '+' 336 // | | | | |-IntegerLiteral <col:13> 'int' 1 337 // | | | | `-IntegerLiteral <col:17> 'int' 0 338 // | | | `-ImplicitCastExpr <col:21> 'int *' <LValueToRValue> 339 // | | | `-DeclRefExpr <col:21> 'int *' lvalue Var 0x29a91a8 'i5' 'int *' 340 // | | `-BinaryOperator <col:26, col:32> 'long' '*' 341 // | | |-ImplicitCastExpr <col:26> 'long' <IntegralCast> 342 // | | | `-IntegerLiteral <col:26> 'int' 5 343 // | | `-CallExpr <col:28, col:32> 'long' 344 // | | `-ImplicitCastExpr <col:28> 'long (*)()' <FunctionToPointerDecay> 345 // | | `-DeclRefExpr <col:28> 'long ()' Function 0x29a8470 'get' 'long ()' 346 // | `-CallExpr <col:36, col:40> 'long' 347 // | `-ImplicitCastExpr <col:36> 'long (*)()' <FunctionToPointerDecay> 348 // | `-DeclRefExpr <col:36> 'long ()' Function 0x29a8470 'get' 'long ()' 349 // `-BinaryOperator <col:44, col:57> 'long' '*' 350 // 351 // |-ImplicitCastExpr <col:44, col:51> 'long' <IntegralCast> 352 // | `-ParenExpr <col:44, col:51> 'int' 353 // | `-BinaryOperator <col:45, col:50> 'int' '+' 354 // | |-IntegerLiteral <col:45> 'int' 12 355 // | `-IntegerLiteral <col:50> 'int' 3 356 // `-CallExpr <col:53, col:57> 'long' 357 // `-ImplicitCastExpr <col:53> 'long (*)()' <FunctionToPointerDecay> 358 // `-DeclRefExpr <col:53> 'long ()' Function 0x29a8470 'get' 'long ()' 359 // 360 // ParenExpr <col:25, col:31> 'char *' 361 // `-UnaryOperator <col:26, col:29> 'char *' postfix '++' 362 // 363 // `-DeclRefExpr <col:26> 'char *' lvalue Var 0x3c05ae8 'pos' 'char *' 364 // 365 // BinaryOperator 0x128d3b8 <col:8, col:28> 'char *' '+' 366 // |-ImplicitCastExpr 0x128d3a0 <col:8> 'char *' <ArrayToPointerDecay> 367 // | `-DeclRefExpr 0x128d2d0 <col:8> 'char [262144]' lvalue Var 0x128b730 'hynums' 'char [262144]' 368 // `-ParenExpr 0x128d380 <col:17, col:28> 'long' 369 // 370 // `-BinaryOperator 0x128d360 <col:18, col:22> 'long' '-' 371 // |-ImplicitCastExpr 0x128d330 <col:18> 'char *' <LValueToRValue> 372 // | `-DeclRefExpr 0x128d2f0 <col:18> 'char *' lvalue Var 0x128bc80 'p' 'char *' 373 // `-ImplicitCastExpr 0x128d348 <col:22> 'char *' <ArrayToPointerDecay> 374 // `-DeclRefExpr 0x128d310 <col:22> 'char [262144]' lvalue Var 0x128b610 'hypats' 'char [262144]' 375 func pointerParts(node *ast.Node, p *program.Program) ( 376 pnt ast.Node, value ast.Node, back func(), undefineIndex bool, err error) { 377 defer func() { 378 if err != nil { 379 err = fmt.Errorf("cannot pointerParts: err = %v", err) 380 } 381 }() 382 383 var counter int 384 385 var lastNode *ast.Node 386 387 // replacer zero 388 zero := &ast.IntegerLiteral{Type: "int", Value: "0"} 389 390 var baseTypes []*string 391 var searcher func(*ast.Node) bool 392 replacer := func(node *ast.Node) { 393 pnt = *node 394 lastNode = node 395 *node = zero 396 counter++ 397 } 398 searcher = func(node *ast.Node) (modify bool) { 399 // save types of all nodes 400 t, ok := ast.GetTypeIfExist(*node) 401 if !ok { 402 panic(fmt.Errorf("not support parent type %T in pointer seaching", node)) 403 } 404 baseTypes = append(baseTypes, t) 405 406 *t = util.CleanCType(*t) 407 408 // typedef type 409 var td string = util.CleanCType(*t) 410 for { 411 if te, ok := p.TypedefType[td]; ok { 412 td = util.CleanCType(te) 413 continue 414 } 415 break 416 } 417 // find 418 if types.IsCPointer(*t, p) || types.IsCArray(*t, p) || 419 types.IsCPointer(td, p) || types.IsCArray(td, p) { 420 switch (*node).(type) { 421 case *ast.BinaryOperator, 422 *ast.ImplicitCastExpr, 423 *ast.ParenExpr: 424 undefineIndex = true // is index probably negative 425 // go deeper 426 default: 427 return true 428 } 429 } else { 430 // type is not pointer 431 switch (*node).(type) { 432 case *ast.CallExpr, 433 *ast.ArraySubscriptExpr, 434 *ast.MemberExpr, 435 *ast.UnaryExprOrTypeTraitExpr, // ignore sizeof 436 *ast.CStyleCastExpr: 437 undefineIndex = true // is index probably negative 438 return 439 } 440 } 441 switch (*node).(type) { 442 case *ast.UnaryOperator: 443 baseTypes = baseTypes[:len(baseTypes)-1] 444 return 445 } 446 for i := range (*node).Children() { 447 if searcher(&((*node).Children()[i])) { 448 replacer(&((*node).Children()[i])) 449 } 450 } 451 return false 452 } 453 if searcher(node) { 454 pnt = *node 455 lastNode = node 456 *node = zero 457 counter++ 458 } 459 460 if counter != 1 { 461 err = fmt.Errorf("counter is not 1: %d", counter) 462 return 463 } 464 if pnt == nil { 465 err = fmt.Errorf("pointer is nil") 466 return 467 } 468 469 copyTypes := make([]string, len(baseTypes)) 470 for i := range baseTypes { 471 copyTypes[i] = *(baseTypes[i]) 472 } 473 back = func() { 474 // return back types 475 for i := range baseTypes { 476 *(baseTypes[i]) = copyTypes[i] 477 } 478 // return back node 479 *lastNode = pnt 480 } 481 482 // replace all types to `int` 483 for i := range baseTypes { 484 *baseTypes[i] = `int` 485 } 486 487 value = *node 488 489 return 490 } 491 492 // transpilePointerArith - transpile pointer aripthmetic 493 // Example of using: 494 // *(t + 1) = ... 495 func transpilePointerArith(n *ast.UnaryOperator, p *program.Program) ( 496 expr goast.Expr, eType string, preStmts []goast.Stmt, postStmts []goast.Stmt, err error) { 497 defer func() { 498 if err != nil { 499 err = fmt.Errorf("cannot transpilePointerArith: err = %v", err) 500 } 501 }() 502 503 if n.Operator != "*" { 504 err = fmt.Errorf("not valid operator : %s", n.Operator) 505 return 506 } 507 508 var pnt, value ast.Node 509 var back func() 510 var undefineIndex bool 511 pnt, value, back, undefineIndex, err = pointerParts(&(n.Children()[0]), p) 512 if err != nil { 513 return 514 } 515 _ = undefineIndex 516 517 e, eType, newPre, newPost, err := atomicOperation(value, p) 518 if err != nil { 519 return 520 } 521 preStmts, postStmts = combinePreAndPostStmts(preStmts, postStmts, newPre, newPost) 522 eType = n.Type 523 524 // return all types 525 back() 526 527 arr, arrType, newPre, newPost, err := atomicOperation(pnt, p) 528 if err != nil { 529 return 530 } 531 _ = arrType 532 preStmts, postStmts = combinePreAndPostStmts(preStmts, postStmts, newPre, newPost) 533 534 switch v := pnt.(type) { 535 case *ast.MemberExpr: 536 return &goast.IndexExpr{ 537 X: arr, 538 Index: e, 539 }, eType, preStmts, postStmts, err 540 541 case *ast.DeclRefExpr: 542 return &goast.IndexExpr{ 543 X: util.NewIdent(v.Name), 544 Index: e, 545 }, eType, preStmts, postStmts, err 546 547 case *ast.CStyleCastExpr, *ast.VAArgExpr, *ast.CallExpr, *ast.ArraySubscriptExpr: 548 return &goast.IndexExpr{ 549 X: &goast.ParenExpr{ 550 X: arr, 551 Lparen: 1, 552 }, 553 Index: e, 554 }, eType, preStmts, postStmts, err 555 556 case *ast.UnaryOperator: 557 if memberName, ok := getMemberName(n.Children()[0]); ok { 558 return &goast.IndexExpr{ 559 X: &goast.SelectorExpr{ 560 X: arr, 561 Sel: util.NewIdent(memberName), 562 }, 563 Index: &goast.BasicLit{ 564 Kind: token.INT, 565 Value: "0", 566 }, 567 }, eType, preStmts, postStmts, err 568 } 569 return &goast.IndexExpr{ 570 X: &goast.ParenExpr{ 571 Lparen: 1, 572 X: arr, 573 }, 574 Index: e, 575 }, eType, preStmts, postStmts, err 576 577 } 578 return nil, "", nil, nil, fmt.Errorf("cannot found : %#v", pnt) 579 } 580 581 func transpileUnaryOperator(n *ast.UnaryOperator, p *program.Program) ( 582 _ goast.Expr, theType string, preStmts []goast.Stmt, postStmts []goast.Stmt, err error) { 583 defer func() { 584 if err != nil { 585 err = fmt.Errorf("cannot transpile UnaryOperator: err = %v", err) 586 p.AddMessage(p.GenerateWarningMessage(err, n)) 587 } 588 }() 589 590 operator, err := getTokenForOperator(n.Operator) 591 if err != nil { 592 err = nil 593 return transpileToExpr(n.Children()[0], p, true) 594 } 595 596 switch operator { 597 case token.MUL: // * 598 // Prefix "*" is not a multiplication. 599 // Prefix "*" used for pointer ariphmetic 600 // Example of using: 601 // *(t + 1) = ... 602 return transpilePointerArith(n, p) 603 case token.INC, token.DEC: // ++, -- 604 return transpileUnaryOperatorInc(n, p, operator) 605 case token.NOT: // ! 606 return transpileUnaryOperatorNot(n, p) 607 case token.AND: // & 608 return transpileUnaryOperatorAmpersant(n, p) 609 } 610 611 // Example: 612 // UnaryOperator 'unsigned int' prefix '-' 613 // `-IntegerLiteral 'unsigned int' 1 614 if il, ok := n.Children()[0].(*ast.IntegerLiteral); ok && types.IsCUnsignedType(n.Type) { 615 var value float64 616 value, err = strconv.ParseFloat(il.Value, 64) 617 if err == nil && value > 0 { 618 var resolveType string 619 resolveType, err = types.ResolveType(p, n.Type) 620 if err == nil && resolveType != "" { 621 return util.ConvertToUnsigned(goast.NewIdent(fmt.Sprintf("-%s", il.Value)), resolveType), 622 n.Type, preStmts, postStmts, nil 623 } 624 } 625 err = nil 626 } 627 628 // Example: 629 // UnaryOperator 'int' prefix '-' 630 // `-ImplicitCastExpr 'int' <LValueToRValue> 631 // `-DeclRefExpr 'int' lvalue Var 0x3b42898 'c' 'int' 632 633 // Otherwise handle like a unary operator. 634 e, eType, newPre, newPost, err := transpileToExpr(n.Children()[0], p, false) 635 if err != nil { 636 return nil, "", nil, nil, err 637 } 638 preStmts, postStmts = combinePreAndPostStmts(preStmts, postStmts, newPre, newPost) 639 640 return util.NewUnaryExpr(e, operator), eType, preStmts, postStmts, nil 641 } 642 643 func transpileUnaryExprOrTypeTraitExpr(n *ast.UnaryExprOrTypeTraitExpr, p *program.Program) ( 644 *goast.BasicLit, string, []goast.Stmt, []goast.Stmt, error) { 645 t := n.Type2 646 647 // It will have children if the sizeof() is referencing a variable. 648 // Fortunately clang already has the type in the AST for us. 649 if len(n.Children()) > 0 { 650 if typ, ok := ast.GetTypeIfExist(n.Children()[0]); ok { 651 t = *typ 652 } else { 653 panic(fmt.Sprintf("cannot find first child from: %#v", n.Children()[0])) 654 } 655 } 656 657 sizeInBytes, err := types.SizeOf(p, t) 658 if err != nil { 659 p.AddMessage(p.GenerateWarningMessage(err, n)) 660 err = nil // ignore error 661 } 662 if sizeInBytes == 0 { 663 p.AddMessage(p.GenerateWarningMessage(fmt.Errorf("zero sizeof for '%s'", t), n)) 664 } 665 666 return util.NewIntLit(sizeInBytes), n.Type1, nil, nil, nil 667 } 668 669 func transpileStmtExpr(n *ast.StmtExpr, p *program.Program) ( 670 *goast.CallExpr, string, []goast.Stmt, []goast.Stmt, error) { 671 returnType, err := types.ResolveType(p, n.Type) 672 if err != nil { 673 return nil, "", nil, nil, err 674 } 675 676 body, pre, post, err := transpileCompoundStmt(n.Children()[0].(*ast.CompoundStmt), p) 677 if err != nil { 678 return nil, "", pre, post, err 679 } 680 681 // The body of the StmtExpr is always a CompoundStmt. However, the last 682 // statement needs to be transformed into an explicit return statement. 683 if e, ok := body.List[len(body.List)-1].(*goast.ExprStmt); ok { 684 body.List[len(body.List)-1] = &goast.ReturnStmt{ 685 Results: []goast.Expr{e.X}, 686 } 687 } 688 689 return util.NewFuncClosure(returnType, body.List...), n.Type, pre, post, nil 690 }