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

Adding Move On Sui Semgrep grammer #508

Merged
merged 11 commits into from
Sep 17, 2024
Merged
2 changes: 1 addition & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -123,4 +123,4 @@
url = https://github.com/Decurity/tree-sitter-circom.git
[submodule "lang/semgrep-grammars/src/tree-sitter-move-on-sui"]
path = lang/semgrep-grammars/src/tree-sitter-move-on-sui
url = https://github.com/tzakian/tree-sitter-move.git
url = https://github.com/maxmysten/tree-sitter-move.git
3 changes: 2 additions & 1 deletion lang/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,8 @@ STAT_LANGUAGES2 = \
rust \
solidity \
tsx \
typescript
typescript \
move-on-sui

STAT_LANGUAGES = $(STAT_LANGUAGES1) $(STAT_LANGUAGES2)

Expand Down
1 change: 1 addition & 0 deletions lang/move-on-sui/projects.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
https://github.com/AftermathFinance/move-interfaces
https://github.com/Bucket-Protocol/bucket-interface
https://github.com/MystenLabs/sui-axelar
https://github.com/MystenLabs/prettier-move/
79 changes: 79 additions & 0 deletions lang/move-on-sui/test/ok/address.move
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

module sui::address {
use sui::hex;
use std::ascii;
use std::bcs;
use std::string;

/// The length of an address, in bytes
const LENGTH: u64 = 32;

// The largest integer that can be represented with 32 bytes: 2^(8*32) - 1
const MAX: u256 = 115792089237316195423570985008687907853269984665640564039457584007913129639935;

#[allow(unused_const)]
/// Error from `from_bytes` when it is supplied too many or too few bytes.
const EAddressParseError: u64 = 0;

/// Convert `a` into a u256 by interpreting `a` as the bytes of a big-endian integer
/// (e.g., `to_u256(0x1) == 1`)
public native fun to_u256(a: address): u256;

spec to_u256 {
pragma opaque;
// TODO: stub to be replaced by actual abort conditions if any
aborts_if [abstract] true;
// TODO: specify actual function behavior
}

/// Convert `n` into an address by encoding it as a big-endian integer (e.g., `from_u256(1) = @0x1`)
/// Aborts if `n` > `MAX_ADDRESS`
public native fun from_u256(n: u256): address;

spec from_u256 {
pragma opaque;
// TODO: stub to be replaced by actual abort conditions if any
aborts_if [abstract] true;
// TODO: specify actual function behavior
}

/// Convert `bytes` into an address.
/// Aborts with `EAddressParseError` if the length of `bytes` is not 32
public native fun from_bytes(bytes: vector<u8>): address;

spec from_bytes {
pragma opaque;
// TODO: stub to be replaced by actual abort conditions if any
aborts_if [abstract] true;
// TODO: specify actual function behavior
}

/// Convert `a` into BCS-encoded bytes.
public fun to_bytes(a: address): vector<u8> {
bcs::to_bytes(&a)
}

/// Convert `a` to a hex-encoded ASCII string
public fun to_ascii_string(a: address): ascii::String {
ascii::string(hex::encode(to_bytes(a)))
}

/// Convert `a` to a hex-encoded ASCII string
public fun to_string(a: address): string::String {
string::from_ascii(to_ascii_string(a))
}

/// Length of a Sui address in bytes
public fun length(): u64 {
LENGTH
}

/// Largest possible address
public fun max(): u256 {
MAX
}

spec module { pragma verify = false; }
}
9 changes: 9 additions & 0 deletions lang/move-on-sui/test/ok/annotations.move
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#[allow(unused_const, unused_variable)]

#[allow(unused_const, unused_variable)]
#[test, expected_failure(abort_code = ::staking::staking_tests::ENotImplemented)]
#[test, expected_failure(abort_code = other_module::ENotFound)]
#[expected_failure(arithmetic_error, location = pkg_addr::other_module)]

