@@ -2,6 +2,7 @@ import * as path from "path";
22import { EmitHost } from "./transpilation" ;
33import * as lua from "./LuaAST" ;
44import { LuaTarget } from "./CompilerOptions" ;
5+ import { getOrUpdate } from "./utils" ;
56
67export enum LuaLibFeature {
78 ArrayConcat = "ArrayConcat" ,
@@ -109,6 +110,7 @@ export interface LuaLibFeatureInfo {
109110 dependencies ?: LuaLibFeature [ ] ;
110111 exports : string [ ] ;
111112}
113+
112114export type LuaLibModulesInfo = Record < LuaLibFeature , LuaLibFeatureInfo > ;
113115
114116export function resolveLuaLibDir ( luaTarget : LuaTarget ) {
@@ -117,27 +119,55 @@ export function resolveLuaLibDir(luaTarget: LuaTarget) {
117119}
118120
119121export const luaLibModulesInfoFileName = "lualib_module_info.json" ;
120- const luaLibModulesInfo = new Map < string , LuaLibModulesInfo > ( ) ;
122+ const luaLibModulesInfo = new Map < LuaTarget , LuaLibModulesInfo > ( ) ;
123+
121124export function getLuaLibModulesInfo ( luaTarget : LuaTarget , emitHost : EmitHost ) : LuaLibModulesInfo {
122- const lualibPath = path . join ( resolveLuaLibDir ( luaTarget ) , luaLibModulesInfoFileName ) ;
123- if ( ! luaLibModulesInfo . has ( lualibPath ) ) {
125+ if ( ! luaLibModulesInfo . has ( luaTarget ) ) {
126+ const lualibPath = path . join ( resolveLuaLibDir ( luaTarget ) , luaLibModulesInfoFileName ) ;
124127 const result = emitHost . readFile ( lualibPath ) ;
125128 if ( result !== undefined ) {
126- luaLibModulesInfo . set ( lualibPath , JSON . parse ( result ) as LuaLibModulesInfo ) ;
129+ luaLibModulesInfo . set ( luaTarget , JSON . parse ( result ) as LuaLibModulesInfo ) ;
127130 } else {
128131 throw new Error ( `Could not load lualib dependencies from '${ lualibPath } '` ) ;
129132 }
130133 }
131- return luaLibModulesInfo . get ( lualibPath ) as LuaLibModulesInfo ;
134+ return luaLibModulesInfo . get ( luaTarget ) ! ;
135+ }
136+
137+ // This caches the names of lualib exports to their LuaLibFeature, avoiding a linear search for every lookup
138+ const lualibExportToFeature = new Map < LuaTarget , ReadonlyMap < string , LuaLibFeature > > ( ) ;
139+
140+ export function getLuaLibExportToFeatureMap (
141+ luaTarget : LuaTarget ,
142+ emitHost : EmitHost
143+ ) : ReadonlyMap < string , LuaLibFeature > {
144+ if ( ! lualibExportToFeature . has ( luaTarget ) ) {
145+ const luaLibModulesInfo = getLuaLibModulesInfo ( luaTarget , emitHost ) ;
146+ const map = new Map < string , LuaLibFeature > ( ) ;
147+ for ( const [ feature , info ] of Object . entries ( luaLibModulesInfo ) ) {
148+ for ( const exportName of info . exports ) {
149+ map . set ( exportName , feature as LuaLibFeature ) ;
150+ }
151+ }
152+ lualibExportToFeature . set ( luaTarget , map ) ;
153+ }
154+
155+ return lualibExportToFeature . get ( luaTarget ) ! ;
132156}
133157
158+ const lualibFeatureCache = new Map < LuaTarget , Map < LuaLibFeature , string > > ( ) ;
159+
134160export function readLuaLibFeature ( feature : LuaLibFeature , luaTarget : LuaTarget , emitHost : EmitHost ) : string {
135- const featurePath = path . join ( resolveLuaLibDir ( luaTarget ) , `${ feature } .lua` ) ;
136- const luaLibFeature = emitHost . readFile ( featurePath ) ;
137- if ( luaLibFeature === undefined ) {
138- throw new Error ( `Could not load lualib feature from '${ featurePath } '` ) ;
161+ const featureMap = getOrUpdate ( lualibFeatureCache , luaTarget , ( ) => new Map ( ) ) ;
162+ if ( ! featureMap . has ( feature ) ) {
163+ const featurePath = path . join ( resolveLuaLibDir ( luaTarget ) , `${ feature } .lua` ) ;
164+ const luaLibFeature = emitHost . readFile ( featurePath ) ;
165+ if ( luaLibFeature === undefined ) {
166+ throw new Error ( `Could not load lualib feature from '${ featurePath } '` ) ;
167+ }
168+ featureMap . set ( feature , luaLibFeature ) ;
139169 }
140- return luaLibFeature ;
170+ return featureMap . get ( feature ) ! ;
141171}
142172
143173export function resolveRecursiveLualibFeatures (
@@ -173,14 +203,9 @@ export function loadInlineLualibFeatures(
173203 luaTarget : LuaTarget ,
174204 emitHost : EmitHost
175205) : string {
176- let result = "" ;
177-
178- for ( const feature of resolveRecursiveLualibFeatures ( features , luaTarget , emitHost ) ) {
179- const luaLibFeature = readLuaLibFeature ( feature , luaTarget , emitHost ) ;
180- result += luaLibFeature + "\n" ;
181- }
182-
183- return result ;
206+ return resolveRecursiveLualibFeatures ( features , luaTarget , emitHost )
207+ . map ( feature => readLuaLibFeature ( feature , luaTarget , emitHost ) )
208+ . join ( "\n" ) ;
184209}
185210
186211export function loadImportedLualibFeatures (
@@ -191,13 +216,13 @@ export function loadImportedLualibFeatures(
191216 const luaLibModuleInfo = getLuaLibModulesInfo ( luaTarget , emitHost ) ;
192217
193218 const imports = Array . from ( features ) . flatMap ( feature => luaLibModuleInfo [ feature ] . exports ) ;
219+ if ( imports . length === 0 ) {
220+ return [ ] ;
221+ }
194222
195223 const requireCall = lua . createCallExpression ( lua . createIdentifier ( "require" ) , [
196224 lua . createStringLiteral ( "lualib_bundle" ) ,
197225 ] ) ;
198- if ( imports . length === 0 ) {
199- return [ ] ;
200- }
201226
202227 const luaLibId = lua . createIdentifier ( "____lualib" ) ;
203228 const importStatement = lua . createVariableDeclarationStatement ( luaLibId , requireCall ) ;
@@ -215,6 +240,7 @@ export function loadImportedLualibFeatures(
215240}
216241
217242const luaLibBundleContent = new Map < string , string > ( ) ;
243+
218244export function getLuaLibBundle ( luaTarget : LuaTarget , emitHost : EmitHost ) : string {
219245 const lualibPath = path . join ( resolveLuaLibDir ( luaTarget ) , "lualib_bundle.lua" ) ;
220246 if ( ! luaLibBundleContent . has ( lualibPath ) ) {
@@ -228,3 +254,42 @@ export function getLuaLibBundle(luaTarget: LuaTarget, emitHost: EmitHost): strin
228254
229255 return luaLibBundleContent . get ( lualibPath ) as string ;
230256}
257+
258+ export function getLualibBundleReturn ( exportedValues : string [ ] ) : string {
259+ return `\nreturn {\n${ exportedValues . map ( exportName => ` ${ exportName } = ${ exportName } ` ) . join ( ",\n" ) } \n}\n` ;
260+ }
261+
262+ export function buildMinimalLualibBundle (
263+ features : Iterable < LuaLibFeature > ,
264+ luaTarget : LuaTarget ,
265+ emitHost : EmitHost
266+ ) : string {
267+ const code = loadInlineLualibFeatures ( features , luaTarget , emitHost ) ;
268+ const moduleInfo = getLuaLibModulesInfo ( luaTarget , emitHost ) ;
269+ const exports = Array . from ( features ) . flatMap ( feature => moduleInfo [ feature ] . exports ) ;
270+
271+ return code + getLualibBundleReturn ( exports ) ;
272+ }
273+
274+ export function findUsedLualibFeatures (
275+ luaTarget : LuaTarget ,
276+ emitHost : EmitHost ,
277+ luaContents : string [ ]
278+ ) : Set < LuaLibFeature > {
279+ const features = new Set < LuaLibFeature > ( ) ;
280+ const exportToFeatureMap = getLuaLibExportToFeatureMap ( luaTarget , emitHost ) ;
281+
282+ for ( const lua of luaContents ) {
283+ const regex = / ^ l o c a l ( \w + ) = _ _ _ _ l u a l i b \. ( \w + ) $ / gm;
284+ while ( true ) {
285+ const match = regex . exec ( lua ) ;
286+ if ( ! match ) break ;
287+ const [ , localName , exportName ] = match ;
288+ if ( localName !== exportName ) continue ;
289+ const feature = exportToFeatureMap . get ( exportName ) ;
290+ if ( feature ) features . add ( feature ) ;
291+ }
292+ }
293+
294+ return features ;
295+ }
0 commit comments