Skip to content

Commit 1ac2271

Browse files
committed
debug trait and its auto implementation
1 parent d22ff45 commit 1ac2271

File tree

9 files changed

+454
-16
lines changed

9 files changed

+454
-16
lines changed

forc-test/src/execute.rs

+43-6
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,12 @@ use forc_pkg::PkgTestEntry;
66
use fuel_tx::{self as tx, output::contract::Contract, Chargeable, Finalizable};
77
use fuel_vm::error::InterpreterError;
88
use fuel_vm::fuel_asm;
9+
use fuel_vm::interpreter::EcalHandler;
910
use fuel_vm::prelude::Instruction;
1011
use fuel_vm::prelude::RegId;
1112
use fuel_vm::{
12-
self as vm,
13-
checked_transaction::builder::TransactionBuilderExt,
14-
interpreter::{Interpreter, NotSupportedEcal},
15-
prelude::SecretKey,
16-
storage::MemoryStorage,
13+
self as vm, checked_transaction::builder::TransactionBuilderExt, interpreter::Interpreter,
14+
prelude::SecretKey, storage::MemoryStorage,
1715
};
1816
use rand::{Rng, SeedableRng};
1917

@@ -23,10 +21,49 @@ use vm::interpreter::{InterpreterParams, MemoryInstance};
2321
use vm::state::DebugEval;
2422
use vm::state::ProgramState;
2523

24+
#[derive(Debug, Clone, Default)]
25+
pub struct EcalState {}
26+
27+
impl EcalHandler for EcalState {
28+
fn ecal<M, S, Tx>(
29+
vm: &mut Interpreter<M, S, Tx, Self>,
30+
a: RegId,
31+
b: RegId,
32+
c: RegId,
33+
d: RegId,
34+
) -> fuel_vm::error::SimpleResult<()>
35+
where
36+
M: fuel_vm::prelude::Memory,
37+
{
38+
let regs = vm.registers();
39+
match regs[a.to_u8() as usize] {
40+
1000 => {
41+
let addr = regs[c.to_u8() as usize];
42+
let count = regs[d.to_u8() as usize];
43+
44+
let s = vm.memory().read(addr, count).unwrap();
45+
let s = std::str::from_utf8(s).unwrap();
46+
47+
use std::io::Write;
48+
use std::os::fd::FromRawFd;
49+
let fd = regs[b.to_u8() as usize];
50+
51+
let mut f = unsafe { std::fs::File::from_raw_fd(fd as i32) };
52+
write!(&mut f, "{}", s).unwrap();
53+
54+
// Dont close the fd
55+
std::mem::forget(f);
56+
}
57+
_ => todo!(),
58+
}
59+
Ok(())
60+
}
61+
}
62+
2663
/// An interface for executing a test within a VM [Interpreter] instance.
2764
#[derive(Debug, Clone)]
2865
pub struct TestExecutor {
29-
pub interpreter: Interpreter<MemoryInstance, MemoryStorage, tx::Script, NotSupportedEcal>,
66+
pub interpreter: Interpreter<MemoryInstance, MemoryStorage, tx::Script, EcalState>,
3067
pub tx: vm::checked_transaction::Ready<tx::Script>,
3168
pub test_entry: PkgTestEntry,
3269
pub name: String,

sway-core/src/semantic_analysis/ast_node/declaration/auto_impl/abi_encoding.rs

+8-4
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ where
190190
}
191191