// #[allow(unused_const, unused_variable)]
module c::k {}
71 changes: 71 additions & 0 deletions lang/move-on-sui/test/ok/as_expressions.move
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
module foo::bar {
fun f(): u64 {
1 as u64;
1 + 2 as u64;
(1 + 2) as u64;
1 + (2 as u64);
v[1 as u64];
1 as u64 + 2;
(1 as u64) + 2;
}

fun simple(cond: bool, x: u32) {
(if (cond) x else { x }) as u32;
(if (cond) x else { x } as u32);
(loop { break 0 }) as u32;
(loop { break 0 } as u32);
('l: { 0 }) as u32;
('l: { 0 } as u32);
(return) as u32;
(return as u32);
loop {
(break) as u32;
(break as u32);
(continue) as u32;
(continue as u32);
};
(0 as u32) as u32;
(0 as u32 as u32);
}

public fun f_imm(s: &S): &u64 { &s.f }

fun ops(x: u8, y: u8) {
(x + y as u32);
(x - y as u32);
(x * y as u32);
(x / y as u32);
(x % y as u32);
(x & y as u32);
(x | y as u32);
(x ^ y as u32);
(x << y as u32);
(x >> y as u32);

(x + y) as u32;
(x - y) as u32;
(x * y) as u32;
(x / y) as u32;
(x % y) as u32;
(x & y) as u32;
(x | y) as u32;
(x ^ y) as u32;
(x << y) as u32;
(x >> y) as u32;
}


public struct S has copy, drop { f: u64 }

fun dotted(cond: bool, mut s: S) {
(1 + s.f) as u32;
(1 + s.f as u32);
(1 + S { f: 0 }.f) as u32;
(1 + S { f: 0 }.f as u32);
(*if (cond) { &0 } else { &mut 0 }) as u32;
// this case still does not work
// (*if (cond) { &0 } else { &mut 0 } as u32);
(*if (cond) { &s } else {&mut s}.f_imm()) as u32;
(*if (cond) { &s } else {&mut s}.f_imm() as u32);
}
}
151 changes: 151 additions & 0 deletions lang/move-on-sui/test/ok/ascii.move
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

/// The `ASCII` module defines basic string and char newtypes in Move that verify
/// that characters are valid ASCII, and that strings consist of only valid ASCII characters.
module std::ascii {
use std::vector;
use std::option::{Self, Option};

/// An invalid ASCII character was encountered when creating an ASCII string.
const EINVALID_ASCII_CHARACTER: u64 = 0x10000;

/// The `String` struct holds a vector of bytes that all represent
/// valid ASCII characters. Note that these ASCII characters may not all
/// be printable. To determine if a `String` contains only "printable"
/// characters you should use the `all_characters_printable` predicate
/// defined in this module.
struct String has copy, drop, store {
bytes: vector<u8>,
}
spec String {
invariant forall i in 0..len(bytes): is_valid_char(bytes[i]);
}

/// An ASCII character.
struct Char has copy, drop, store {
byte: u8,
}
spec Char {
invariant is_valid_char(byte);
}

/// Convert a `byte` into a `Char` that is checked to make sure it is valid ASCII.
public fun char(byte: u8): Char {
assert!(is_valid_char(byte), EINVALID_ASCII_CHARACTER);
Char { byte }
}
spec char {
aborts_if !is_valid_char(byte) with EINVALID_ASCII_CHARACTER;
}

/// Convert a vector of bytes `bytes` into an `String`. Aborts if
/// `bytes` contains non-ASCII characters.
public fun string(bytes: vector<u8>): String {
let x = try_string(bytes);
assert!(
option::is_some(&x),
EINVALID_ASCII_CHARACTER
);
option::destroy_some(x)
}
spec string {
aborts_if exists i in 0..len(bytes): !is_valid_char(bytes[i]) with EINVALID_ASCII_CHARACTER;
}

/// Convert a vector of bytes `bytes` into an `String`. Returns
/// `Some(<ascii_string>)` if the `bytes` contains all valid ASCII
/// characters. Otherwise returns `None`.
public fun try_string(bytes: vector<u8>): Option<String> {
let len = vector::length(&bytes);
let i = 0;
while ({
spec {
invariant i <= len;
invariant forall j in 0..i: is_valid_char(bytes[j]);
};
i < len
}) {
let possible_byte = *vector::borrow(&bytes, i);
if (!is_valid_char(possible_byte)) return option::none();
i = i + 1;
};
spec {
assert i == len;
assert forall j in 0..len: is_valid_char(bytes[j]);
};
option::some(String { bytes })
}

/// Returns `true` if all characters in `string` are printable characters
/// Returns `false` otherwise. Not all `String`s are printable strings.
public fun all_characters_printable(string: &String): bool {
let len = vector::length(&string.bytes);
let i = 0;
while ({
spec {
invariant i <= len;
invariant forall j in 0..i: is_printable_char(string.bytes[j]);
};
i < len
}) {
let byte = *vector::borrow(&string.bytes, i);
if (!is_printable_char(byte)) return false;
i = i + 1;
};
spec {
assert i == len;
assert forall j in 0..len: is_printable_char(string.bytes[j]);
};
true
}
spec all_characters_printable {
ensures result ==> (forall j in 0..len(string.bytes): is_printable_char(string.bytes[j]));
}

public fun push_char(string: &mut String, char: Char) {
vector::push_back(&mut string.bytes, char.byte);
}
spec push_char {
ensures len(string.bytes) == len(old(string.bytes)) + 1;
}

public fun pop_char(string: &mut String): Char {
Char { byte: vector::pop_back(&mut string.bytes) }
}
spec pop_char {
ensures len(string.bytes) == len(old(string.bytes)) - 1;
}

public fun length(string: &String): u64 {
vector::length(as_bytes(string))
}

/// Get the inner bytes of the `string` as a reference
public fun as_bytes(string: &String): &vector<u8> {
&string.bytes
}

/// Unpack the `string` to get its backing bytes
public fun into_bytes(string: String): vector<u8> {
let String { bytes } = string;
bytes
}

/// Unpack the `char` into its underlying byte.
public fun byte(char: Char): u8 {
let Char { byte } = char;
byte
}

/// Returns `true` if `b` is a valid ASCII character. Returns `false` otherwise.
public fun is_valid_char(b: u8): bool {
b <= 0x7F
}

/// Returns `true` if `byte` is an printable ASCII character. Returns `false` otherwise.
public fun is_printable_char(byte: u8): bool {
byte >= 0x20 && // Disallow metacharacters
byte <= 0x7E // Don't allow DEL metacharacter
}
}
Loading
Loading