1use std::collections::{HashMap, HashSet, VecDeque};
6
7#[derive(Debug, Clone, PartialEq)]
9pub enum TsTemplatePart {
10 Text(std::string::String),
12 Expr(TsExpr),
14}
15#[allow(dead_code)]
16pub struct TSPassRegistry {
17 pub(super) configs: Vec<TSPassConfig>,
18 pub(super) stats: std::collections::HashMap<String, TSPassStats>,
19}
20impl TSPassRegistry {
21 #[allow(dead_code)]
22 pub fn new() -> Self {
23 TSPassRegistry {
24 configs: Vec::new(),
25 stats: std::collections::HashMap::new(),
26 }
27 }
28 #[allow(dead_code)]
29 pub fn register(&mut self, config: TSPassConfig) {
30 self.stats
31 .insert(config.pass_name.clone(), TSPassStats::new());
32 self.configs.push(config);
33 }
34 #[allow(dead_code)]
35 pub fn enabled_passes(&self) -> Vec<&TSPassConfig> {
36 self.configs.iter().filter(|c| c.enabled).collect()
37 }
38 #[allow(dead_code)]
39 pub fn get_stats(&self, name: &str) -> Option<&TSPassStats> {
40 self.stats.get(name)
41 }
42 #[allow(dead_code)]
43 pub fn total_passes(&self) -> usize {
44 self.configs.len()
45 }
46 #[allow(dead_code)]
47 pub fn enabled_count(&self) -> usize {
48 self.enabled_passes().len()
49 }
50 #[allow(dead_code)]
51 pub fn update_stats(&mut self, name: &str, changes: u64, time_ms: u64, iter: u32) {
52 if let Some(stats) = self.stats.get_mut(name) {
53 stats.record_run(changes, time_ms, iter);
54 }
55 }
56}
57#[derive(Debug, Clone)]
59pub struct TsClass {
60 pub name: std::string::String,
61 pub extends: Option<std::string::String>,
62 pub implements: Vec<std::string::String>,
63 pub fields: Vec<TsClassField>,
64 pub methods: Vec<TsClassMethod>,
65 pub type_params: Vec<std::string::String>,
66 pub is_exported: bool,
67}
68#[derive(Debug, Clone)]
70pub struct TsExtDiagMsg {
71 pub severity: TsExtDiagSeverity,
72 pub pass: String,
73 pub message: String,
74}
75impl TsExtDiagMsg {
76 pub fn error(pass: impl Into<String>, msg: impl Into<String>) -> Self {
77 TsExtDiagMsg {
78 severity: TsExtDiagSeverity::Error,
79 pass: pass.into(),
80 message: msg.into(),
81 }
82 }
83 pub fn warning(pass: impl Into<String>, msg: impl Into<String>) -> Self {
84 TsExtDiagMsg {
85 severity: TsExtDiagSeverity::Warning,
86 pass: pass.into(),
87 message: msg.into(),
88 }
89 }
90 pub fn note(pass: impl Into<String>, msg: impl Into<String>) -> Self {
91 TsExtDiagMsg {
92 severity: TsExtDiagSeverity::Note,
93 pass: pass.into(),
94 message: msg.into(),
95 }
96 }
97}
98#[allow(dead_code)]
99#[derive(Debug, Clone)]
100pub struct TSCacheEntry {
101 pub key: String,
102 pub data: Vec<u8>,
103 pub timestamp: u64,
104 pub valid: bool,
105}
106#[derive(Debug, Clone, Default)]
108pub struct TsExtFeatures {
109 pub(super) flags: std::collections::HashSet<String>,
110}
111impl TsExtFeatures {
112 pub fn new() -> Self {
113 TsExtFeatures::default()
114 }
115 pub fn enable(&mut self, flag: impl Into<String>) {
116 self.flags.insert(flag.into());
117 }
118 pub fn disable(&mut self, flag: &str) {
119 self.flags.remove(flag);
120 }
121 pub fn is_enabled(&self, flag: &str) -> bool {
122 self.flags.contains(flag)
123 }
124 pub fn len(&self) -> usize {
125 self.flags.len()
126 }
127 pub fn is_empty(&self) -> bool {
128 self.flags.is_empty()
129 }
130 pub fn union(&self, other: &TsExtFeatures) -> TsExtFeatures {
131 TsExtFeatures {
132 flags: self.flags.union(&other.flags).cloned().collect(),
133 }
134 }
135 pub fn intersection(&self, other: &TsExtFeatures) -> TsExtFeatures {
136 TsExtFeatures {
137 flags: self.flags.intersection(&other.flags).cloned().collect(),
138 }
139 }
140}
141#[derive(Debug, Clone)]
143pub struct TsParam {
144 pub name: std::string::String,
145 pub ty: TsType,
146 pub optional: bool,
147 pub rest: bool,
148}
149#[derive(Debug, Clone)]
151pub struct TsClassField {
152 pub name: std::string::String,
153 pub ty: TsType,
154 pub readonly: bool,
155 pub optional: bool,
156 pub is_private: bool,
157 pub is_static: bool,
158}
159#[derive(Debug, Clone)]
161pub struct TsModule {
162 pub imports: Vec<TsImport>,
163 pub type_imports: Vec<TsImport>,
164 pub declarations: Vec<TsDeclaration>,
165}
166impl TsModule {
167 pub fn new() -> Self {
169 TsModule {
170 imports: Vec::new(),
171 type_imports: Vec::new(),
172 declarations: Vec::new(),
173 }
174 }
175 pub fn emit(&self) -> std::string::String {
177 let mut out = std::string::String::new();
178 for imp in &self.type_imports {
179 out.push_str(&format!("{}\n", imp));
180 }
181 for imp in &self.imports {
182 out.push_str(&format!("{}\n", imp));
183 }
184 if !self.imports.is_empty() || !self.type_imports.is_empty() {
185 out.push('\n');
186 }
187 for decl in &self.declarations {
188 out.push_str(&format!("{}\n\n", decl));
189 }
190 out
191 }
192 pub fn emit_d_ts(&self) -> std::string::String {
194 let mut out = std::string::String::new();
195 for imp in &self.type_imports {
196 out.push_str(&format!("{}\n", imp));
197 }
198 for decl in &self.declarations {
199 let dts = match decl {
200 TsDeclaration::Interface(i) => format!("{}\n\n", i),
201 TsDeclaration::TypeAlias(t) => format!("{}\n\n", t),
202 TsDeclaration::Enum(e) => format!("{}\n\n", e),
203 TsDeclaration::Function(f) => {
204 let mut sig = std::string::String::new();
205 if f.is_exported {
206 sig.push_str("export ");
207 }
208 sig.push_str(&format!("declare function {}", f.name));
209 if !f.type_params.is_empty() {
210 sig.push_str(&format!("<{}>", f.type_params.join(", ")));
211 }
212 sig.push('(');
213 for (i, p) in f.params.iter().enumerate() {
214 if i > 0 {
215 sig.push_str(", ");
216 }
217 sig.push_str(&format!("{}: {}", p.name, p.ty));
218 }
219 sig.push_str(&format!("): {};\n\n", f.return_type));
220 sig
221 }
222 TsDeclaration::Class(c) => format!("{}\n\n", c),
223 TsDeclaration::Const(name, ty, _) => {
224 if let Some(t) = ty {
225 format!("export declare const {}: {};\n\n", name, t)
226 } else {
227 format!("export declare const {};\n\n", name)
228 }
229 }
230 TsDeclaration::Let(name, ty, _) => {
231 if let Some(t) = ty {
232 format!("export declare let {}: {};\n\n", name, t)
233 } else {
234 format!("export declare let {};\n\n", name)
235 }
236 }
237 TsDeclaration::ReExport(path) => {
238 format!("export * from \"{}\";\n\n", path)
239 }
240 };
241 out.push_str(&dts);
242 }
243 out
244 }
245}
246#[derive(Debug, Default)]
248pub struct TsExtSourceBuffer {
249 pub(super) buf: String,
250 pub(super) indent_level: usize,
251 pub(super) indent_str: String,
252}
253impl TsExtSourceBuffer {
254 pub fn new() -> Self {
255 TsExtSourceBuffer {
256 buf: String::new(),
257 indent_level: 0,
258 indent_str: " ".to_string(),
259 }
260 }
261 pub fn with_indent(mut self, indent: impl Into<String>) -> Self {
262 self.indent_str = indent.into();
263 self
264 }
265 pub fn push_line(&mut self, line: &str) {
266 for _ in 0..self.indent_level {
267 self.buf.push_str(&self.indent_str);
268 }
269 self.buf.push_str(line);
270 self.buf.push('\n');
271 }
272 pub fn push_raw(&mut self, s: &str) {
273 self.buf.push_str(s);
274 }
275 pub fn indent(&mut self) {
276 self.indent_level += 1;
277 }
278 pub fn dedent(&mut self) {
279 self.indent_level = self.indent_level.saturating_sub(1);
280 }
281 pub fn as_str(&self) -> &str {
282 &self.buf
283 }
284 pub fn len(&self) -> usize {
285 self.buf.len()
286 }
287 pub fn is_empty(&self) -> bool {
288 self.buf.is_empty()
289 }
290 pub fn line_count(&self) -> usize {
291 self.buf.lines().count()
292 }
293 pub fn into_string(self) -> String {
294 self.buf
295 }
296 pub fn reset(&mut self) {
297 self.buf.clear();
298 self.indent_level = 0;
299 }
300}
301#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
303pub enum TsExtDiagSeverity {
304 Note,
305 Warning,
306 Error,
307}
308#[derive(Debug, Clone, PartialEq, Eq, Hash)]
310pub struct TsExtIncrKey {
311 pub content_hash: u64,
312 pub config_hash: u64,
313}
314impl TsExtIncrKey {
315 pub fn new(content: u64, config: u64) -> Self {
316 TsExtIncrKey {
317 content_hash: content,
318 config_hash: config,
319 }
320 }
321 pub fn combined_hash(&self) -> u64 {
322 self.content_hash.wrapping_mul(0x9e3779b97f4a7c15) ^ self.config_hash
323 }
324 pub fn matches(&self, other: &TsExtIncrKey) -> bool {
325 self.content_hash == other.content_hash && self.config_hash == other.config_hash
326 }
327}
328#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
330pub struct TsExtVersion {
331 pub major: u32,
332 pub minor: u32,
333 pub patch: u32,
334 pub pre: Option<String>,
335}
336impl TsExtVersion {
337 pub fn new(major: u32, minor: u32, patch: u32) -> Self {
338 TsExtVersion {
339 major,
340 minor,
341 patch,
342 pre: None,
343 }
344 }
345 pub fn with_pre(mut self, pre: impl Into<String>) -> Self {
346 self.pre = Some(pre.into());
347 self
348 }
349 pub fn is_stable(&self) -> bool {
350 self.pre.is_none()
351 }
352 pub fn is_compatible_with(&self, other: &TsExtVersion) -> bool {
353 self.major == other.major && self.minor >= other.minor
354 }
355}
356#[derive(Debug, Clone, PartialEq)]
358pub enum TsLit {
359 Num(f64),
360 Str(std::string::String),
361 Bool(bool),
362 Null,
363 Undefined,
364 BigInt(i64),
365}
366#[allow(dead_code)]
367#[derive(Debug, Clone, Default)]
368pub struct TSPassStats {
369 pub total_runs: u32,
370 pub successful_runs: u32,
371 pub total_changes: u64,
372 pub time_ms: u64,
373 pub iterations_used: u32,
374}
375impl TSPassStats {
376 #[allow(dead_code)]
377 pub fn new() -> Self {
378 Self::default()
379 }
380 #[allow(dead_code)]
381 pub fn record_run(&mut self, changes: u64, time_ms: u64, iterations: u32) {
382 self.total_runs += 1;
383 self.successful_runs += 1;
384 self.total_changes += changes;
385 self.time_ms += time_ms;
386 self.iterations_used = iterations;
387 }
388 #[allow(dead_code)]
389 pub fn average_changes_per_run(&self) -> f64 {
390 if self.total_runs == 0 {
391 return 0.0;
392 }
393 self.total_changes as f64 / self.total_runs as f64
394 }
395 #[allow(dead_code)]
396 pub fn success_rate(&self) -> f64 {
397 if self.total_runs == 0 {
398 return 0.0;
399 }
400 self.successful_runs as f64 / self.total_runs as f64
401 }
402 #[allow(dead_code)]
403 pub fn format_summary(&self) -> String {
404 format!(
405 "Runs: {}/{}, Changes: {}, Time: {}ms",
406 self.successful_runs, self.total_runs, self.total_changes, self.time_ms
407 )
408 }
409}
410#[derive(Debug, Clone)]
412pub enum TsDeclaration {
413 Interface(TsInterface),
414 TypeAlias(TsTypeAlias),
415 Enum(TsEnum),
416 Function(TsFunction),
417 Class(TsClass),
418 Const(std::string::String, Option<TsType>, TsExpr),
419 Let(std::string::String, Option<TsType>, TsExpr),
420 ReExport(std::string::String),
422}
423#[allow(dead_code)]
424#[derive(Debug, Clone)]
425pub struct TSDominatorTree {
426 pub idom: Vec<Option<u32>>,
427 pub dom_children: Vec<Vec<u32>>,
428 pub dom_depth: Vec<u32>,
429}
430impl TSDominatorTree {
431 #[allow(dead_code)]
432 pub fn new(size: usize) -> Self {
433 TSDominatorTree {
434 idom: vec![None; size],
435 dom_children: vec![Vec::new(); size],
436 dom_depth: vec![0; size],
437 }
438 }
439 #[allow(dead_code)]
440 pub fn set_idom(&mut self, node: usize, idom: u32) {
441 self.idom[node] = Some(idom);
442 }
443 #[allow(dead_code)]
444 pub fn dominates(&self, a: usize, b: usize) -> bool {
445 if a == b {
446 return true;
447 }
448 let mut cur = b;
449 loop {
450 match self.idom[cur] {
451 Some(parent) if parent as usize == a => return true,
452 Some(parent) if parent as usize == cur => return false,
453 Some(parent) => cur = parent as usize,
454 None => return false,
455 }
456 }
457 }
458 #[allow(dead_code)]
459 pub fn depth(&self, node: usize) -> u32 {
460 self.dom_depth.get(node).copied().unwrap_or(0)
461 }
462}
463#[allow(dead_code)]
464#[derive(Debug, Clone)]
465pub struct TSLivenessInfo {
466 pub live_in: Vec<std::collections::HashSet<u32>>,
467 pub live_out: Vec<std::collections::HashSet<u32>>,
468 pub defs: Vec<std::collections::HashSet<u32>>,
469 pub uses: Vec<std::collections::HashSet<u32>>,
470}
471impl TSLivenessInfo {
472 #[allow(dead_code)]
473 pub fn new(block_count: usize) -> Self {
474 TSLivenessInfo {
475 live_in: vec![std::collections::HashSet::new(); block_count],
476 live_out: vec![std::collections::HashSet::new(); block_count],
477 defs: vec![std::collections::HashSet::new(); block_count],
478 uses: vec![std::collections::HashSet::new(); block_count],
479 }
480 }
481 #[allow(dead_code)]
482 pub fn add_def(&mut self, block: usize, var: u32) {
483 if block < self.defs.len() {
484 self.defs[block].insert(var);
485 }
486 }
487 #[allow(dead_code)]
488 pub fn add_use(&mut self, block: usize, var: u32) {
489 if block < self.uses.len() {
490 self.uses[block].insert(var);
491 }
492 }
493 #[allow(dead_code)]
494 pub fn is_live_in(&self, block: usize, var: u32) -> bool {
495 self.live_in
496 .get(block)
497 .map(|s| s.contains(&var))
498 .unwrap_or(false)
499 }
500 #[allow(dead_code)]
501 pub fn is_live_out(&self, block: usize, var: u32) -> bool {
502 self.live_out
503 .get(block)
504 .map(|s| s.contains(&var))
505 .unwrap_or(false)
506 }
507}
508#[derive(Debug, Clone)]
510pub struct TsEnumMember {
511 pub name: std::string::String,
512 pub value: Option<TsLit>,
513}
514#[derive(Debug, Clone, Default)]
516pub struct TsExtEmitStats {
517 pub bytes_emitted: usize,
518 pub items_emitted: usize,
519 pub errors: usize,
520 pub warnings: usize,
521 pub elapsed_ms: u64,
522}
523impl TsExtEmitStats {
524 pub fn new() -> Self {
525 TsExtEmitStats::default()
526 }
527 pub fn throughput_bps(&self) -> f64 {
528 if self.elapsed_ms == 0 {
529 0.0
530 } else {
531 self.bytes_emitted as f64 / (self.elapsed_ms as f64 / 1000.0)
532 }
533 }
534 pub fn is_clean(&self) -> bool {
535 self.errors == 0
536 }
537}
538#[allow(dead_code)]
539pub struct TSConstantFoldingHelper;
540impl TSConstantFoldingHelper {
541 #[allow(dead_code)]
542 pub fn fold_add_i64(a: i64, b: i64) -> Option<i64> {
543 a.checked_add(b)
544 }
545 #[allow(dead_code)]
546 pub fn fold_sub_i64(a: i64, b: i64) -> Option<i64> {
547 a.checked_sub(b)
548 }
549 #[allow(dead_code)]
550 pub fn fold_mul_i64(a: i64, b: i64) -> Option<i64> {
551 a.checked_mul(b)
552 }
553 #[allow(dead_code)]
554 pub fn fold_div_i64(a: i64, b: i64) -> Option<i64> {
555 if b == 0 {
556 None
557 } else {
558 a.checked_div(b)
559 }
560 }
561 #[allow(dead_code)]
562 pub fn fold_add_f64(a: f64, b: f64) -> f64 {
563 a + b
564 }
565 #[allow(dead_code)]
566 pub fn fold_mul_f64(a: f64, b: f64) -> f64 {
567 a * b
568 }
569 #[allow(dead_code)]
570 pub fn fold_neg_i64(a: i64) -> Option<i64> {
571 a.checked_neg()
572 }
573 #[allow(dead_code)]
574 pub fn fold_not_bool(a: bool) -> bool {
575 !a
576 }
577 #[allow(dead_code)]
578 pub fn fold_and_bool(a: bool, b: bool) -> bool {
579 a && b
580 }
581 #[allow(dead_code)]
582 pub fn fold_or_bool(a: bool, b: bool) -> bool {
583 a || b
584 }
585 #[allow(dead_code)]
586 pub fn fold_shl_i64(a: i64, b: u32) -> Option<i64> {
587 a.checked_shl(b)
588 }
589 #[allow(dead_code)]
590 pub fn fold_shr_i64(a: i64, b: u32) -> Option<i64> {
591 a.checked_shr(b)
592 }
593 #[allow(dead_code)]
594 pub fn fold_rem_i64(a: i64, b: i64) -> Option<i64> {
595 if b == 0 {
596 None
597 } else {
598 Some(a % b)
599 }
600 }
601 #[allow(dead_code)]
602 pub fn fold_bitand_i64(a: i64, b: i64) -> i64 {
603 a & b
604 }
605 #[allow(dead_code)]
606 pub fn fold_bitor_i64(a: i64, b: i64) -> i64 {
607 a | b
608 }
609 #[allow(dead_code)]
610 pub fn fold_bitxor_i64(a: i64, b: i64) -> i64 {
611 a ^ b
612 }
613 #[allow(dead_code)]
614 pub fn fold_bitnot_i64(a: i64) -> i64 {
615 !a
616 }
617}
618#[derive(Debug, Clone)]
620pub struct TsFunction {
621 pub name: std::string::String,
622 pub params: Vec<TsParam>,
623 pub return_type: TsType,
624 pub body: Vec<TsStmt>,
625 pub is_async: bool,
626 pub type_params: Vec<std::string::String>,
627 pub is_exported: bool,
628}
629#[derive(Debug)]
631pub struct TsExtEventLog {
632 pub(super) entries: std::collections::VecDeque<String>,
633 pub(super) capacity: usize,
634}
635impl TsExtEventLog {
636 pub fn new(capacity: usize) -> Self {
637 TsExtEventLog {
638 entries: std::collections::VecDeque::with_capacity(capacity),
639 capacity,
640 }
641 }
642 pub fn push(&mut self, event: impl Into<String>) {
643 if self.entries.len() >= self.capacity {
644 self.entries.pop_front();
645 }
646 self.entries.push_back(event.into());
647 }
648 pub fn iter(&self) -> impl Iterator<Item = &String> {
649 self.entries.iter()
650 }
651 pub fn len(&self) -> usize {
652 self.entries.len()
653 }
654 pub fn is_empty(&self) -> bool {
655 self.entries.is_empty()
656 }
657 pub fn capacity(&self) -> usize {
658 self.capacity
659 }
660 pub fn clear(&mut self) {
661 self.entries.clear();
662 }
663}
664#[allow(dead_code)]
665#[derive(Debug, Clone)]
666pub struct TSAnalysisCache {
667 pub(super) entries: std::collections::HashMap<String, TSCacheEntry>,
668 pub(super) max_size: usize,
669 pub(super) hits: u64,
670 pub(super) misses: u64,
671}
672impl TSAnalysisCache {
673 #[allow(dead_code)]
674 pub fn new(max_size: usize) -> Self {
675 TSAnalysisCache {
676 entries: std::collections::HashMap::new(),
677 max_size,
678 hits: 0,
679 misses: 0,
680 }
681 }
682 #[allow(dead_code)]
683 pub fn get(&mut self, key: &str) -> Option<&TSCacheEntry> {
684 if self.entries.contains_key(key) {
685 self.hits += 1;
686 self.entries.get(key)
687 } else {
688 self.misses += 1;
689 None
690 }
691 }
692 #[allow(dead_code)]
693 pub fn insert(&mut self, key: String, data: Vec<u8>) {
694 if self.entries.len() >= self.max_size {
695 if let Some(oldest) = self.entries.keys().next().cloned() {
696 self.entries.remove(&oldest);
697 }
698 }
699 self.entries.insert(
700 key.clone(),
701 TSCacheEntry {
702 key,
703 data,
704 timestamp: 0,
705 valid: true,
706 },
707 );
708 }
709 #[allow(dead_code)]
710 pub fn invalidate(&mut self, key: &str) {
711 if let Some(entry) = self.entries.get_mut(key) {
712 entry.valid = false;
713 }
714 }
715 #[allow(dead_code)]
716 pub fn clear(&mut self) {
717 self.entries.clear();
718 }
719 #[allow(dead_code)]
720 pub fn hit_rate(&self) -> f64 {
721 let total = self.hits + self.misses;
722 if total == 0 {
723 return 0.0;
724 }
725 self.hits as f64 / total as f64
726 }
727 #[allow(dead_code)]
728 pub fn size(&self) -> usize {
729 self.entries.len()
730 }
731}
732#[allow(dead_code)]
733#[derive(Debug, Clone, PartialEq)]
734pub enum TSPassPhase {
735 Analysis,
736 Transformation,
737 Verification,
738 Cleanup,
739}
740impl TSPassPhase {
741 #[allow(dead_code)]
742 pub fn name(&self) -> &str {
743 match self {
744 TSPassPhase::Analysis => "analysis",
745 TSPassPhase::Transformation => "transformation",
746 TSPassPhase::Verification => "verification",
747 TSPassPhase::Cleanup => "cleanup",
748 }
749 }
750 #[allow(dead_code)]
751 pub fn is_modifying(&self) -> bool {
752 matches!(self, TSPassPhase::Transformation | TSPassPhase::Cleanup)
753 }
754}
755#[derive(Debug, Clone, PartialEq)]
757pub enum TsExpr {
758 Lit(TsLit),
760 Var(std::string::String),
762 BinOp(std::string::String, Box<TsExpr>, Box<TsExpr>),
764 UnaryOp(std::string::String, Box<TsExpr>),
766 Call(Box<TsExpr>, Vec<TsExpr>),
768 MethodCall(Box<TsExpr>, std::string::String, Vec<TsExpr>),
770 New(Box<TsExpr>, Vec<TsExpr>),
772 Arrow(Vec<(std::string::String, Option<TsType>)>, Box<TsExpr>),
774 Ternary(Box<TsExpr>, Box<TsExpr>, Box<TsExpr>),
776 As(Box<TsExpr>, TsType),
778 Satisfies(Box<TsExpr>, TsType),
780 TypeAssert(Box<TsExpr>),
782 ObjectLit(Vec<(std::string::String, TsExpr)>),
784 ArrayLit(Vec<TsExpr>),
786 Template(Vec<TsTemplatePart>),
788 Await(Box<TsExpr>),
790 Nullish(Box<TsExpr>, Box<TsExpr>),
792 OptChain(Box<TsExpr>, std::string::String),
794}
795#[derive(Debug, Clone, PartialEq)]
797pub enum TsStmt {
798 Expr(TsExpr),
800 Const(std::string::String, Option<TsType>, TsExpr),
802 Let(std::string::String, Option<TsType>, TsExpr),
804 Var(std::string::String, Option<TsType>, TsExpr),
806 If(TsExpr, Vec<TsStmt>, Vec<TsStmt>),
808 Switch(TsExpr, Vec<(TsExpr, Vec<TsStmt>)>, Vec<TsStmt>),
810 For(Box<TsStmt>, TsExpr, TsExpr, Vec<TsStmt>),
812 ForOf(std::string::String, TsExpr, Vec<TsStmt>),
814 ForIn(std::string::String, TsExpr, Vec<TsStmt>),
816 While(TsExpr, Vec<TsStmt>),
818 Return(TsExpr),
820 Throw(TsExpr),
822 TryCatch(Vec<TsStmt>, std::string::String, Vec<TsStmt>, Vec<TsStmt>),
824 Block(Vec<TsStmt>),
826 Break,
828 Continue,
830}
831#[derive(Debug, Clone)]
833pub struct TsClassMethod {
834 pub name: std::string::String,
835 pub params: Vec<TsParam>,
836 pub return_type: TsType,
837 pub body: Vec<TsStmt>,
838 pub is_async: bool,
839 pub is_static: bool,
840 pub is_private: bool,
841 pub is_getter: bool,
842 pub is_setter: bool,
843}
844pub struct TypeScriptBackend {
848 pub(super) module: TsModule,
849}
850impl TypeScriptBackend {
851 pub fn new() -> Self {
853 TypeScriptBackend {
854 module: TsModule::new(),
855 }
856 }
857 pub fn add_declaration(&mut self, decl: TsDeclaration) {
859 self.module.declarations.push(decl);
860 }
861 pub fn add_import(&mut self, imp: TsImport) {
863 if imp.is_type {
864 self.module.type_imports.push(imp);
865 } else {
866 self.module.imports.push(imp);
867 }
868 }
869 pub fn make_discriminated_union(
878 &self,
879 type_name: &str,
880 variants: &[(&str, Vec<(&str, TsType)>)],
881 ) -> TsTypeAlias {
882 let union_types: Vec<TsType> = variants
883 .iter()
884 .map(|(variant_name, fields)| {
885 let mut members: Vec<(std::string::String, TsType)> = vec![(
886 "kind".to_string(),
887 TsType::Custom(format!("'{}'", variant_name)),
888 )];
889 for (field_name, field_ty) in fields {
890 members.push((field_name.to_string(), field_ty.clone()));
891 }
892 TsType::Object(members)
893 })
894 .collect();
895 TsTypeAlias {
896 name: type_name.to_string(),
897 type_params: Vec::new(),
898 definition: TsType::Union(union_types),
899 }
900 }
901 pub fn emit_module(&self) -> std::string::String {
903 self.module.emit()
904 }
905 pub fn emit_d_ts(&self) -> std::string::String {
907 self.module.emit_d_ts()
908 }
909}
910#[derive(Debug, Clone, PartialEq, Eq, Hash)]
912pub enum TsType {
913 Number,
915 String,
917 Boolean,
919 Void,
921 Never,
923 Unknown,
925 Any,
927 Null,
929 Undefined,
931 Tuple(Vec<TsType>),
933 Array(Box<TsType>),
935 Object(Vec<(std::string::String, TsType)>),
937 Union(Vec<TsType>),
939 Intersection(Vec<TsType>),
941 Function {
943 params: Vec<TsType>,
944 ret: Box<TsType>,
945 },
946 Custom(std::string::String),
948 Generic(std::string::String, Vec<TsType>),
950 ReadOnly(Box<TsType>),
952 Readonly,
954}
955#[allow(dead_code)]
956#[derive(Debug, Clone)]
957pub struct TSPassConfig {
958 pub phase: TSPassPhase,
959 pub enabled: bool,
960 pub max_iterations: u32,
961 pub debug_output: bool,
962 pub pass_name: String,
963}
964impl TSPassConfig {
965 #[allow(dead_code)]
966 pub fn new(name: impl Into<String>, phase: TSPassPhase) -> Self {
967 TSPassConfig {
968 phase,
969 enabled: true,
970 max_iterations: 10,
971 debug_output: false,
972 pass_name: name.into(),
973 }
974 }
975 #[allow(dead_code)]
976 pub fn disabled(mut self) -> Self {
977 self.enabled = false;
978 self
979 }
980 #[allow(dead_code)]
981 pub fn with_debug(mut self) -> Self {
982 self.debug_output = true;
983 self
984 }
985 #[allow(dead_code)]
986 pub fn max_iter(mut self, n: u32) -> Self {
987 self.max_iterations = n;
988 self
989 }
990}
991#[derive(Debug, Default)]
993pub struct TsExtDiagCollector {
994 pub(super) msgs: Vec<TsExtDiagMsg>,
995}
996impl TsExtDiagCollector {
997 pub fn new() -> Self {
998 TsExtDiagCollector::default()
999 }
1000 pub fn emit(&mut self, d: TsExtDiagMsg) {
1001 self.msgs.push(d);
1002 }
1003 pub fn has_errors(&self) -> bool {
1004 self.msgs
1005 .iter()
1006 .any(|d| d.severity == TsExtDiagSeverity::Error)
1007 }
1008 pub fn errors(&self) -> Vec<&TsExtDiagMsg> {
1009 self.msgs
1010 .iter()
1011 .filter(|d| d.severity == TsExtDiagSeverity::Error)
1012 .collect()
1013 }
1014 pub fn warnings(&self) -> Vec<&TsExtDiagMsg> {
1015 self.msgs
1016 .iter()
1017 .filter(|d| d.severity == TsExtDiagSeverity::Warning)
1018 .collect()
1019 }
1020 pub fn len(&self) -> usize {
1021 self.msgs.len()
1022 }
1023 pub fn is_empty(&self) -> bool {
1024 self.msgs.is_empty()
1025 }
1026 pub fn clear(&mut self) {
1027 self.msgs.clear();
1028 }
1029}
1030#[derive(Debug, Clone, PartialEq)]
1032pub struct TsInterfaceMember {
1033 pub name: std::string::String,
1034 pub ty: TsType,
1035 pub optional: bool,
1036 pub readonly: bool,
1037}
1038#[derive(Debug, Clone)]
1040pub struct TsEnum {
1041 pub name: std::string::String,
1042 pub is_const: bool,
1043 pub members: Vec<TsEnumMember>,
1044}
1045#[allow(dead_code)]
1046#[derive(Debug, Clone)]
1047pub struct TSDepGraph {
1048 pub(super) nodes: Vec<u32>,
1049 pub(super) edges: Vec<(u32, u32)>,
1050}
1051impl TSDepGraph {
1052 #[allow(dead_code)]
1053 pub fn new() -> Self {
1054 TSDepGraph {
1055 nodes: Vec::new(),
1056 edges: Vec::new(),
1057 }
1058 }
1059 #[allow(dead_code)]
1060 pub fn add_node(&mut self, id: u32) {
1061 if !self.nodes.contains(&id) {
1062 self.nodes.push(id);
1063 }
1064 }
1065 #[allow(dead_code)]
1066 pub fn add_dep(&mut self, dep: u32, dependent: u32) {
1067 self.add_node(dep);
1068 self.add_node(dependent);
1069 self.edges.push((dep, dependent));
1070 }
1071 #[allow(dead_code)]
1072 pub fn dependents_of(&self, node: u32) -> Vec<u32> {
1073 self.edges
1074 .iter()
1075 .filter(|(d, _)| *d == node)
1076 .map(|(_, dep)| *dep)
1077 .collect()
1078 }
1079 #[allow(dead_code)]
1080 pub fn dependencies_of(&self, node: u32) -> Vec<u32> {
1081 self.edges
1082 .iter()
1083 .filter(|(_, dep)| *dep == node)
1084 .map(|(d, _)| *d)
1085 .collect()
1086 }
1087 #[allow(dead_code)]
1088 pub fn topological_sort(&self) -> Vec<u32> {
1089 let mut in_degree: std::collections::HashMap<u32, u32> = std::collections::HashMap::new();
1090 for &n in &self.nodes {
1091 in_degree.insert(n, 0);
1092 }
1093 for (_, dep) in &self.edges {
1094 *in_degree.entry(*dep).or_insert(0) += 1;
1095 }
1096 let mut queue: std::collections::VecDeque<u32> = self
1097 .nodes
1098 .iter()
1099 .filter(|&&n| in_degree[&n] == 0)
1100 .copied()
1101 .collect();
1102 let mut result = Vec::new();
1103 while let Some(node) = queue.pop_front() {
1104 result.push(node);
1105 for dep in self.dependents_of(node) {
1106 let cnt = in_degree.entry(dep).or_insert(0);
1107 *cnt = cnt.saturating_sub(1);
1108 if *cnt == 0 {
1109 queue.push_back(dep);
1110 }
1111 }
1112 }
1113 result
1114 }
1115 #[allow(dead_code)]
1116 pub fn has_cycle(&self) -> bool {
1117 self.topological_sort().len() < self.nodes.len()
1118 }
1119}
1120#[derive(Debug, Default)]
1122pub struct TsExtNameScope {
1123 pub(super) declared: std::collections::HashSet<String>,
1124 pub(super) depth: usize,
1125 pub(super) parent: Option<Box<TsExtNameScope>>,
1126}
1127impl TsExtNameScope {
1128 pub fn new() -> Self {
1129 TsExtNameScope::default()
1130 }
1131 pub fn declare(&mut self, name: impl Into<String>) -> bool {
1132 self.declared.insert(name.into())
1133 }
1134 pub fn is_declared(&self, name: &str) -> bool {
1135 self.declared.contains(name)
1136 }
1137 pub fn push_scope(self) -> Self {
1138 TsExtNameScope {
1139 declared: std::collections::HashSet::new(),
1140 depth: self.depth + 1,
1141 parent: Some(Box::new(self)),
1142 }
1143 }
1144 pub fn pop_scope(self) -> Self {
1145 *self.parent.unwrap_or_default()
1146 }
1147 pub fn depth(&self) -> usize {
1148 self.depth
1149 }
1150 pub fn len(&self) -> usize {
1151 self.declared.len()
1152 }
1153}
1154#[derive(Debug, Clone)]
1156pub struct TsTypeAlias {
1157 pub name: std::string::String,
1158 pub type_params: Vec<std::string::String>,
1159 pub definition: TsType,
1160}
1161#[allow(dead_code)]
1162#[derive(Debug, Clone)]
1163pub struct TSWorklist {
1164 pub(super) items: std::collections::VecDeque<u32>,
1165 pub(super) in_worklist: std::collections::HashSet<u32>,
1166}
1167impl TSWorklist {
1168 #[allow(dead_code)]
1169 pub fn new() -> Self {
1170 TSWorklist {
1171 items: std::collections::VecDeque::new(),
1172 in_worklist: std::collections::HashSet::new(),
1173 }
1174 }
1175 #[allow(dead_code)]
1176 pub fn push(&mut self, item: u32) -> bool {
1177 if self.in_worklist.insert(item) {
1178 self.items.push_back(item);
1179 true
1180 } else {
1181 false
1182 }
1183 }
1184 #[allow(dead_code)]
1185 pub fn pop(&mut self) -> Option<u32> {
1186 let item = self.items.pop_front()?;
1187 self.in_worklist.remove(&item);
1188 Some(item)
1189 }
1190 #[allow(dead_code)]
1191 pub fn is_empty(&self) -> bool {
1192 self.items.is_empty()
1193 }
1194 #[allow(dead_code)]
1195 pub fn len(&self) -> usize {
1196 self.items.len()
1197 }
1198 #[allow(dead_code)]
1199 pub fn contains(&self, item: u32) -> bool {
1200 self.in_worklist.contains(&item)
1201 }
1202}
1203#[derive(Debug, Default)]
1205pub struct TsExtProfiler {
1206 pub(super) timings: Vec<TsExtPassTiming>,
1207}
1208impl TsExtProfiler {
1209 pub fn new() -> Self {
1210 TsExtProfiler::default()
1211 }
1212 pub fn record(&mut self, t: TsExtPassTiming) {
1213 self.timings.push(t);
1214 }
1215 pub fn total_elapsed_us(&self) -> u64 {
1216 self.timings.iter().map(|t| t.elapsed_us).sum()
1217 }
1218 pub fn slowest_pass(&self) -> Option<&TsExtPassTiming> {
1219 self.timings.iter().max_by_key(|t| t.elapsed_us)
1220 }
1221 pub fn num_passes(&self) -> usize {
1222 self.timings.len()
1223 }
1224 pub fn profitable_passes(&self) -> Vec<&TsExtPassTiming> {
1225 self.timings.iter().filter(|t| t.is_profitable()).collect()
1226 }
1227}
1228#[derive(Debug, Clone)]
1230pub struct TsExtPassTiming {
1231 pub pass_name: String,
1232 pub elapsed_us: u64,
1233 pub items_processed: usize,
1234 pub bytes_before: usize,
1235 pub bytes_after: usize,
1236}
1237impl TsExtPassTiming {
1238 pub fn new(
1239 pass_name: impl Into<String>,
1240 elapsed_us: u64,
1241 items: usize,
1242 before: usize,
1243 after: usize,
1244 ) -> Self {
1245 TsExtPassTiming {
1246 pass_name: pass_name.into(),
1247 elapsed_us,
1248 items_processed: items,
1249 bytes_before: before,
1250 bytes_after: after,
1251 }
1252 }
1253 pub fn throughput_mps(&self) -> f64 {
1254 if self.elapsed_us == 0 {
1255 0.0
1256 } else {
1257 self.items_processed as f64 / (self.elapsed_us as f64 / 1_000_000.0)
1258 }
1259 }
1260 pub fn size_ratio(&self) -> f64 {
1261 if self.bytes_before == 0 {
1262 1.0
1263 } else {
1264 self.bytes_after as f64 / self.bytes_before as f64
1265 }
1266 }
1267 pub fn is_profitable(&self) -> bool {
1268 self.size_ratio() <= 1.05
1269 }
1270}
1271#[derive(Debug, Clone, Default)]
1273pub struct TsExtConfig {
1274 pub(super) entries: std::collections::HashMap<String, String>,
1275}
1276impl TsExtConfig {
1277 pub fn new() -> Self {
1278 TsExtConfig::default()
1279 }
1280 pub fn set(&mut self, key: impl Into<String>, value: impl Into<String>) {
1281 self.entries.insert(key.into(), value.into());
1282 }
1283 pub fn get(&self, key: &str) -> Option<&str> {
1284 self.entries.get(key).map(|s| s.as_str())
1285 }
1286 pub fn get_bool(&self, key: &str) -> bool {
1287 matches!(self.get(key), Some("true") | Some("1") | Some("yes"))
1288 }
1289 pub fn get_int(&self, key: &str) -> Option<i64> {
1290 self.get(key)?.parse().ok()
1291 }
1292 pub fn len(&self) -> usize {
1293 self.entries.len()
1294 }
1295 pub fn is_empty(&self) -> bool {
1296 self.entries.is_empty()
1297 }
1298}
1299#[derive(Debug, Clone)]
1301pub struct TsImport {
1302 pub names: Vec<std::string::String>,
1303 pub from: std::string::String,
1304 pub is_type: bool,
1305}
1306#[derive(Debug, Default)]
1308pub struct TsExtIdGen {
1309 pub(super) next: u32,
1310}
1311impl TsExtIdGen {
1312 pub fn new() -> Self {
1313 TsExtIdGen::default()
1314 }
1315 pub fn next_id(&mut self) -> u32 {
1316 let id = self.next;
1317 self.next += 1;
1318 id
1319 }
1320 pub fn peek_next(&self) -> u32 {
1321 self.next
1322 }
1323 pub fn reset(&mut self) {
1324 self.next = 0;
1325 }
1326 pub fn skip(&mut self, n: u32) {
1327 self.next += n;
1328 }
1329}
1330#[derive(Debug, Clone)]
1332pub struct TsInterface {
1333 pub name: std::string::String,
1334 pub extends: Vec<std::string::String>,
1336 pub members: Vec<TsInterfaceMember>,
1338 pub type_params: Vec<std::string::String>,
1340}