Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Checks for the Length of Arguments List #533

Merged
merged 23 commits into from
Oct 28, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
9ca3149
feat: added checks for the length of arguments list
BowTiedWoo Oct 14, 2024
43ecb90
feat: add error variant for invalid argument length
BowTiedWoo Oct 14, 2024
117e02b
feat: add tests for length of args list
BowTiedWoo Oct 14, 2024
2cdaa75
Merge branch 'main' into feat/check-args-length
BowTiedWoo Oct 15, 2024
4c8655d
feat: add runtime error for arg length and update tests
BowTiedWoo Oct 18, 2024
c0e0fef
Merge branch 'main' into feat/check-args-length
BowTiedWoo Oct 18, 2024
552f79f
feat: modify arg checks and arg count errors
BowTiedWoo Oct 18, 2024
4866a0e
fix: remove code duplications
BowTiedWoo Oct 18, 2024
3f893c1
fix: add functions for checking arg length
BowTiedWoo Oct 18, 2024
fe2af84
fix: refactor to remove code duplication
BowTiedWoo Oct 21, 2024
db6ae8f
fix: update tests
BowTiedWoo Oct 21, 2024
ea22f47
Merge branch 'main' into feat/check-args-length
BowTiedWoo Oct 21, 2024
40dacf7
chore: remove prints
BowTiedWoo Oct 22, 2024
a1c8206
chore: add comments on tests where typechecker fails
BowTiedWoo Oct 22, 2024
01bd418
Merge branch 'main' into feat/check-args-length
BowTiedWoo Oct 22, 2024
8be2284
Merge branch 'main' into feat/check-args-length
BowTiedWoo Oct 22, 2024
e0c678e
fix: update tests to use crosscheck
BowTiedWoo Oct 22, 2024
6bf5b05
fix: format code
BowTiedWoo Oct 23, 2024
90bfb36
fix: remove prints
BowTiedWoo Oct 23, 2024
b415867
feat: short-cut traverse function
BowTiedWoo Oct 23, 2024
5c2afe2
feat: add arg count check for user defined fns
BowTiedWoo Oct 24, 2024
e8936cc
Merge branch 'main' into feat/check-args-length
BowTiedWoo Oct 24, 2024
341ec46
feat: read/write arg lengths from/to memory as bytes
BowTiedWoo Oct 25, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions clar2wasm/src/error_mapping.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ pub enum ErrorMap {
/// Indicates an attempt to use a name that is already in use, possibly for a variable or function.
NameAlreadyUsed = 9,

/// Indicates an attempt to use a function with the wrong amount of arguments
ArgumentCountMismatch = 10,

/// A catch-all for errors that are not mapped to specific error codes.
/// This might be used for unexpected or unclassified errors.
NotMapped = 99,
Expand All @@ -73,6 +76,7 @@ impl From<i32> for ErrorMap {
7 => ErrorMap::ShortReturnAssertionFailure,
8 => ErrorMap::ArithmeticPowError,
9 => ErrorMap::NameAlreadyUsed,
10 => ErrorMap::ArgumentCountMismatch,
_ => ErrorMap::NotMapped,
}
}
Expand Down Expand Up @@ -225,6 +229,35 @@ fn from_runtime_error_code(

Error::Unchecked(CheckErrors::NameAlreadyUsed(arg_name))
}
ErrorMap::ArgumentCountMismatch => {
let runtime_error_arg_offset = instance
.get_global(&mut store, "runtime-error-arg-offset")
.and_then(|glob| glob.get(&mut store).i32())
.unwrap_or_else(|| {
panic!("Could not find $runtime-error-arg-offset global with i32 value")
});
let runtime_error_arg_len = instance
.get_global(&mut store, "runtime-error-arg-len")
.and_then(|glob| glob.get(&mut store).i32())
.unwrap_or_else(|| {
panic!("Could not find $runtime-error-arg-len global with i32 value")
});
let memory = instance
.get_memory(&mut store, "memory")
.unwrap_or_else(|| panic!("Could not find wasm instance memory"));
let arg_lengths = read_identifier_from_wasm(
memory,
&mut store,
runtime_error_arg_offset,
runtime_error_arg_len,
)
.unwrap_or_else(|e| panic!("Could not recover arg_name: {e}"));
let re = regex::Regex::new(r"expected: (\d+) got: (\d+)").unwrap();
let captures = re.captures(&arg_lengths).unwrap();
let expected: usize = captures[1].parse().unwrap();
let got: usize = captures[2].parse().unwrap();
Error::Unchecked(CheckErrors::IncorrectArgumentCount(expected, got))
}
_ => panic!("Runtime error code {} not supported", runtime_error_code),
}
}
20 changes: 12 additions & 8 deletions clar2wasm/src/words/bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,6 @@ impl ComplexWord for Let {
_expr: &SymbolicExpression,
args: &[SymbolicExpression],
) -> Result<(), GeneratorError> {
if args.len() < 2 {
return Err(GeneratorError::ArgumentLengthError(format!(
"let expected at least 2 arguments, got {}",
args.len()
)));
};

let bindings = args.get_list(0)?;

// Save the current named locals
Expand Down Expand Up @@ -87,7 +80,7 @@ impl ComplexWord for Let {
mod tests {
use clarity::vm::Value;

use crate::tools::{crosscheck, crosscheck_compare_only, crosscheck_expect_failure};
use crate::tools::{crosscheck, crosscheck_compare_only, crosscheck_expect_failure, evaluate};

#[cfg(feature = "test-clarity-v1")]
mod clarity_v1 {
Expand All @@ -112,6 +105,17 @@ mod tests {
crosscheck_expect_failure("(let ((current-count (count u1))))");
}

#[test]
fn let_less_than_two_args_evaluate() {
let result = evaluate("(let ((current-count (count u1))))");
println!("{:#?}", result);
csgui marked this conversation as resolved.
Show resolved Hide resolved
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("expecting >= 2 arguments, got 1"));
}

#[test]
fn clar_let_disallow_builtin_names() {
// It's not allowed to use names of user-defined functions as bindings
Expand Down
94 changes: 69 additions & 25 deletions clar2wasm/src/words/blockinfo.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,26 @@
use clarity::vm::{ClarityName, SymbolicExpression};
use walrus::{GlobalId, Module};

use super::ComplexWord;
use crate::error_mapping::ErrorMap;
use crate::wasm_generator::{ArgumentsExt, GeneratorError, WasmGenerator};

fn get_global(module: &Module, name: &str) -> Result<GlobalId, GeneratorError> {
module
.globals
.iter()
.find(|global| {
global
.name
.as_ref()
.map_or(false, |other_name| name == other_name)
})
.map(|global| global.id())
.ok_or_else(|| {
GeneratorError::InternalError(format!("Expected to find a global named ${name}"))
})
}
csgui marked this conversation as resolved.
Show resolved Hide resolved

#[derive(Debug)]
pub struct GetBlockInfo;

Expand All @@ -19,10 +37,17 @@ impl ComplexWord for GetBlockInfo {
args: &[SymbolicExpression],
) -> Result<(), GeneratorError> {
if args.len() != 2 {
return Err(GeneratorError::ArgumentLengthError(format!(
"get-block-info? expected 2 arguments, got {}",
args.len()
)));
let (arg_name_offset, arg_name_len) =
generator.add_string_literal(&format!("expected: {} got: {}", 2, args.len()))?;
builder
.i32_const(arg_name_offset as i32)
.global_set(get_global(&generator.module, "runtime-error-arg-offset")?)
.i32_const(arg_name_len as i32)
.global_set(get_global(&generator.module, "runtime-error-arg-len")?)
.i32_const(ErrorMap::ArgumentCountMismatch as i32)
.call(generator.func_by_name("stdlib.runtime-error"))
// To avoid having to generate correct return values
.unreachable();
BowTiedWoo marked this conversation as resolved.
Show resolved Hide resolved
BowTiedWoo marked this conversation as resolved.
Show resolved Hide resolved
};

let prop_name = args.get_name(0)?;
Expand Down Expand Up @@ -77,13 +102,6 @@ impl ComplexWord for GetBurnBlockInfo {
expr: &SymbolicExpression,
args: &[SymbolicExpression],
) -> Result<(), GeneratorError> {
if args.len() != 2 {
return Err(GeneratorError::ArgumentLengthError(format!(
"get-burn-block-info? expected 2 arguments, got {}",
args.len()
)));
};

let prop_name = args.get_name(0)?;
let block = args.get_expr(1)?;

Expand Down Expand Up @@ -138,13 +156,6 @@ impl ComplexWord for AtBlock {
_expr: &SymbolicExpression,
args: &[SymbolicExpression],
) -> Result<(), GeneratorError> {
if args.len() != 2 {
return Err(GeneratorError::ArgumentLengthError(format!(
"at-block expected 2 arguments, got {}",
args.len()
)));
};

let block_hash = args.get_expr(0)?;
let e = args.get_expr(1)?;

Expand All @@ -170,7 +181,7 @@ mod tests {
use clarity::vm::types::{OptionalData, PrincipalData, TupleData};
use clarity::vm::Value;

use crate::tools::{crosscheck_expect_failure, evaluate, TestEnvironment};
use crate::tools::{evaluate, TestEnvironment};

//
// Module with tests that should only be executed
Expand Down Expand Up @@ -250,12 +261,24 @@ mod tests {

#[test]
fn get_block_info_less_than_two_args() {
crosscheck_expect_failure("(get-block-info? id-header-hash)");
let mut env = TestEnvironment::default();
env.advance_chain_tip(1);
let result = env.evaluate("(get-block-info? id-header-hash)");
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("expecting >= 2 arguments, got 1"));
}

#[test]
fn get_block_info_more_than_two_args() {
crosscheck_expect_failure("(get-block-info? id-header-hash u0 u0)");
let mut env = TestEnvironment::default();
env.advance_chain_tip(1);
let snippet = "(get-block-info? burnchain-header-hash u0 u0)";
let expected = Err(Error::Unchecked(CheckErrors::IncorrectArgumentCount(2, 3)));
let result = env.evaluate(snippet);
assert_eq!(result, expected);
}

#[test]
Expand Down Expand Up @@ -376,12 +399,23 @@ mod tests {

#[test]
fn get_burn_block_info_less_than_two_args() {
crosscheck_expect_failure("(get-burn-block-info? id-header-hash)");
let result = evaluate("(get-burn-block-info? id-header-hash)");
println!("{:#?}", result);
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("expecting 2 arguments, got 1"));
}

#[test]
fn get_burn_block_info_more_than_two_args() {
crosscheck_expect_failure("(get-burn-block-info? id-header-hash u0 u0)");
let result = evaluate("(get-burn-block-info? id-header-hash u0 u0)");
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("expecting 2 arguments, got 3"));
}

#[test]
Expand Down Expand Up @@ -421,16 +455,26 @@ mod tests {

#[test]
fn at_block_less_than_two_args() {
crosscheck_expect_failure(
let result = evaluate(
"(at-block 0xb5e076ab7609c7f8c763b5c571d07aea80b06b41452231b1437370f4964ed66e)",
);
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("expecting 2 arguments, got 1"));
}

#[test]
fn at_block_more_than_two_args() {
crosscheck_expect_failure(
let result = evaluate(
"(at-block 0xb5e076ab7609c7f8c763b5c571d07aea80b06b41452231b1437370f4964ed66e u0 u0)",
);
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("expecting 2 arguments, got 3"));
}

#[test]
Expand Down
Loading