Repository: huff-language/huff-rs
Pull request #129 (2022-06-24 18:21:56)

fix(huff_core): Allow Missing Constructor
## Overview

As detailed in #126 , `huffc` errors when there is no `CONSTRUCTOR` macro defined. This should not be the behavior when no constructor inputs are passed. `huffc` should produce valid bytecode when there is no constructor present and fail in this case if constructor inputs are still provided.


Patch:
diff --git a/huff_core/src/lib.rs b/huff_core/src/lib.rs
index 85274e8..ca56334 100644
--- a/huff_core/src/lib.rs
+++ b/huff_core/src/lib.rs
@@ -215,11 +215,10 @@ impl<'a> Compiler {
                 e.span = AstSpan(
                     e.span
                         .0
-                        .iter()
-                        .map(|s| {
-                            let mut n_s = s.clone();
-                            n_s.file = Some(Arc::clone(&file));
-                            n_s
+                        .into_iter()
+                        .map(|mut s| {
+                            s.file = Some(Arc::clone(&file));
+                            s
                         })
                         .collect::<Vec<Span>>(),
                 );
@@ -228,29 +227,32 @@ impl<'a> Compiler {
             }
         };
         tracing::info!(target: "core", "MAIN BYTECODE GENERATED [{}]", main_bytecode);
+        let inputs = self.get_constructor_args();
         let constructor_bytecode = match Codegen::generate_constructor_bytecode(&contract) {
             Ok(mb) => mb,
             Err(mut e) => {
-                // Add File Source to Span
-                e.span = AstSpan(
-                    e.span
-                        .0
-                        .iter()
-                        .map(|s| {
-                            let mut n_s = s.clone();
-                            n_s.file = Some(Arc::clone(&file));
-                            n_s
-                        })
-                        .collect::<Vec<Span>>(),
-                );
-                tracing::error!(target: "codegen", "Construct Failed with CodegenError: {:?}", e);
-                return Err(CompilerError::CodegenError(e))
+                if !inputs.is_empty() {
+                    // Add File Source to Span
+                    e.span = AstSpan(
+                        e.span
+                            .0
+                            .into_iter()
+                            .map(|mut s| {
+                                s.file = Some(Arc::clone(&file));
+                                s
+                            })
+                            .collect::<Vec<Span>>(),
+                    );
+                    tracing::error!(target: "codegen", "Constructor inputs provided, but contract missing \"CONSTRUCTOR\" macro!");
+                    return Err(CompilerError::CodegenError(e))
+                }
+                tracing::warn!(target: "codegen", "Contract has no \"CONSTRUCTOR\" macro definition!");
+                "".to_string()
             }
         };
 
         // Encode Constructor Arguments
         tracing::info!(target: "core", "CONSTRUCTOR BYTECODE GENERATED [{}]", constructor_bytecode);
-        let inputs = self.get_constructor_args();
         tracing::info!(target: "core", "ENCODING {} INPUTS", inputs.len());
         let encoded_inputs = Codegen::encode_constructor_args(inputs);
         tracing::info!(target: "core", "ENCODED {} INPUTS", encoded_inputs.len());


Test patch:
diff --git a/huff_core/tests/errors.rs b/huff_core/tests/errors.rs
index 669436f..d647f47 100644
--- a/huff_core/tests/errors.rs
+++ b/huff_core/tests/errors.rs
@@ -186,7 +186,7 @@ fn test_missing_constructor() {
     let mut contract = parser.parse().unwrap();
     contract.derive_storage_pointers();
 
-    // Createconstructor bytecode
+    // Create constructor bytecode
     match Codegen::generate_constructor_bytecode(&contract) {
         Ok(_) => panic!("moose"),
         Err(e) => {
diff --git a/huff_core/tests/gen_artifact.rs b/huff_core/tests/gen_artifact.rs
new file mode 100644
index 0000000..d4305bb
--- /dev/null
+++ b/huff_core/tests/gen_artifact.rs
@@ -0,0 +1,96 @@
+use std::sync::Arc;
+
+use huff_core::Compiler;
+use huff_utils::prelude::*;
+
+#[test]
+fn test_missing_constructor() {
+    let source = r#"
+    #define macro MINT() = takes(0) returns (0) {
+        0x04 calldataload   // [to]
+        0x00                // [from (0x00), to]
+        0x24 calldataload   // [value, from, to]
+    }
+
+    #define macro MAIN() = takes(0) returns (0) {
+        0x00 calldataload 0xE0 shr
+        dup1 0x40c10f19 eq mints jumpi
+
+        mints:
+            MINT()
+    }
+    "#;
+
+    // Full source
+    let full_source = FileSource {
+        source: Some(source.to_string()),
+        id: uuid::Uuid::new_v4(),
+        path: "".to_string(),
+        access: None,
+        dependencies: None,
+    };
+
+    // Instantiate a new compiler
+    let compiler = Compiler::new(Arc::new(vec![]), None, None, false);
+
+    // Generate the compile artifact
+    let arc_source = Arc::new(full_source);
+    match compiler.gen_artifact(Arc::clone(&arc_source)) {
+        Ok(artifact) => {
+            assert_eq!(artifact.file, arc_source);
+            assert_eq!(
+                artifact.bytecode,
+                "61001a8061000d6000396000f360003560e01c806340c10f1914610011575b6004356000602435"
+                    .to_string()
+            );
+        }
+        _ => panic!("moose"),
+    }
+}
+
+#[test]
+fn test_missing_constructor_with_inputs() {
+    let source = r#"
+    #define macro MINT() = takes(0) returns (0) {
+        0x04 calldataload   // [to]
+        0x00                // [from (0x00), to]
+        0x24 calldataload   // [value, from, to]
+    }
+
+    #define macro MAIN() = takes(0) returns (0) {
+        0x00 calldataload 0xE0 shr
+        dup1 0x40c10f19 eq mints jumpi
+
+        mints:
+            MINT()
+    }
+    "#;
+
+    // Full source
+    let full_source = FileSource {
+        source: Some(source.to_string()),
+        id: uuid::Uuid::new_v4(),
+        path: "".to_string(),
+        access: None,
+        dependencies: None,
+    };
+
+    // Instantiate a new compiler
+    let compiler = Compiler::new(Arc::new(vec![]), None, Some(vec!["0".to_string()]), false);
+
+    // Generate the compile artifact
+    let arc_source = Arc::new(full_source);
+    match compiler.gen_artifact(Arc::clone(&arc_source)) {
+        Ok(_) => panic!("moose"),
+        Err(e) => {
+            assert_eq!(
+                e,
+                CompilerError::CodegenError(CodegenError {
+                    kind: CodegenErrorKind::MissingMacroDefinition("CONSTRUCTOR".to_string()),
+                    span: AstSpan(vec![Span { start: 0, end: 0, file: Some(arc_source) }]),
+                    token: None
+                })
+            )
+        }
+    }
+}