diff --git a/Cargo.lock b/Cargo.lock index 8318b40..c162452 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -45,9 +45,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "backtrace" -version = "0.3.64" +version = "0.3.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e121dee8023ce33ab248d9ce1493df03c3b38a659b240096fcbd7048ff9c31f" +checksum = "11a17d453482a265fd5f8479f2a3f405566e6ca627837aaddb85af8b1ab8ef61" dependencies = [ "addr2line", "cc", @@ -78,9 +78,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "3.1.9" +version = "3.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6aad2534fad53df1cc12519c5cda696dd3e20e6118a027e24054aea14a0bdcbe" +checksum = "d2dbdf4bdacb33466e854ce889eee8dfd5729abf7ccd7664d0a2d60cd384440b" dependencies = [ "atty", "bitflags", @@ -95,9 +95,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "3.1.7" +version = "3.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3aab4734e083b809aaf5794e14e756d1c798d2c69c7f7de7a09a2f5214993c1" +checksum = "25320346e922cffe59c0bbc5410c8d8784509efb321488971081313cb1e1a33c" dependencies = [ "heck", "proc-macro-error", @@ -108,9 +108,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.1.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "189ddd3b5d32a70b35e7686054371742a937b0d99128e76dde6340210e966669" +checksum = "a37c35f1112dad5e6e0b1adaff798507497a18fceeb30cceb3bae7d1427b9213" dependencies = [ "os_str_bytes", ] @@ -159,9 +159,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.13.1" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0d720b8683f8dd83c65155f0530560cba68cd2bf395f6513a483caee57ff7f4" +checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" dependencies = [ "darling_core", "darling_macro", @@ -169,9 +169,9 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.13.1" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a340f241d2ceed1deb47ae36c4144b2707ec7dd0b649f894cb39bb595986324" +checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" dependencies = [ "fnv", "ident_case", @@ -183,9 +183,9 @@ dependencies = [ [[package]] name = "darling_macro" -version = "0.13.1" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72c41b3b7352feb3211a0d743dc5700a4e3b60f51bd2b368892d1e0f9a95f44b" +checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" dependencies = [ "darling_core", "quote", @@ -206,9 +206,9 @@ checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" [[package]] name = "eyre" -version = "0.6.7" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9289ed2c0440a6536e65119725cf91fc2c6b5e513bfd2e36e1134d7cca6ca12f" +checksum = "4c2b6b5a29c02cdc822728b7d7b8ae1bab3e3b05d44522770ddd49722eeac7eb" dependencies = [ "indenter", "once_cell", @@ -244,6 +244,18 @@ dependencies = [ [[package]] name = "gc" version = "0.1.0" +dependencies = [ + "gctrace-derive", +] + +[[package]] +name = "gctrace-derive" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "gimli" @@ -311,15 +323,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.121" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efaa7b300f3b5fe8eb6bf21ce3895e1751d9665086af2d64b42f19701015ff4f" +checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" [[package]] name = "log" -version = "0.4.14" +version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ "cfg-if", ] @@ -341,52 +353,51 @@ dependencies = [ [[package]] name = "memchr" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "miniz_oxide" -version = "0.4.4" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" +checksum = "d2b29bd4bc3f33391105ebee3589c19197c4271e3e5a9ec9bfe8127eeff8f082" dependencies = [ "adler", - "autocfg", ] [[package]] name = "object" -version = "0.27.1" +version = "0.28.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67ac1d3f9a1d3616fd9a60c8d74296f22406a238b6a72f5cc1e6f314df4ffbf9" +checksum = "e42c982f2d955fac81dd7e1d0e1426a7d702acd9c98d19ab01083a6a0328c424" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" +checksum = "7b10983b38c53aebdf33f542c6275b0f58a238129d00c4ae0e6fb59738d783ca" [[package]] name = "os_str_bytes" -version = "6.0.0" +version = "6.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64" +checksum = "029d8d0b2f198229de29dca79676f2738ff952edf3fde542eb8bf94d8c21b435" [[package]] name = "owo-colors" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e72e30578e0d0993c8ae20823dd9cff2bc5517d2f586a8aef462a581e8a03eb" +checksum = "decf7381921fea4dcb2549c5667eda59b3ec297ab7e2b5fc33eac69d2e7da87b" [[package]] name = "pin-project-lite" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" [[package]] name = "proc-macro-error" @@ -414,27 +425,27 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.36" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" +checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7" dependencies = [ - "unicode-xid", + "unicode-ident", ] [[package]] name = "quote" -version = "1.0.16" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4af2ec4714533fcdf07e886f17025ace8b997b9ce51204ee69b6da831c3da57" +checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804" dependencies = [ "proc-macro2", ] [[package]] name = "regex" -version = "1.5.5" +version = "1.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" +checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1" dependencies = [ "regex-syntax", ] @@ -450,9 +461,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.25" +version = "0.6.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" +checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64" [[package]] name = "rustc-demangle" @@ -483,13 +494,13 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "1.0.89" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea297be220d52398dcc07ce15a209fce436d361735ac1db700cab3b6cdfb9f54" +checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" dependencies = [ "proc-macro2", "quote", - "unicode-xid", + "unicode-ident", ] [[package]] @@ -518,9 +529,9 @@ dependencies = [ [[package]] name = "tracing" -version = "0.1.32" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a1bdf54a7c28a2bbf701e1d2233f6c77f473486b94bee4f9678da5a148dca7f" +checksum = "5d0ecdcb44a79f0fe9844f0c4f33a342cbcbb5117de8001e6ba0dc2351327d09" dependencies = [ "cfg-if", "pin-project-lite", @@ -530,9 +541,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.20" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e65ce065b4b5c53e73bb28912318cb8c9e9ad3921f1d669eb0e68b4c8143a2b" +checksum = "cc6b8ad3567499f98a1db7a752b07a7c8c7c7c34c332ec00effb2b0027974b7c" dependencies = [ "proc-macro2", "quote", @@ -541,9 +552,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.23" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa31669fa42c09c34d94d8165dd2012e8ff3c66aca50f3bb226b68f216f2706c" +checksum = "f54c8ca710e81886d498c2fd3331b56c93aa248d49de2222ad2742247c60072f" dependencies = [ "lazy_static", "valuable", @@ -561,9 +572,9 @@ dependencies = [ [[package]] name = "tracing-log" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6923477a48e41c1951f1999ef8bb5a3023eb723ceadafe78ffb65dc366761e3" +checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" dependencies = [ "lazy_static", "log", @@ -572,9 +583,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.9" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e0ab7bdc962035a87fba73f3acca9b8a8d0034c2e6f60b84aeaaddddc155dce" +checksum = "4bc28f93baff38037f64e6f43d34cfa1605f27a49c34e8a04c5e78b0babf2596" dependencies = [ "ansi_term", "lazy_static", @@ -589,10 +600,10 @@ dependencies = [ ] [[package]] -name = "unicode-xid" -version = "0.2.2" +name = "unicode-ident" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee" [[package]] name = "valuable" diff --git a/Cargo.toml b/Cargo.toml index 3f33d6f..e0de65a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,8 @@ edition = "2021" [workspace] members = [ - "gc" + "gc", + "gc/gctrace-derive" ] [dependencies] diff --git a/gc/Cargo.toml b/gc/Cargo.toml index dead181..b00bc19 100644 --- a/gc/Cargo.toml +++ b/gc/Cargo.toml @@ -3,6 +3,5 @@ name = "gc" version = "0.1.0" edition = "2021" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] +gctrace-derive = { version = "0.1.0", path = "gctrace-derive" } diff --git a/gc/fuzz/.gitignore b/gc/fuzz/.gitignore new file mode 100644 index 0000000..a092511 --- /dev/null +++ b/gc/fuzz/.gitignore @@ -0,0 +1,3 @@ +target +corpus +artifacts diff --git a/gc/fuzz/Cargo.lock b/gc/fuzz/Cargo.lock new file mode 100644 index 0000000..78771d2 --- /dev/null +++ b/gc/fuzz/Cargo.lock @@ -0,0 +1,93 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "arbitrary" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a7924531f38b1970ff630f03eb20a2fde69db5c590c93b0f3482e95dcc5fd60" +dependencies = [ + "derive_arbitrary", +] + +[[package]] +name = "cc" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" + +[[package]] +name = "derive_arbitrary" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9a577516173adb681466d517d39bd468293bc2c2a16439375ef0f35bba45f3d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "gc" +version = "0.1.0" + +[[package]] +name = "gc-fuzz" +version = "0.0.0" +dependencies = [ + "gc", + "libfuzzer-sys", +] + +[[package]] +name = "libfuzzer-sys" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "336244aaeab6a12df46480dc585802aa743a72d66b11937844c61bbca84c991d" +dependencies = [ + "arbitrary", + "cc", + "once_cell", +] + +[[package]] +name = "once_cell" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" + +[[package]] +name = "proc-macro2" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c" diff --git a/gc/fuzz/Cargo.toml b/gc/fuzz/Cargo.toml new file mode 100644 index 0000000..f1062c3 --- /dev/null +++ b/gc/fuzz/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "gc-fuzz" +version = "0.0.0" +authors = ["Automatically generated"] +publish = false +edition = "2018" + +[package.metadata] +cargo-fuzz = true + +[dependencies] +libfuzzer-sys = { version = "0.4", features = ["arbitrary-derive"] } + +[dependencies.gc] +path = ".." + +# Prevent this from interfering with workspaces +[workspace] +members = ["."] + +[[bin]] +name = "fuzz_target_1" +path = "fuzz_targets/fuzz_target_1.rs" +test = false +doc = false diff --git a/gc/fuzz/fuzz_targets/fuzz_target_1.rs b/gc/fuzz/fuzz_targets/fuzz_target_1.rs new file mode 100644 index 0000000..a981436 --- /dev/null +++ b/gc/fuzz/fuzz_targets/fuzz_target_1.rs @@ -0,0 +1,33 @@ +#![no_main] +use libfuzzer_sys::fuzz_target; +use gc::{self, test_utils::GotDropped}; + +use libfuzzer_sys::arbitrary::Arbitrary; + +#[derive(Arbitrary, Debug)] +enum AllocatorMethod { + Alloc, + Remove { + // Free the index^th allocation we've made. + index: usize + }, + CollectGarbage +} + +fuzz_target!(|ops: Vec| { + let allocator = gc::allocator::GCAllocator::new(); + for op in ops { + match op { + AllocatorMethod::Alloc => { + + }, + + AllocatorMethod::Remove {index} => { + }, + + AllocatorMethod::CollectGarbage => { + + } + } + } +}); diff --git a/gc/gctrace-derive/Cargo.toml b/gc/gctrace-derive/Cargo.toml new file mode 100644 index 0000000..a9df158 --- /dev/null +++ b/gc/gctrace-derive/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "gctrace-derive" +version = "0.1.0" +edition = "2021" + +[lib] +proc-macro = true + +[dependencies] +proc-macro2 = "1.0.40" +quote = "1.0.20" +syn = "1.0.98" diff --git a/gc/gctrace-derive/src/lib.rs b/gc/gctrace-derive/src/lib.rs new file mode 100644 index 0000000..12ce064 --- /dev/null +++ b/gc/gctrace-derive/src/lib.rs @@ -0,0 +1,75 @@ +use proc_macro::TokenStream; +use proc_macro2::TokenStream as TokenStream2; +use quote::{quote, quote_spanned}; +use syn::spanned::Spanned; +use syn::{ + parse_macro_input, parse_quote, Data, DeriveInput, Fields, GenericParam, Generics, Index, +}; + +// Add a bound `T: GcTrace` to every type parameter T. +fn add_trait_bounds(mut generics: Generics) -> Generics { + for param in &mut generics.params { + if let GenericParam::Type(ref mut type_param) = *param { + type_param.bounds.push(parse_quote!(gc::trace::GcTrace)); + } + } + generics +} + +// Generate an expression to call trace on every gc field recursively. +fn gctrace_fields(data: &Data) -> TokenStream2 { + match *data { + Data::Struct(ref data) => match data.fields { + Fields::Named(ref fields) => { + let recurse = fields.named.iter().map(|f| { + let name = &f.ident; + quote_spanned! {f.span()=> + gc::trace::GCTrace::trace(&self.#name, tracer) + } + }); + quote! { + #(#recurse;)* + } + } + Fields::Unnamed(ref fields) => { + let recurse = fields.unnamed.iter().enumerate().map(|(i, f)| { + let index = Index::from(i); + quote_spanned! {f.span()=> + gc::trace::GCTrace::trace(&self.#index, tracer) + } + }); + quote! { + #(#recurse;)* + } + } + Fields::Unit => TokenStream2::default(), + }, + Data::Enum(_) | Data::Union(_) => unimplemented!(), + } +} + +#[proc_macro_derive(GCTrace)] +pub fn gctrace_derive(input: TokenStream) -> TokenStream { + // Parse the input tokens into a syntax tree + let input = parse_macro_input!(input as DeriveInput); + + let name = input.ident; + + let generics = add_trait_bounds(input.generics); + + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + let trace_fields = gctrace_fields(&input.data); + + // Build the output, possibly using quasi-quotation + // + let expanded = quote! { + unsafe impl #impl_generics gc::trace::GCTrace for #name #ty_generics #where_clause { + fn trace(&self, tracer: &mut gc::trace::GCTracer) { + #trace_fields + } + } + }; + + // Hand the output tokens back to the compiler + TokenStream::from(expanded) +} diff --git a/gc/src/allocator.rs b/gc/src/allocator.rs index ad14de4..da98f0b 100644 --- a/gc/src/allocator.rs +++ b/gc/src/allocator.rs @@ -10,7 +10,7 @@ pub struct GCAllocator { impl GCAllocator { #[inline(always)] - pub fn alloc(&mut self, x: T) -> GcRef { + pub fn alloc(&mut self, x: T) -> GcRef { let alloc = Allocation::new(x); let ptr = alloc.ptr as *mut T; self.allocations.push(alloc); @@ -41,7 +41,7 @@ impl GCAllocator { pub unsafe fn gc_ref_root(&mut self, root: &GcRef) { // Mark let mut tracer = trace::GCTracer::with_capacity(self.allocations.len()); - tracer.mark_reachable(root); + tracer.mark_reachable_rec(root); root.trace(&mut tracer); // And sweep diff --git a/gc/src/gc_ref.rs b/gc/src/gc_ref.rs index 14f1700..7f5ae86 100644 --- a/gc/src/gc_ref.rs +++ b/gc/src/gc_ref.rs @@ -1,8 +1,11 @@ use std::{marker::PhantomData, ops::Deref, ptr::NonNull}; -pub struct GcRef(pub(crate) NonNull, PhantomData); +use crate::trace::GCTrace; -impl Deref for GcRef { +#[derive(Clone)] +pub struct GcRef(pub(crate) NonNull, PhantomData); + +impl Deref for GcRef { type Target = T; fn deref(&self) -> &Self::Target { @@ -10,7 +13,7 @@ impl Deref for GcRef { } } -impl GcRef { +impl GcRef { pub(crate) unsafe fn new(ptr: NonNull) -> Self { Self(ptr, PhantomData) } diff --git a/gc/src/lib.rs b/gc/src/lib.rs index f1c4c7b..2fc164c 100644 --- a/gc/src/lib.rs +++ b/gc/src/lib.rs @@ -1,50 +1,35 @@ #![feature(drain_filter)] +#![feature(negative_impls)] pub mod allocator; pub mod gc_ref; pub mod trace; +mod trace_impl; + +// make the test_utils mod pub to use it in fuzzing +pub mod test_utils; #[cfg(test)] -mod tests { +pub(crate) mod tests { use super::allocator::GCAllocator; - use super::gc_ref::GcRef; - use super::trace; - use std::sync::atomic::{AtomicBool, Ordering}; - - struct GCRoot<'a>(GcRef>); - - unsafe impl<'a> trace::GCTrace for GCRoot<'a> { - fn trace(&self, tracer: &mut trace::GCTracer) { - tracer.mark_reachable(&self.0) - } - } - - struct GotDropped<'a>(&'a AtomicBool); - impl<'a> Drop for GotDropped<'a> { - fn drop(&mut self) { - self.0.store(true, Ordering::Release); - } - } + use super::test_utils::GotDropped; #[test] fn it_works() { - let dropped = AtomicBool::from(false); - let got_dropped = GotDropped(&dropped); + let got_dropped = GotDropped::default(); let mut gc = GCAllocator::default(); - gc.alloc(got_dropped); - unsafe { gc.gc(&()) }; - assert!(dropped.load(Ordering::Acquire)); + gc.alloc(got_dropped.make_drop_checker()); - let dropped = AtomicBool::from(false); - let got_dropped = gc.alloc(GotDropped(&dropped)); - let gc_root = gc.alloc(GCRoot(got_dropped)); - - unsafe { - gc.gc_ref_root(&gc_root); - }; - assert!(!dropped.load(Ordering::Acquire)); unsafe { gc.gc(&()) }; - assert!(dropped.load(Ordering::Acquire)); + assert!(got_dropped.was_dropped()); + + let got_dropped = GotDropped::default(); + let drop_checker = gc.alloc(got_dropped.make_drop_checker()); + + unsafe { gc.gc(&(drop_checker,)) }; + assert!(!got_dropped.was_dropped()); + unsafe { gc.gc(&()) }; + assert!(got_dropped.was_dropped()); } } diff --git a/gc/src/test_utils.rs b/gc/src/test_utils.rs new file mode 100644 index 0000000..8225d92 --- /dev/null +++ b/gc/src/test_utils.rs @@ -0,0 +1,28 @@ +use std::sync::atomic::{AtomicBool, Ordering}; + +use crate::trace::GCTrace; + +#[derive(Default)] +pub struct GotDropped(AtomicBool); + +impl GotDropped { + pub fn make_drop_checker(&self) -> DropChecker { + DropChecker(self) + } + + pub fn was_dropped(&self) -> bool { + self.0.load(Ordering::SeqCst) + } +} + +pub struct DropChecker<'a>(pub &'a GotDropped); + +impl<'a> Drop for DropChecker<'a> { + fn drop(&mut self) { + self.0 .0.store(true, Ordering::SeqCst); + } +} + +unsafe impl<'a> GCTrace for DropChecker<'a> { + fn trace(&self, _tracer: &mut crate::trace::GCTracer) {} +} diff --git a/gc/src/trace.rs b/gc/src/trace.rs index 0e5b57b..3d7a76d 100644 --- a/gc/src/trace.rs +++ b/gc/src/trace.rs @@ -1,15 +1,17 @@ -use std::collections::HashSet; +use std::{collections::HashSet, ops::Deref}; use crate::gc_ref::GcRef; pub struct GCTracer { accessible: HashSet<*const ()>, + explored: HashSet<*const ()>, } impl GCTracer { pub(super) fn with_capacity(cap: usize) -> Self { Self { accessible: HashSet::with_capacity(cap), + explored: HashSet::with_capacity(cap), } } @@ -17,50 +19,28 @@ impl GCTracer { self.accessible.contains(&ptr) } - pub fn mark_reachable(&mut self, obj: &GcRef) { - let ptr = obj.0.as_ptr() as *const (); - self.accessible.insert(ptr); - } - - pub fn mark_reachable_rec(&mut self, obj: &GcRef) { - let ptr = obj.0.as_ptr() as *const (); - if !self.accessible.contains(&ptr) { - self.accessible.insert(ptr); + pub fn mark_reachable_rec(&mut self, obj: &T) { + let ptr = obj as *const T as *const (); + if !self.explored.contains(&ptr) { + self.explored.insert(ptr); obj.trace(self); } } } +pub use gctrace_derive::GCTrace; + /// /// # Safety /// Implementors of the trait *need* to ensure that every reachable reference gets marked as -/// reachable with mark_reachable or mark_reachable_rec. +/// reachable by callign trace on it pub unsafe trait GCTrace { - fn trace(&self, tracer: &mut GCTracer); -} - -unsafe impl GCTrace for &[T] -where - T: GCTrace, -{ - fn trace(&self, tracer: &mut GCTracer) { - for item in self.iter() { - item.trace(tracer) - } - } -} - -unsafe impl GCTrace for Option -where - T: GCTrace, -{ - fn trace(&self, t: &mut GCTracer) { - if let Some(ref v) = self { - v.trace(t); - } - } -} - -unsafe impl GCTrace for () { fn trace(&self, _tracer: &mut GCTracer) {} } + +unsafe impl GCTrace for GcRef { + fn trace(&self, tracer: &mut GCTracer) { + tracer.accessible.insert(self.0.as_ptr() as *const ()); + tracer.mark_reachable_rec(self.deref()); + } +} diff --git a/gc/src/trace_impl.rs b/gc/src/trace_impl.rs new file mode 100644 index 0000000..d264fa3 --- /dev/null +++ b/gc/src/trace_impl.rs @@ -0,0 +1,42 @@ +use super::trace::{GCTrace, GCTracer}; +use std::{cell::RefCell, collections::HashMap, ops::Deref}; +unsafe impl GCTrace for () {} +unsafe impl GCTrace for i32 {} +unsafe impl GCTrace for u32 {} +unsafe impl GCTrace for i64 {} +unsafe impl GCTrace for u64 {} +unsafe impl GCTrace for f32 {} +unsafe impl GCTrace for f64 {} +unsafe impl GCTrace for isize {} +unsafe impl GCTrace for usize {} +unsafe impl GCTrace for String {} + +unsafe impl GCTrace for (T,) { + fn trace(&self, tracer: &mut GCTracer) { + tracer.mark_reachable_rec(&self.0) + } +} + +unsafe impl GCTrace for RefCell { + fn trace(&self, tracer: &mut GCTracer) { + let borrow = self.borrow(); + tracer.mark_reachable_rec(borrow.deref()) + } +} + +unsafe impl GCTrace for Option { + fn trace(&self, tracer: &mut GCTracer) { + if let Some(ref v) = self { + tracer.mark_reachable_rec(v) + } + } +} + +unsafe impl GCTrace for HashMap { + fn trace(&self, tracer: &mut GCTracer) { + for (k, v) in self.iter() { + tracer.mark_reachable_rec(k); + tracer.mark_reachable_rec(v); + } + } +} diff --git a/src/error.rs b/src/error.rs index e286045..4a1b4fc 100644 --- a/src/error.rs +++ b/src/error.rs @@ -52,6 +52,7 @@ impl ErrorLocationWrapper { impl ErrorWithLocation for ErrorLocationWrapper { fn get_location(&self) -> Location { + #[allow(clippy::needless_borrow)] (&self.location).into() } } diff --git a/src/interpreter/ast_walker/expression_interpreter.rs b/src/interpreter/ast_walker/expression_interpreter.rs index bc7def2..caa46b4 100644 --- a/src/interpreter/ast_walker/expression_interpreter.rs +++ b/src/interpreter/ast_walker/expression_interpreter.rs @@ -1,5 +1,5 @@ use super::Interpret; -use super::{types::Value, RuntimeError}; +use super::{types::Primitive, RuntimeError}; use crate::ast::expression::{ self, operator::{Operator, UnaryOperator}, @@ -7,19 +7,19 @@ use crate::ast::expression::{ use crate::interpreter::world::World; impl Interpret for expression::ExpressionNode { - fn interpret(&self, w: &mut World) -> Result { + fn interpret(&self, w: &mut World) -> Result { expression::all_variants!(self, n => n.interpret(w)) } } impl Interpret for expression::Literal { - fn interpret(&self, _: &mut World) -> Result { + fn interpret(&self, _: &mut World) -> Result { Ok(self.clone().into()) } } impl Interpret for expression::BinaryExpr { - fn interpret(&self, w: &mut World) -> Result { + fn interpret(&self, w: &mut World) -> Result { let left_val = self.left.interpret(w).expect("expected lval"); let right_val = self.right.interpret(w).expect("expected rval"); match self.operator { @@ -36,13 +36,13 @@ impl Interpret for expression::BinaryExpr { } impl Interpret for expression::UnaryExpr { - fn interpret(&self, w: &mut World) -> Result { + fn interpret(&self, w: &mut World) -> Result { let val = self.right.interpret(w)?; match self.operator { - UnaryOperator::Bang => Ok(Value::Bool(!val.truthy())), + UnaryOperator::Bang => Ok(Primitive::Bool(!val.truthy())), UnaryOperator::Minus => match val { - Value::Int(i) => Ok(Value::Int(-i)), - Value::Float(f) => Ok(Value::Float(-f)), + Primitive::Int(i) => Ok(Primitive::Int(-i)), + Primitive::Float(f) => Ok(Primitive::Float(-f)), _ => Err(RuntimeError), }, } @@ -50,25 +50,25 @@ impl Interpret for expression::UnaryExpr { } impl Interpret for expression::GroupingExpr { - fn interpret(&self, w: &mut World) -> Result { + fn interpret(&self, w: &mut World) -> Result { self.0.interpret(w) } } impl Interpret for expression::VariableExpr { - fn interpret(&self, world: &mut World) -> Result { + fn interpret(&self, world: &mut World) -> Result { match world.get_var(&self.var_name) { - Some(v) => Ok(v.clone()), + Some(v) => Ok(v), None => Err(RuntimeError), } } } impl Interpret for expression::BlockExpr { - fn interpret(&self, world: &mut World) -> Result { + fn interpret(&self, world: &mut World) -> Result { for stmnt in self.statements.iter() { stmnt.interpret(world)?; } - Ok(Value::Nil) + Ok(Primitive::Nil) } } diff --git a/src/interpreter/ast_walker/mod.rs b/src/interpreter/ast_walker/mod.rs index 2a2ef22..d4c6f97 100644 --- a/src/interpreter/ast_walker/mod.rs +++ b/src/interpreter/ast_walker/mod.rs @@ -1,8 +1,8 @@ mod expression_interpreter; mod statement_interpreter; pub use super::{error::RuntimeError, types}; -use super::{types::Value, world::World}; +use super::{types::Primitive, world::World}; pub trait Interpret { - fn interpret(&self, world: &mut World) -> Result; + fn interpret(&self, world: &mut World) -> Result; } diff --git a/src/interpreter/ast_walker/statement_interpreter.rs b/src/interpreter/ast_walker/statement_interpreter.rs index 5cf4a62..83ca719 100644 --- a/src/interpreter/ast_walker/statement_interpreter.rs +++ b/src/interpreter/ast_walker/statement_interpreter.rs @@ -1,16 +1,16 @@ use super::Interpret; -use super::{types::Value, RuntimeError}; +use super::{types::Primitive, RuntimeError}; use crate::ast::statement; use crate::interpreter::world::World; impl Interpret for statement::Statement { - fn interpret(&self, w: &mut World) -> Result { + fn interpret(&self, w: &mut World) -> Result { statement::all_variants!(self, n => n.interpret(w)) } } impl Interpret for statement::PrintStatement { - fn interpret(&self, w: &mut World) -> Result { + fn interpret(&self, w: &mut World) -> Result { let res = self.0.interpret(w)?; println!("{:?}", res); Ok(res) @@ -18,13 +18,13 @@ impl Interpret for statement::PrintStatement { } impl Interpret for statement::ExpressionStatement { - fn interpret(&self, w: &mut World) -> Result { + fn interpret(&self, w: &mut World) -> Result { self.0.interpret(w) } } impl Interpret for statement::VariableAssignmentStatement { - fn interpret(&self, w: &mut World) -> Result { + fn interpret(&self, w: &mut World) -> Result { let expr_val = self.node.interpret(w)?; // Clone for now, later this will use a GC and won't need to clone w.set_var(self.var_name.clone(), expr_val.clone()); diff --git a/src/interpreter/types.rs b/src/interpreter/types.rs index 10ac1f4..ba8e0e5 100644 --- a/src/interpreter/types.rs +++ b/src/interpreter/types.rs @@ -1,8 +1,9 @@ use crate::ast::expression; use from_variants::FromVariants; +use gc::trace::GCTrace; #[derive(Debug, PartialEq, PartialOrd, Clone, FromVariants)] -pub enum Value { +pub enum Primitive { Int(i32), Float(f32), Bool(bool), @@ -11,7 +12,9 @@ pub enum Value { String(String), } -impl From for Value { +unsafe impl GCTrace for Primitive {} + +impl From for Primitive { fn from(l: expression::Literal) -> Self { match_any::match_any!(l, expression::Literal::Int(v) | expression::Literal::Bool(v) | expression::Literal::Float(v) | expression::Literal::String(v) => v.into(), @@ -20,13 +23,13 @@ impl From for Value { } } -impl Default for Value { +impl Default for Primitive { fn default() -> Self { Self::Nil } } -impl Value { +impl Primitive { pub fn truthy(&self) -> bool { !(matches!(*self, Self::Bool(false))) } diff --git a/src/interpreter/world.rs b/src/interpreter/world.rs index b462603..ef89c5c 100644 --- a/src/interpreter/world.rs +++ b/src/interpreter/world.rs @@ -1,23 +1,44 @@ -use std::collections::HashMap; +use std::{cell::RefCell, collections::HashMap}; + +use gc::{allocator::GCAllocator, gc_ref::GcRef, trace::GCTrace}; use crate::ast::statement::Statement; use super::{ ast_walker::{Interpret, RuntimeError}, - types::Value, + types::Primitive, }; -#[derive(Default)] +#[derive(Default, GCTrace)] +pub struct Environment { + variables: HashMap, + parent: Option>>, +} + +impl Environment { + pub fn set_var(&mut self, name: String, v: Primitive) -> Option { + self.variables.insert(name, v) + } + pub fn get_var(&self, name: &str) -> Option { + self.variables + .get(name) + .cloned() + .or_else(|| self.parent.as_ref().and_then(|v| v.borrow().get_var(name))) + } +} + pub struct World { - variables: HashMap, + env: GcRef, + _gc: GCAllocator, } impl World { - pub fn set_var(&mut self, name: String, v: Value) -> Option { - self.variables.insert(name, v) + pub fn set_var(&mut self, _name: String, _v: Primitive) -> Option { + todo!() + // self.env.set_var(name, v) } - pub fn get_var(&mut self, name: &str) -> Option<&Value> { - self.variables.get(name) + pub fn get_var(&self, name: &str) -> Option { + self.env.get_var(name) } } @@ -25,11 +46,17 @@ impl World { pub fn new() -> Self { Self::default() } - pub fn exec(&mut self, nodes: Vec) -> Result { - let mut last_res = Value::Nil; - for statement in nodes { - last_res = statement.interpret(self)?; - } - Ok(last_res) + pub fn exec(&mut self, nodes: Vec) -> Result { + nodes + .into_iter() + .try_fold(Primitive::Nil, |_, stmnt| stmnt.interpret(self)) + } +} + +impl Default for World { + fn default() -> Self { + let mut gc = GCAllocator::default(); + let env = gc.alloc(Environment::default()); + Self { env, _gc: gc } } } diff --git a/src/lexer/mod.rs b/src/lexer/mod.rs index c066cf2..22a1b3a 100644 --- a/src/lexer/mod.rs +++ b/src/lexer/mod.rs @@ -87,7 +87,7 @@ impl<'a, 'b> Lexer<'a, 'b> { } found_period = true; } - c.is_digit(10) || c == '.' + c.is_ascii_digit() || c == '.' }); let res = if found_period { num_str diff --git a/src/lib.rs b/src/lib.rs index 3a6b0be..5245f61 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,7 +7,7 @@ pub mod lexer; use ast::parser::ParseAllResult; use ast::statement::Statement; use interpreter::ast_walker::{Interpret, RuntimeError}; -use interpreter::types::Value; +use interpreter::types::Primitive; use interpreter::world::World; use lexer::{token::Token, Lexer, LexingError}; @@ -24,8 +24,8 @@ pub fn parse(tokens: Vec) -> ParseAllResult { parser.parse_all() } -pub fn exec(nodes: Vec) -> Result { - let mut last_res = Value::Nil; +pub fn exec(nodes: Vec) -> Result { + let mut last_res = Primitive::Nil; let mut world = World::new(); for statement in nodes { last_res = statement.interpret(&mut world)?; @@ -33,7 +33,7 @@ pub fn exec(nodes: Vec) -> Result { Ok(last_res) } -pub fn run(code: &str) -> Result { +pub fn run(code: &str) -> Result { let tokens = lex(code, None)?; let nodes = parse(tokens)?; Ok(exec(nodes)?)