1#![forbid(unsafe_code)]
2
3#[cfg(test)]
4mod debugger_test;
5#[cfg(test)]
6mod gc_test;
7#[cfg(test)]
8mod report_test;
9#[cfg(test)]
10mod runner_test;
11#[cfg(test)]
12mod value_test;
13#[cfg(test)]
14mod vm_test;
15
16pub mod debugger;
17pub mod frame;
18pub mod header;
19pub mod heap;
20pub mod list;
21pub mod malloc;
22pub mod report;
23pub mod runner;
24#[path = "gc_runtime.rs"]
27pub mod runtime;
28pub mod value;
29pub mod vm;
30
31pub use debugger::{
32 CaptureView, DebuggerHit, FrameView, HeapEdgeView, HeapMemberView, HeapObjectView, HeapView,
33 SlotView, StackSlotView, ValueView, MAX_DEBUGGER_DISPLAY_CHARS, MAX_DEBUGGER_EDGES,
34 MAX_DEBUGGER_HITS, MAX_DEBUGGER_MEMBERS, MAX_DEBUGGER_OBJECTS, MAX_DEBUGGER_SUMMARY_DEPTH,
35};
36pub use frame::Frame;
37pub use header::{GcId, GcObjectHeader, GcObjectType, GcPhase, RefCountHeader, RefCountId};
38pub use heap::{GcHeap, GcRef};
39pub use malloc::{MallocState, DEFAULT_GC_THRESHOLD, MALLOC_OVERHEAD};
40pub use report::{
41 EdgeRelation, FinalFate, FreeCycleStats, GcCollectionReport, GcObjectSummary, GcPhaseStats,
42 GcStatsBundle, GlobalRoot, HashKeyKind, HeapSnapshot, ObjectDecision, RestorationWitness,
43 ScanStats, TrialDecision, TrialDeletionStats, ValueKindCounts, VisitedEdge,
44};
45pub use runner::{compile_source, run_bytecode, run_bytecode_with_output};
46pub use runtime::{GcObject, GcRuntime, MarkFunc};
47pub use value::{
48 export_object, import_object, try_export_object, value_to_string, GcClosure, Value, ValueKind,
49};
50pub use vm::{
51 GcClassifiedRuntimeError, GcRuntimeError, GcRuntimeErrorKind, GcVM, DEFAULT_INSTRUCTION_BUDGET,
52};
53
54use compiler::compiler::{Bytecode, Compiler};
55use object::Object;
56use parser::ast::Node;
57use parser::lexer::token::Span;
58use serde::Serialize;
59
60#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize)]
61#[serde(rename_all = "lowercase")]
62pub enum GcRunStage {
63 Parse,
64 Compile,
65 Runtime,
66}
67
68#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
69#[serde(rename_all = "camelCase")]
70pub struct GcRunSuccess {
71 pub result: String,
72 pub report: GcCollectionReport,
73}
74
75#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
77#[serde(rename_all = "camelCase")]
78pub struct GcRunError {
79 pub stage: GcRunStage,
80 pub message: String,
81 pub span: Option<Span>,
82}
83
84#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
86#[serde(rename_all = "camelCase")]
87pub struct GcClassifiedRunError {
88 pub stage: GcRunStage,
89 pub kind: String,
90 pub message: String,
91 pub span: Option<Span>,
92}
93
94impl From<GcClassifiedRunError> for GcRunError {
95 fn from(error: GcClassifiedRunError) -> Self {
96 Self {
97 stage: error.stage,
98 message: error.message,
99 span: error.span,
100 }
101 }
102}
103
104pub fn compile(program: &Node) -> Result<Bytecode, String> {
106 let mut compiler = Compiler::new();
107 compiler.compile(program)
108}
109
110pub fn eval(program: &Node) -> Result<Object, String> {
112 let bytecode = compile(program)?;
113 let mut vm = GcVM::new(bytecode);
114 vm.run_with_budget(usize::MAX)
115 .map_err(|error| error.message)?;
116 vm.try_export_last_result()
117}
118
119pub fn eval_source(source: &str) -> Result<Object, String> {
121 let program = parser::parse(source).map_err(|errors| errors[0].clone())?;
122 eval(&program)
123}
124
125pub fn run_source_with_report(
127 source: &str,
128 instruction_budget: usize,
129) -> Result<GcRunSuccess, GcRunError> {
130 run_source_with_report_classified(source, instruction_budget).map_err(Into::into)
131}
132
133pub fn run_source_with_report_classified(
136 source: &str,
137 instruction_budget: usize,
138) -> Result<GcRunSuccess, GcClassifiedRunError> {
139 let program = parser::parse(source).map_err(|errors| GcClassifiedRunError {
140 stage: GcRunStage::Parse,
141 kind: "syntax".to_string(),
142 message: errors
143 .first()
144 .cloned()
145 .unwrap_or_else(|| "unknown parse error".to_string()),
146 span: None,
147 })?;
148 let mut compiler = Compiler::new();
149 let bytecode = compiler
150 .compile(&program)
151 .map_err(|message| GcClassifiedRunError {
152 stage: GcRunStage::Compile,
153 kind: "compile".to_string(),
154 message,
155 span: None,
156 })?;
157 let global_bindings = compiler.global_bindings();
158 let mut vm = GcVM::new(bytecode);
159 vm.set_global_bindings(global_bindings);
160 vm.heap_mut().set_gc_threshold(usize::MAX);
161 vm.run_with_budget_classified(instruction_budget)
162 .map_err(|error| GcClassifiedRunError {
163 stage: GcRunStage::Runtime,
164 kind: error.kind.as_str().to_string(),
165 message: error.message,
166 span: error.span,
167 })?;
168 let result = vm.last_result_string();
169 let report = vm.collect_garbage();
170 Ok(GcRunSuccess {
171 result,
172 report,
173 })
174}
175
176#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
180#[serde(rename_all = "camelCase")]
181pub enum GcDebuggerRunOutcome {
182 Ok {
183 result: String,
184 stdout: String,
185 hits: Vec<DebuggerHit>,
186 dropped_hits: usize,
187 },
188 Error {
189 error: GcClassifiedRunError,
190 stdout: String,
191 hits: Vec<DebuggerHit>,
192 dropped_hits: usize,
193 },
194}
195
196pub fn run_source_with_debugger_classified(
200 input: &str,
201 instruction_budget: usize,
202) -> GcDebuggerRunOutcome {
203 let error_outcome = |stage: GcRunStage, kind: &str, message: String| {
204 return GcDebuggerRunOutcome::Error {
205 error: GcClassifiedRunError {
206 stage,
207 kind: kind.to_string(),
208 message,
209 span: None,
210 },
211 stdout: String::new(),
212 hits: Vec::new(),
213 dropped_hits: 0,
214 };
215 };
216 let program = match parser::parse(input) {
217 Ok(program) => program,
218 Err(errors) => {
219 return error_outcome(
220 GcRunStage::Parse,
221 "syntax",
222 errors
223 .first()
224 .cloned()
225 .unwrap_or_else(|| "unknown parse error".to_string()),
226 );
227 }
228 };
229 let mut compiler = Compiler::new();
230 let bytecode = match compiler.compile(&program) {
231 Ok(bytecode) => bytecode,
232 Err(message) => return error_outcome(GcRunStage::Compile, "compile", message),
233 };
234 let global_bindings = compiler.global_bindings();
235 let mut vm = GcVM::new(bytecode);
236 vm.set_global_bindings(global_bindings);
237 vm.set_capture_output(true);
238 vm.heap_mut().set_gc_threshold(usize::MAX);
239 let run = vm.run_with_budget_classified(instruction_budget);
240 let stdout = vm.take_output();
241 let (hits, dropped_hits) = vm.take_debugger_hits();
242 match run {
243 Ok(()) => GcDebuggerRunOutcome::Ok {
244 result: vm.last_result_string(),
245 stdout,
246 hits,
247 dropped_hits,
248 },
249 Err(error) => GcDebuggerRunOutcome::Error {
250 error: GcClassifiedRunError {
251 stage: GcRunStage::Runtime,
252 kind: error.kind.as_str().to_string(),
253 message: error.message,
254 span: error.span,
255 },
256 stdout,
257 hits,
258 dropped_hits,
259 },
260 }
261}