1+ import ts , { SignatureDeclaration } from "typescript" ;
2+ import { TransformationContext } from "../context" ;
3+ import { InlineFunctionInfo } from "../context" ;
4+ import * as lua from "../../LuaAST" ;
5+ import { isMultiFunctionCall } from "../visitors/language-extensions/multi" ;
6+ import { AnnotationKind , getNodeAnnotations , getSymbolAnnotations } from "./annotations" ;
7+
8+ interface InlineBodyResult {
9+ paramAssignments : lua . Statement [ ] ;
10+ bodyStatements : lua . Statement [ ] ;
11+ returnExpressions : lua . Expression [ ] ;
12+ hasMultiReturn : boolean ;
13+ }
14+
15+ // AST transformer to substitute parameter identifiers with temp variables
16+ function createParameterSubstitutionTransformer (
17+ paramReplacements : Map < string , string >
18+ ) : ts . TransformerFactory < ts . Node > {
19+ return ( context : ts . TransformationContext ) => {
20+ const visit = ( node : ts . Node ) : ts . Node => {
21+ // Replace identifier if it matches a parameter
22+ if ( ts . isIdentifier ( node ) ) {
23+ const replacementName = paramReplacements . get ( node . text ) ;
24+ if ( replacementName ) {
25+ return ts . factory . createIdentifier ( replacementName ) ;
26+ }
27+ }
28+
29+ // Recursively visit children
30+ return ts . visitEachChild ( node , visit , context ) ;
31+ } ;
32+
33+ return visit ;
34+ } ;
35+ }
36+ export function prepareInlineBody (
37+ context : TransformationContext ,
38+ inlineInfo : InlineFunctionInfo ,
39+ args : ts . NodeArray < ts . Expression >
40+ ) : InlineBodyResult {
41+ if ( inlineInfo . isProcessing ) {
42+ throw new Error ( "Recursive inline call detected (should be caught earlier)" ) ;
43+ }
44+
45+ inlineInfo . isProcessing = true ;
46+ try {
47+ const { body, parameters } = inlineInfo ;
48+ const paramReplacements = new Map < string , string > ( ) ;
49+ const paramAssignments : lua . Statement [ ] = [ ] ;
50+
51+ let argIndex = 0 ;
52+ for ( const param of parameters ) {
53+ if ( ts . isIdentifier ( param . name ) && param . name . text !== "this" ) {
54+ const paramName = param . name . text ;
55+ const tempName = context . createTempName ( paramName ) ;
56+ paramReplacements . set ( paramName , tempName ) ;
57+
58+ const arg = argIndex < args . length ? args [ argIndex ] : undefined ;
59+ const transformedArg = arg ? context . transformExpression ( arg ) : lua . createNilLiteral ( ) ;
60+ paramAssignments . push (
61+ lua . createVariableDeclarationStatement (
62+ lua . createIdentifier ( tempName ) ,
63+ transformedArg
64+ )
65+ ) ;
66+ argIndex ++ ;
67+ }
68+ }
69+
70+ // Substitute in body
71+ const substitutedBody = ts . transform ( body , [
72+ createParameterSubstitutionTransformer ( paramReplacements )
73+ ] ) . transformed [ 0 ] as ts . ConciseBody ;
74+
75+ // Extract body statements and return expressions
76+ let bodyStatements : lua . Statement [ ] = [ ] ;
77+ let returnExpressions : lua . Expression [ ] = [ ] ;
78+ let hasMultiReturn = false ;
79+
80+ if ( ! ts . isBlock ( substitutedBody ) ) {
81+ returnExpressions = [ context . transformExpression ( substitutedBody ) ] ;
82+ } else {
83+ bodyStatements = context . transformStatements (
84+ substitutedBody . statements . filter ( s => ! ts . isReturnStatement ( s ) )
85+ ) ;
86+ const returnStmt = substitutedBody . statements . find ( ts . isReturnStatement ) ;
87+ const returnExpr = returnStmt ?. expression ;
88+ if ( returnExpr ) {
89+ const unwrappedExpr = ts . skipOuterExpressions ( returnExpr , ts . OuterExpressionKinds . Assertions ) ;
90+ if ( ts . isCallExpression ( unwrappedExpr ) && isMultiFunctionCall ( context , unwrappedExpr ) ) {
91+ hasMultiReturn = true ;
92+ returnExpressions = unwrappedExpr . arguments . map ( arg => context . transformExpression ( arg ) ) ;
93+ } else {
94+ returnExpressions = [ context . transformExpression ( returnExpr ) ] ;
95+ }
96+ }
97+ }
98+
99+ return { paramAssignments, bodyStatements, returnExpressions, hasMultiReturn } ;
100+ } finally {
101+ inlineInfo . isProcessing = false ;
102+ }
103+ }
104+
105+ export function embedInlineResult (
106+ context : TransformationContext ,
107+ paramAndBodyStmts : lua . Statement [ ] ,
108+ returnExprs : lua . Expression [ ] ,
109+ hasMulti : boolean ,
110+ target ?: { // если target задан, то результат присваивается ему
111+ kind : 'variables' ; // может быть массив или одна переменная
112+ vars : lua . Identifier [ ] ;
113+ } ,
114+ isReturnContext ?: boolean // true, если вызов был внутри return
115+ ) : lua . Expression {
116+ const allStmts = [ ...paramAndBodyStmts ] ;
117+
118+ if ( isReturnContext ) {
119+ // В контексте return: просто вставляем return в do...end
120+ allStmts . push ( lua . createReturnStatement ( hasMulti ? returnExprs : returnExprs ) ) ;
121+ context . addPrecedingStatements ( [ lua . createDoStatement ( allStmts ) ] ) ;
122+ return lua . createNilLiteral ( ) ; // сам return уже внутри
123+ }
124+
125+ if ( target ) {
126+ if ( hasMulti ) {
127+ allStmts . push ( lua . createAssignmentStatement ( target . vars , returnExprs ) ) ;
128+ } else {
129+ allStmts . push ( lua . createAssignmentStatement ( target . vars [ 0 ] , returnExprs [ 0 ] ) ) ;
130+ }
131+ if ( target . vars . length > 1 ) {
132+ // Для деструктуризации возвращаем nil, объявление переменных снаружи
133+ context . addPrecedingStatements ( [ lua . createDoStatement ( allStmts ) ] ) ;
134+ return lua . createNilLiteral ( ) ;
135+ } else {
136+ // Для одной переменной: возвращаем её, чтобы использовать как expression
137+ context . addPrecedingStatements ( [ lua . createDoStatement ( allStmts ) ] ) ;
138+ return target . vars [ 0 ] ;
139+ }
140+ }
141+
142+ // Контекст выражения (не присваивание)
143+ const tempVar = lua . createIdentifier ( context . createTempName ( "inline_result" ) ) ;
144+ allStmts . push ( lua . createAssignmentStatement ( tempVar , hasMulti ? returnExprs [ 0 ] : returnExprs [ 0 ] ) ) ;
145+ context . addPrecedingStatements ( [ lua . createDoStatement ( allStmts ) ] ) ;
146+ return tempVar ;
147+ }
148+
149+ export function createInlineAssignment (
150+ paramAndBodyStmts : lua . Statement [ ] ,
151+ returnExprs : lua . Expression [ ] ,
152+ hasMulti : boolean ,
153+ targetVars : lua . Identifier [ ]
154+ ) : lua . DoStatement {
155+ const allStmts = [ ...paramAndBodyStmts ] ;
156+ if ( hasMulti ) {
157+ allStmts . push ( lua . createAssignmentStatement ( targetVars , returnExprs ) ) ;
158+ } else {
159+ allStmts . push ( lua . createAssignmentStatement ( targetVars [ 0 ] , returnExprs [ 0 ] ) ) ;
160+ }
161+ return lua . createDoStatement ( allStmts ) ;
162+ }
163+
164+ export function isInlineFunctionCandidate (
165+ context : TransformationContext ,
166+ node : SignatureDeclaration
167+ ) : boolean {
168+ // Check for @inline annotation
169+ const symbol = node . name ? context . checker . getSymbolAtLocation ( node . name ) : undefined ;
170+ if ( symbol ) {
171+ const annotations = getSymbolAnnotations ( symbol ) ;
172+ if ( annotations . has ( AnnotationKind . Inline ) ) {
173+ return true ;
174+ }
175+ }
176+
177+ // Also check node annotations (for cases where symbol might not be available)
178+ const nodeAnnotations = getNodeAnnotations ( node ) ;
179+ return nodeAnnotations . has ( AnnotationKind . Inline ) ;
180+ }
0 commit comments