192192
// Auto implements AbiEncode and AbiDecode for structs and returns their `AstNode`s.
193-
fn auto_impl_struct(
193+
fn abi_encode_and_decode_auto_impl_struct(
194194
&mut self,
195195
engines: &Engines,
196196
decl: &TyDecl,
@@ -225,7 +225,7 @@ where
225225
Some((abi_encode_node.ok(), abi_decode_node.ok()))
226226
}
227227

228-
fn auto_impl_enum(
228+
fn abi_encode_and_decode_auto_impl_enum(
229229
&mut self,
230230
engines: &Engines,
231231
decl: &TyDecl,
@@ -266,8 +266,12 @@ where
266266
decl: &ty::TyDecl,
267267
) -> (Option<TyAstNode>, Option<TyAstNode>) {
268268
match decl {
269-
TyDecl::StructDecl(_) => self.auto_impl_struct(engines, decl).unwrap_or((None, None)),
270-
TyDecl::EnumDecl(_) => self.auto_impl_enum(engines, decl).unwrap_or((None, None)),
269+
TyDecl::StructDecl(_) => self
270+
.abi_encode_and_decode_auto_impl_struct(engines, decl)
271+
.unwrap_or((None, None)),
272+
TyDecl::EnumDecl(_) => self
273+
.abi_encode_and_decode_auto_impl_enum(engines, decl)
274+
.unwrap_or((None, None)),
271275
_ => (None, None),
272276
}
273277
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
use sway_error::handler::Handler;
2+
use sway_types::{BaseIdent, Named, Spanned};
3+
4+
use crate::{
5+
decl_engine::DeclEngineGet,
6+
language::ty::{self, TyAstNode, TyDecl, TyStructDecl},
7+
Engines, TypeParameter,
8+
};
9+
10+
use super::abi_encoding::AbiEncodingAutoImplInfo;
11+
12+
pub type DebugAutoImplContext<'a, 'b> = super::AutoImplContext<'a, 'b, AbiEncodingAutoImplInfo>;
13+
14+
impl<'a, 'b> DebugAutoImplContext<'a, 'b>
15+
where
16+
'a: 'b,
17+
{
18+
pub fn generate_debug_impl(
19+
&mut self,
20+
engines: &Engines,
21+
decl: &ty::TyDecl,
22+
) -> Option<TyAstNode> {
23+
match decl {
24+
TyDecl::StructDecl(_) => self.debug_auto_impl_struct(engines, decl).unwrap_or(None),
25+
TyDecl::EnumDecl(_) => None,
26+
_ => None,
27+
}
28+
}
29+
30+
// Auto implements AbiEncode and AbiDecode for structs and returns their `AstNode`s.
31+
fn debug_auto_impl_struct(
32+
&mut self,
33+
engines: &Engines,
34+
decl: &TyDecl,
35+
) -> Option<Option<TyAstNode>> {
36+
if self.ctx.namespace.current_package_name().as_str() == "core" {
37+
return Some(None);
38+
}
39+
40+
let implementing_for_decl_id = decl.to_struct_decl(&Handler::default(), engines).unwrap();
41+
let struct_decl = self.ctx.engines().de().get(&implementing_for_decl_id);
42+
43+
let program_id = struct_decl.span().source_id().map(|sid| sid.program_id());
44+
45+
let abi_encode_body = self.generate_fmt_struct_body(engines, &struct_decl);
46+
let abi_encode_code = self.generate_fmt_code(
47+
struct_decl.name(),
48+
&struct_decl.type_parameters,
49+
abi_encode_body,
50+
);
51+
let abi_encode_node =
52+
self.parse_impl_trait_to_ty_ast_node(engines, program_id, &abi_encode_code);
53+
54+
Some(abi_encode_node.ok())
55+
}
56+
57+
fn generate_fmt_code(
58+
&self,
59+
name: &BaseIdent,
60+
type_parameters: &[TypeParameter],
61+
body: String,
62+
) -> String {
63+
let type_parameters_declaration =
64+
self.generate_type_parameters_declaration_code(type_parameters);
65+
let type_parameters_constraints =
66+
self.generate_type_parameters_constraints_code(type_parameters, "Debug");
67+
68+
let name = name.as_str();
69+
70+
if body.is_empty() {
71+
format!("#[allow(dead_code)] impl{type_parameters_declaration} Debug for {name}{type_parameters_declaration}{type_parameters_constraints} {{
72+
#[allow(dead_code)]
73+
fn fmt(self, ref mut f: Formatter) {{ }}
74+
}}")
75+
} else {
76+
format!("#[allow(dead_code)] impl{type_parameters_declaration} Debug for {name}{type_parameters_declaration}{type_parameters_constraints} {{
77+
#[allow(dead_code)]
78+
fn fmt(self, ref mut f: Formatter) {{
79+
f.debug_struct(\"{name}\")
80+
{body}
81+
.finish();
82+
}}
83+
}}")
84+
}
85+
}
86+
87+
fn generate_fmt_struct_body(&self, _engines: &Engines, decl: &TyStructDecl) -> String {
88+
let mut code = String::new();
89+
90+
for f in decl.fields.iter() {
91+
code.push_str(&format!(
92+
".field(\"{field_name}\", self.{field_name})\n",
93+
field_name = f.name.as_str(),
94+
));
95+
}
96+
97+
code
98+
}
99+
}

sway-core/src/semantic_analysis/ast_node/declaration/auto_impl/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//! This module contains common infrastructure for generating and parsing auto-generated code.
22
pub mod abi_encoding;
3+
pub mod debug;
34
pub mod marker_traits;
45

56
use crate::{

sway-core/src/semantic_analysis/module.rs

+29-6
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ use crate::{
2828

2929
use super::{
3030
declaration::auto_impl::{
31-
abi_encoding::AbiEncodingAutoImplContext, marker_traits::MarkerTraitsAutoImplContext,
31+
abi_encoding::AbiEncodingAutoImplContext, debug::DebugAutoImplContext,
32+
marker_traits::MarkerTraitsAutoImplContext,
3233
},
3334
symbol_collection_context::SymbolCollectionContext,
3435
};
@@ -542,20 +543,29 @@ impl ty::TyModule {
542543
let all_abiencode_impls = Self::get_all_impls(ctx.by_ref(), nodes, |decl| {
543544
decl.trait_name.suffix.as_str() == "AbiEncode"
544545
});
546+
let all_debug_impls = Self::get_all_impls(ctx.by_ref(), nodes, |decl| {
547+
decl.trait_name.suffix.as_str() == "Debug"
548+
});
545549

546550
let mut typed_nodes = vec![];
547551
for node in nodes {
548-
// Check if the encoding traits are explicitly implemented.
549-
let auto_impl_encoding_traits = match &node.content {
552+
// Check if the encoding and debug traits are explicitly implemented.
553+
let (auto_impl_encoding_traits, auto_impl_debug_traits) = match &node.content {
550554
AstNodeContent::Declaration(Declaration::StructDeclaration(decl_id)) => {
551555
let decl = ctx.engines().pe().get_struct(decl_id);
552-
!all_abiencode_impls.contains_key(&decl.name)
556+
(
557+
!all_abiencode_impls.contains_key(&decl.name),
558+
!all_debug_impls.contains_key(&decl.name),
559+
)
553560
}
554561
AstNodeContent::Declaration(Declaration::EnumDeclaration(decl_id)) => {
555562
let decl = ctx.engines().pe().get_enum(decl_id);
556-
!all_abiencode_impls.contains_key(&decl.name)
563+
(
564+
!all_abiencode_impls.contains_key(&decl.name),
565+
!all_debug_impls.contains_key(&decl.name),
566+
)
557567
}
558-
_ => false,
568+
_ => (false, false),
559569
};
560570

561571
let Ok(node) = ty::TyAstNode::type_check(handler, ctx.by_ref(), node) else {
@@ -581,6 +591,19 @@ impl ty::TyModule {
581591
};
582592
}
583593

594+
// Auto impl debug traits only if they are not explicitly implemented
595+
if auto_impl_debug_traits {
596+
match &node.content {
597+
TyAstNodeContent::Declaration(decl @ TyDecl::StructDecl(_))
598+
| TyAstNodeContent::Declaration(decl @ TyDecl::EnumDecl(_)) => {
599+
let mut ctx = DebugAutoImplContext::new(&mut ctx);
600+
let a = ctx.generate_debug_impl(engines, decl);
601+
generated.extend(a);
602+
}
603+
_ => {}
604+
}
605+
}
606+
584607
// Always auto impl marker traits. If an explicit implementation exists, that will be
585608
// reported as an error when type-checking trait impls.
586609
if ctx.experimental.error_type {

0 commit comments

Comments
 (0)