Skip to main content

nargo_compiler/
lib.rs

1#![warn(missing_docs)]
2
3use nargo_ir::IRModule;
4use nargo_optimizer::Optimizer;
5use nargo_parser::{Parser, ParserRegistry};
6use nargo_transformer::Transformer;
7pub use nargo_types as types;
8pub use nargo_types::{CompileMode, CompileOptions, Result};
9use std::{
10    collections::HashMap,
11    hash::Hash,
12    sync::{Arc, Mutex, OnceLock},
13    thread,
14    time::Instant,
15};
16
17/// 线程池配置
18pub struct ThreadPoolConfig {
19    /// 线程数
20    pub thread_count: usize,
21}
22
23impl Default for ThreadPoolConfig {
24    fn default() -> Self {
25        Self { thread_count: num_cpus::get() }
26    }
27}
28
29impl Clone for ThreadPoolConfig {
30    fn clone(&self) -> Self {
31        Self { thread_count: self.thread_count }
32    }
33}
34
35/// 编译缓存键
36#[derive(Hash, PartialEq, Eq, Clone)]
37pub struct CompileCacheKey {
38    /// 文件名
39    pub name: String,
40    /// 源代码哈希
41    pub source_hash: u64,
42    /// 编译选项哈希
43    pub options_hash: u64,
44}
45
46/// 编译缓存值
47#[derive(Clone)]
48pub struct CompileCacheValue {
49    /// 编译结果
50    pub result: CompileResult,
51    /// 编译时间
52    pub timestamp: Instant,
53}
54
55/// Nargo 框架的便捷编译函数
56///
57/// 这个函数封装了 Compiler 的实例化和默认配置,适合简单的单文件编译场景。
58pub fn compile(name: &str, source: &str) -> Result<CompileResult> {
59    // 获取或创建编译器缓存
60    let compiler_cache = COMPILER_CACHE.get_or_init(|| Mutex::new(HashMap::new()));
61
62    // 从缓存中获取编译器实例,如果没有则创建新的
63    let mut cache = compiler_cache.lock().unwrap();
64    let compiler = cache.entry(name.to_string()).or_insert_with(|| Compiler::new());
65
66    // 使用编译器实例
67    compiler.compile(name, source)
68}
69
70/// 编译 Vmz 格式的文件
71///
72/// # Arguments
73///
74/// * `name` - 文件名
75/// * `source` - Vmz 源文件内容
76///
77/// # Returns
78///
79/// 编译结果,包含生成的代码、CSS、HTML 和 WASM
80pub fn compile_vmz(name: &str, source: &str) -> Result<CompileResult> {
81    // 获取或创建编译器缓存
82    let compiler_cache = COMPILER_CACHE.get_or_init(|| Mutex::new(HashMap::new()));
83
84    // 从缓存中获取编译器实例,如果没有则创建新的
85    let mut cache = compiler_cache.lock().unwrap();
86    let compiler = cache.entry(name.to_string()).or_insert_with(|| Compiler::new());
87
88    // 使用编译器实例
89    compiler.compile(name, source)
90}
91
92/// 使用自定义选项编译 Nargo 源代码
93pub fn compile_with_options(name: &str, source: &str, options: CompileOptions) -> Result<CompileResult> {
94    // 获取或创建编译器缓存
95    let compiler_cache = COMPILER_CACHE.get_or_init(|| Mutex::new(HashMap::new()));
96
97    // 从缓存中获取编译器实例,如果没有则创建新的
98    let mut cache = compiler_cache.lock().unwrap();
99    let compiler = cache.entry(name.to_string()).or_insert_with(|| Compiler::new());
100
101    // 使用编译器实例
102    compiler.compile_with_options(name, source, options)
103}
104
105/// Nargo 核心 API 的统一导出
106pub mod prelude {
107    pub use crate::{compile, compile_vmz, compile_with_options, CompileMode, CompileOptions, CompileResult, Compiler};
108    pub use nargo_types::{Error, ErrorKind, Position, Result, Span};
109}
110
111use serde::Serialize;
112
113pub mod adapter;
114pub mod codegen;
115
116/// 编译结果
117#[derive(Serialize, Clone)]
118pub struct CompileResult {
119    /// 生成的 JavaScript 代码
120    pub code: String,
121    /// 生成的 CSS 代码
122    pub css: String,
123    /// 生成的 HTML 代码
124    pub html: String,
125    /// 生成的 WASM 代码(WAT 格式,用于 playground)
126    pub wasm: String,
127    /// 编译时间(毫秒)
128    pub compile_time_ms: u64,
129}
130
131/// 编译阶段
132#[derive(Debug, Clone, Serialize, Eq, Hash, PartialEq)]
133pub enum CompileStage {
134    /// 解析阶段
135    Parse,
136    /// 转换阶段
137    Transform,
138    /// 优化阶段
139    Optimize,
140    /// 代码生成阶段
141    CodeGen,
142}
143
144/// 编译统计信息
145#[derive(Debug, Clone, Serialize)]
146pub struct CompileStats {
147    /// 各阶段耗时(毫秒)
148    pub stage_times: std::collections::HashMap<CompileStage, u64>,
149    /// 总编译时间(毫秒)
150    pub total_time: u64,
151    /// 源代码大小(字节)
152    pub source_size: usize,
153    /// 生成代码大小(字节)
154    pub output_size: usize,
155}
156
157// 静态缓存ParserRegistry实例,避免每次创建编译器时重复注册解析器
158static REGISTRY_CACHE: OnceLock<Arc<ParserRegistry>> = OnceLock::new();
159
160// 静态缓存编译器实例,避免重复创建
161type CompilerCache = Mutex<HashMap<String, Compiler>>;
162static COMPILER_CACHE: OnceLock<CompilerCache> = OnceLock::new();
163
164/// Nargo 编译器
165///
166/// 负责协调各个编译阶段,集成 nargo-parser、nargo-transformer 等组件,并提供编译配置和优化选项。
167pub struct Compiler {
168    /// 解析器注册表
169    pub registry: Arc<ParserRegistry>,
170    /// 上次生成的 CSS
171    pub last_css: String,
172    /// 转换器
173    pub transformer: Transformer,
174    /// 编译统计信息
175    pub stats: Option<CompileStats>,
176    /// 线程池配置
177    pub thread_pool_config: ThreadPoolConfig,
178    /// 编译缓存
179    pub compile_cache: HashMap<CompileCacheKey, CompileCacheValue>,
180    /// 缓存大小限制(默认 1000)
181    pub cache_size_limit: usize,
182    /// 热编译路径快速缓存
183    pub hot_cache: Option<(CompileCacheKey, CompileResult)>,
184}
185
186impl Default for Compiler {
187    fn default() -> Self {
188        Self::new()
189    }
190}
191
192impl Clone for Compiler {
193    fn clone(&self) -> Self {
194        Self { registry: self.registry.clone(), last_css: self.last_css.clone(), transformer: self.transformer.clone(), stats: self.stats.clone(), thread_pool_config: self.thread_pool_config.clone(), compile_cache: self.compile_cache.clone(), cache_size_limit: self.cache_size_limit, hot_cache: self.hot_cache.clone() }
195    }
196}
197
198impl Compiler {
199    /// 创建新的编译器实例
200    pub fn new() -> Self {
201        // 获取或创建缓存的ParserRegistry实例
202        let registry = REGISTRY_CACHE.get_or_init(|| {
203            let registry = ParserRegistry::new();
204
205            // Register default parsers
206            // 简化实现,实际需要根据 nargo_parser 的 API 进行调整
207            // 由于 ParserRegistry 期望特定类型的解析器,我们暂时不注册任何解析器
208            // 实际使用时需要根据 nargo_parser 的 API 提供正确的解析器实现
209
210            Arc::new(registry)
211        });
212
213        Self { registry: registry.clone(), last_css: String::new(), transformer: Transformer::new(), stats: None, thread_pool_config: ThreadPoolConfig::default(), compile_cache: HashMap::with_capacity(100), cache_size_limit: 1000, hot_cache: None }
214    }
215
216    /// 设置缓存大小限制
217    ///
218    /// # Arguments
219    ///
220    /// * `limit` - 缓存大小限制
221    ///
222    /// # Returns
223    ///
224    /// 返回编译器实例,便于链式调用
225    pub fn with_cache_size_limit(mut self, limit: usize) -> Self {
226        self.cache_size_limit = limit;
227        self
228    }
229
230    /// 设置线程池配置
231    ///
232    /// # Arguments
233    ///
234    /// * `config` - 线程池配置
235    ///
236    /// # Returns
237    ///
238    /// 返回编译器实例,便于链式调用
239    pub fn with_thread_pool_config(mut self, config: ThreadPoolConfig) -> Self {
240        self.thread_pool_config = config;
241        self
242    }
243
244    /// 编译 Nargo 源代码
245    ///
246    /// # Arguments
247    ///
248    /// * `name` - 组件名称
249    /// * `source` - Nargo 源代码
250    ///
251    /// # Returns
252    ///
253    /// 编译结果
254    pub fn compile(&mut self, name: &str, source: &str) -> Result<CompileResult> {
255        self.compile_with_options(name, source, CompileOptions::default())
256    }
257
258    /// 使用自定义选项编译 Nargo 源代码
259    ///
260    /// # Arguments
261    ///
262    /// * `name` - 组件名称
263    /// * `source` - Nargo 源代码
264    /// * `options` - 编译选项
265    ///
266    /// # Returns
267    ///
268    /// 编译结果
269    pub fn compile_with_options(&mut self, name: &str, source: &str, mut options: CompileOptions) -> Result<CompileResult> {
270        // 快速路径:使用更快的哈希计算
271        let source_hash = fast_hash(source);
272        let options_hash = fast_hash_options(&options);
273        let cache_key = CompileCacheKey { name: name.to_string(), source_hash, options_hash };
274
275        // 热缓存快速检查(用于增量构建)
276        if let Some((ref hot_key, ref hot_result)) = self.hot_cache {
277            if hot_key == &cache_key {
278                // 热缓存命中,极快返回
279                self.stats = Some(CompileStats { stage_times: HashMap::new(), total_time: 0, source_size: source.len(), output_size: hot_result.code.len() + hot_result.css.len() });
280                return Ok(hot_result.clone());
281            }
282        }
283
284        // 检查主缓存
285        if let Some(cache_value) = self.compile_cache.get(&cache_key) {
286            // 缓存命中,更新热缓存
287            self.hot_cache = Some((cache_key.clone(), cache_value.result.clone()));
288            self.stats = Some(CompileStats { stage_times: HashMap::new(), total_time: 0, source_size: source.len(), output_size: cache_value.result.code.len() + cache_value.result.css.len() });
289            return Ok(cache_value.result.clone());
290        }
291
292        // 缓存未命中,执行编译
293        let start_time = Instant::now();
294
295        // 预分配空间,减少内存分配
296        let mut stage_times = std::collections::HashMap::with_capacity(4);
297
298        // 1. 解析源代码到 IR
299        let parse_start = Instant::now();
300        let _ir = self.compile_to_ir(name, source, &mut options)?;
301        stage_times.insert(CompileStage::Parse, parse_start.elapsed().as_millis() as u64);
302
303        // 2. 代码生成
304        let codegen_start = Instant::now();
305        let code = String::new(); // 简单的 JavaScript 代码生成,实际的代码生成已迁移到 nargo-bundler
306
307        // 生成其他目标代码(用于 playground/检查)
308        let html = String::new(); // HtmlBackend 已迁移到 nargo-bundler
309        let wasm = String::new(); // WasmBackend 已移除
310        stage_times.insert(CompileStage::CodeGen, codegen_start.elapsed().as_millis() as u64);
311
312        // 计算总编译时间
313        let total_time = start_time.elapsed().as_millis() as u64;
314
315        // 生成编译统计信息
316        self.stats = Some(CompileStats { stage_times, total_time, source_size: source.len(), output_size: code.len() + self.last_css.len() });
317
318        // 创建编译结果
319        let result = CompileResult { code, css: std::mem::take(&mut self.last_css), html, wasm, compile_time_ms: total_time };
320
321        // 将结果存入热缓存和主缓存
322        self.hot_cache = Some((cache_key.clone(), result.clone()));
323        self.compile_cache.insert(cache_key, CompileCacheValue { result: result.clone(), timestamp: Instant::now() });
324
325        // 延迟缓存清理,减少性能开销
326        if self.compile_cache.len() > self.cache_size_limit * 2 {
327            self.cleanup_cache();
328        }
329
330        Ok(result)
331    }
332
333    /// 编译源代码到 IR 模块
334    ///
335    /// # Arguments
336    ///
337    /// * `name` - 组件名称
338    /// * `source` - Nargo 源代码
339    /// * `options` - 编译选项
340    ///
341    /// # Returns
342    ///
343    /// IR 模块
344    pub fn compile_to_ir(&mut self, name: &str, source: &str, options: &mut CompileOptions) -> Result<IRModule> {
345        // 1. 解析源代码到 IR
346        let mut parser = Parser::new(name.to_string(), source, self.registry.clone());
347        let mut ir = parser.parse_all()?;
348
349        // 2. 转换脚本(VOC TypeScript 适配器)
350        let ts_adapter = adapter::TsAdapter::new();
351        ts_adapter.transform(&mut ir)?;
352
353        // 3. 重新分析脚本以更新转换后的元数据
354        let analyzer = nargo_script_analyzer::ScriptAnalyzer::new();
355        if let Some(script) = &ir.script {
356            if let Ok(meta) = analyzer.analyze(script) {
357                ir.script_meta = Some(meta.to_nargo_value());
358            }
359        }
360
361        // 4. 优化和转换 IR
362        let mut optimizer = Optimizer::new();
363
364        // 处理作用域 ID
365        let has_scoped_style = ir.styles.iter().any(|s| s.scoped);
366        if has_scoped_style && options.scope_id.is_none() {
367            options.scope_id = Some(optimizer.generate_scope_id(name));
368        }
369
370        // 通过优化器应用所有转换
371        optimizer.optimize(&mut ir, options.i18n_locale.as_deref(), options.is_prod);
372
373        // 应用 scoped CSS 转换(如果需要)
374        if let Some(scope_id) = &options.scope_id {
375            optimizer.apply_scope_id(&mut ir, scope_id);
376        }
377
378        // 处理样式(Tailwind/实用 CSS)
379        optimizer.process_styles(&ir)?;
380
381        // 直接获取CSS,避免不必要的克隆
382        self.last_css = optimizer.get_css();
383
384        Ok(ir)
385    }
386
387    /// 获取上次生成的 CSS
388    ///
389    /// # Returns
390    ///
391    /// CSS 代码
392    pub fn get_css(&self) -> String {
393        self.last_css.clone()
394    }
395
396    /// 获取编译统计信息
397    ///
398    /// # Returns
399    ///
400    /// 编译统计信息
401    pub fn get_stats(&self) -> Option<&CompileStats> {
402        self.stats.as_ref()
403    }
404
405    /// 重置编译器状态
406    pub fn reset(&mut self) {
407        self.last_css.clear();
408        self.transformer.clear_logs();
409        self.stats = None;
410        self.thread_pool_config = ThreadPoolConfig::default();
411        self.compile_cache.clear();
412        self.cache_size_limit = 1000;
413        self.hot_cache = None;
414    }
415
416    /// 计算字符串的哈希值(保留向后兼容性)
417    fn hash_string(&self, s: &str) -> u64 {
418        fast_hash(s)
419    }
420
421    /// 计算编译选项的哈希值(保留向后兼容性)
422    fn hash_options(&self, options: &CompileOptions) -> u64 {
423        fast_hash_options(options)
424    }
425
426    /// 清理缓存,保持缓存大小在限制范围内
427    fn cleanup_cache(&mut self) {
428        if self.compile_cache.len() > self.cache_size_limit {
429            // 按时间戳排序,移除最旧的缓存项
430            let mut entries: Vec<(CompileCacheKey, CompileCacheValue)> = self.compile_cache.drain().collect();
431            entries.sort_by(|a, b| a.1.timestamp.cmp(&b.1.timestamp));
432            let keep_count = self.cache_size_limit;
433            let to_keep = entries.into_iter().take(keep_count).collect();
434            self.compile_cache = to_keep;
435        }
436    }
437
438    /// 并行编译多个 Nargo 源代码文件
439    ///
440    /// # Arguments
441    ///
442    /// * `files` - 文件名和源代码的映射
443    /// * `options` - 编译选项
444    ///
445    /// # Returns
446    ///
447    /// 编译结果的映射,键为文件名,值为编译结果
448    pub fn compile_parallel(&self, files: &HashMap<String, String>, options: CompileOptions) -> Result<HashMap<String, CompileResult>> {
449        let thread_count = self.thread_pool_config.thread_count;
450        let files: Vec<(String, String)> = files.iter().map(|(k, v)| (k.clone(), v.clone())).collect();
451        let chunk_size = (files.len() + thread_count - 1) / thread_count;
452
453        // 预分配足够的空间,避免运行时扩容
454        let mut handles = Vec::with_capacity(thread_count);
455
456        for chunk in files.chunks(chunk_size) {
457            // 只克隆当前块的数据,避免克隆整个文件列表
458            let chunk_clone: Vec<(String, String)> = chunk.to_vec();
459            let options_clone = options.clone();
460            let compiler_clone = self.clone();
461
462            let handle = thread::spawn(move || {
463                // 预分配结果空间
464                let mut results = HashMap::with_capacity(chunk_clone.len());
465                for (name, source) in chunk_clone {
466                    let mut compiler = compiler_clone.clone();
467                    match compiler.compile_with_options(&name, &source, options_clone.clone()) {
468                        Ok(result) => {
469                            results.insert(name, result);
470                        }
471                        Err(e) => {
472                            // 处理错误
473                            eprintln!("编译文件 {} 时出错: {:?}", name, e);
474                        }
475                    }
476                }
477                results
478            });
479
480            handles.push(handle);
481        }
482
483        // 预分配结果空间
484        let mut all_results = HashMap::with_capacity(files.len());
485        for handle in handles {
486            if let Ok(results) = handle.join() {
487                all_results.extend(results);
488            }
489        }
490
491        Ok(all_results)
492    }
493}
494
495/// 快速字符串哈希函数
496#[inline(always)]
497fn fast_hash(s: &str) -> u64 {
498    // 使用更高效的哈希算法
499    let mut hash = 0xcbf29ce484222325u64;
500    let prime = 0x100000001b3u64;
501
502    for byte in s.bytes() {
503        hash ^= byte as u64;
504        hash = hash.wrapping_mul(prime);
505    }
506
507    hash
508}
509
510/// 快速编译选项哈希函数
511#[inline(always)]
512fn fast_hash_options(options: &CompileOptions) -> u64 {
513    let mut hash = 0xcbf29ce484222325u64;
514    let prime = 0x100000001b3u64;
515
516    // 哈希 mode
517    hash ^= options.mode as u64;
518    hash = hash.wrapping_mul(prime);
519
520    // 哈希 is_prod
521    hash ^= options.is_prod as u64;
522    hash = hash.wrapping_mul(prime);
523
524    // 哈希 scope_id
525    if let Some(scope_id) = &options.scope_id {
526        for byte in scope_id.bytes() {
527            hash ^= byte as u64;
528            hash = hash.wrapping_mul(prime);
529        }
530    }
531
532    // 哈希 i18n_locale
533    if let Some(locale) = &options.i18n_locale {
534        for byte in locale.bytes() {
535            hash ^= byte as u64;
536            hash = hash.wrapping_mul(prime);
537        }
538    }
539
540    hash
541}