浏览代码

String concatenation using +

	string foo = "xyz";
	string bar = foo + "abc";

Signed-off-by: Sean Young <sean@mess.org>
Sean Young 5 年之前
父节点
当前提交
727c4736c8
共有 6 个文件被更改,包括 171 次插入28 次删除
  1. 14 0
      src/emit/mod.rs
  2. 5 0
      src/resolver/cfg.rs
  3. 98 28
      src/resolver/expression.rs
  4. 二进制
      stdlib/stdlib.bc
  5. 22 0
      stdlib/stdlib.c
  6. 32 0
      tests/substrate_strings/mod.rs

+ 14 - 0
src/emit/mod.rs

@@ -1155,6 +1155,20 @@ impl<'a> Contract<'a> {
                     .left()
                     .unwrap()
             }
+            Expression::StringConcat(_, l, r) => {
+                let (left, left_len) = self.string_location(l, vartab, function, runtime);
+                let (right, right_len) = self.string_location(r, vartab, function, runtime);
+
+                self.builder
+                    .build_call(
+                        self.module.get_function("concat").unwrap(),
+                        &[left.into(), left_len.into(), right.into(), right_len.into()],
+                        "",
+                    )
+                    .try_as_basic_value()
+                    .left()
+                    .unwrap()
+            }
             Expression::Poison => unreachable!(),
         }
     }

+ 5 - 0
src/resolver/cfg.rs

@@ -341,6 +341,11 @@ impl ControlFlowGraph {
                 self.location_to_string(ns, l),
                 self.location_to_string(ns, r)
             ),
+            Expression::StringConcat(_, l, r) => format!(
+                "(concat ({}) ({}))",
+                self.location_to_string(ns, l),
+                self.location_to_string(ns, r)
+            ),
             Expression::Keccak256(_, e) => format!("(keccak256 {})", self.expr_to_string(ns, e)),
         }
     }

+ 98 - 28
src/resolver/expression.rs

@@ -74,6 +74,7 @@ pub enum Expression {
     DynamicArrayLength(Loc, Box<Expression>),
     DynamicArraySubscript(Loc, Box<Expression>, resolver::Type, Box<Expression>),
     StringCompare(Loc, StringLocation, StringLocation),
+    StringConcat(Loc, StringLocation, StringLocation),
 
     Or(Loc, Box<Expression>, Box<Expression>),
     And(Loc, Box<Expression>, Box<Expression>),
@@ -139,6 +140,7 @@ impl Expression {
             | Expression::DynamicArrayLength(loc, _)
             | Expression::DynamicArraySubscript(loc, _, _, _)
             | Expression::StringCompare(loc, _, _)
+            | Expression::StringConcat(loc, _, _)
             | Expression::Keccak256(loc, _)
             | Expression::And(loc, _, _) => *loc,
             Expression::Poison => unreachable!(),
@@ -239,7 +241,7 @@ impl Expression {
             Expression::Keccak256(_, e) => e.reads_contract_storage(),
             Expression::And(_, l, r) => l.reads_contract_storage() || r.reads_contract_storage(),
             Expression::Or(_, l, r) => l.reads_contract_storage() || r.reads_contract_storage(),
-            Expression::StringCompare(_, l, r) => {
+            Expression::StringConcat(_, l, r) | Expression::StringCompare(_, l, r) => {
                 if let StringLocation::RunTime(e) = l {
                     if !e.reads_contract_storage() {
                         return false;
@@ -1006,29 +1008,7 @@ pub fn expression(
                 Err(())
             }
         }
-        ast::Expression::Add(loc, l, r) => {
-            let (left, left_type) = expression(l, cfg, ns, vartab, errors)?;
-            let (right, right_type) = expression(r, cfg, ns, vartab, errors)?;
-
-            let ty = coerce_int(
-                &left_type,
-                &l.loc(),
-                &right_type,
-                &r.loc(),
-                false,
-                ns,
-                errors,
-            )?;
-
-            Ok((
-                Expression::Add(
-                    *loc,
-                    Box::new(cast(&l.loc(), left, &left_type, &ty, true, ns, errors)?),
-                    Box::new(cast(&r.loc(), right, &right_type, &ty, true, ns, errors)?),
-                ),
-                ty,
-            ))
-        }
+        ast::Expression::Add(loc, l, r) => addition(loc, l, r, cfg, ns, vartab, errors),
         ast::Expression::Subtract(loc, l, r) => {
             let (left, left_type) = expression(l, cfg, ns, vartab, errors)?;
             let (right, right_type) = expression(r, cfg, ns, vartab, errors)?;
@@ -2567,7 +2547,7 @@ fn new(
     ))
 }
 
-/// Resolve an array subscript expression
+/// Test for equality; first check string equality, then integer equality
 fn equal(
     loc: &ast::Loc,
     l: &ast::Expression,
@@ -2613,9 +2593,7 @@ fn equal(
     // compare string
     match (&left_type, &right_type) {
         (resolver::Type::String, resolver::Type::String)
-        | (resolver::Type::DynamicBytes, resolver::Type::DynamicBytes)
-        | (resolver::Type::String, resolver::Type::DynamicBytes)
-        | (resolver::Type::DynamicBytes, resolver::Type::String) => {
+        | (resolver::Type::DynamicBytes, resolver::Type::DynamicBytes) => {
             return Ok(Expression::StringCompare(
                 *loc,
                 StringLocation::RunTime(Box::new(left)),
@@ -2634,6 +2612,98 @@ fn equal(
     ))
 }
 
+/// Try string concatenation
+fn addition(
+    loc: &ast::Loc,
+    l: &ast::Expression,
+    r: &ast::Expression,
+    cfg: &mut ControlFlowGraph,
+    ns: &resolver::Contract,
+    vartab: &mut Option<&mut Vartable>,
+    errors: &mut Vec<output::Output>,
+) -> Result<(Expression, resolver::Type), ()> {
+    let (left, left_type) = expression(l, cfg, ns, vartab, errors)?;
+    let (right, right_type) = expression(r, cfg, ns, vartab, errors)?;
+
+    // Concatenate stringliteral with stringliteral
+    if let (Expression::BytesLiteral(_, l), Expression::BytesLiteral(_, r)) = (&left, &right) {
+        let mut c = Vec::with_capacity(l.len() + r.len());
+        c.extend_from_slice(l);
+        c.extend_from_slice(r);
+        let length = c.len();
+        return Ok((
+            Expression::BytesLiteral(*loc, c),
+            resolver::Type::Bytes(length as u8),
+        ));
+    }
+
+    // compare string against literal
+    match (&left, &right_type) {
+        (Expression::BytesLiteral(_, l), resolver::Type::String)
+        | (Expression::BytesLiteral(_, l), resolver::Type::DynamicBytes) => {
+            return Ok((
+                Expression::StringConcat(
+                    *loc,
+                    StringLocation::CompileTime(l.clone()),
+                    StringLocation::RunTime(Box::new(right)),
+                ),
+                right_type,
+            ));
+        }
+        _ => {}
+    }
+
+    match (&right, &left_type) {
+        (Expression::BytesLiteral(_, l), resolver::Type::String)
+        | (Expression::BytesLiteral(_, l), resolver::Type::DynamicBytes) => {
+            return Ok((
+                Expression::StringConcat(
+                    *loc,
+                    StringLocation::RunTime(Box::new(left)),
+                    StringLocation::CompileTime(l.clone()),
+                ),
+                left_type,
+            ));
+        }
+        _ => {}
+    }
+
+    // compare string
+    match (&left_type, &right_type) {
+        (resolver::Type::String, resolver::Type::String)
+        | (resolver::Type::DynamicBytes, resolver::Type::DynamicBytes) => {
+            return Ok((
+                Expression::StringConcat(
+                    *loc,
+                    StringLocation::RunTime(Box::new(left)),
+                    StringLocation::RunTime(Box::new(right)),
+                ),
+                right_type,
+            ));
+        }
+        _ => {}
+    }
+
+    let ty = coerce_int(
+        &left_type,
+        &l.loc(),
+        &right_type,
+        &r.loc(),
+        false,
+        ns,
+        errors,
+    )?;
+
+    Ok((
+        Expression::Add(
+            *loc,
+            Box::new(cast(&l.loc(), left, &left_type, &ty, true, ns, errors)?),
+            Box::new(cast(&r.loc(), right, &right_type, &ty, true, ns, errors)?),
+        ),
+        ty,
+    ))
+}
+
 /// Resolve an array subscript expression
 fn array_subscript(
     loc: &ast::Loc,

二进制
stdlib/stdlib.bc


+ 22 - 0
stdlib/stdlib.c

@@ -434,4 +434,26 @@ __attribute__((visibility("hidden"))) bool memcmp(uint8_t *left, uint32_t left_l
 	}
 
 	return true;
+}
+
+__attribute__((visibility("hidden"))) struct vector *concat(uint8_t *left, uint32_t left_len, uint8_t *right, uint32_t right_len)
+{
+	size_t size_array = left_len + right_len;
+	struct vector *v = __malloc(sizeof(*v) + size_array);
+	v->len = size_array;
+	v->size = size_array;
+
+	uint8_t *data = v->data;
+
+	while (left_len--)
+	{
+		*data++ = *left++;
+	}
+
+	while (right_len--)
+	{
+		*data++ = *right++;
+	}
+
+	return v;
 }

+ 32 - 0
tests/substrate_strings/mod.rs

@@ -206,3 +206,35 @@ fn string_compare() {
 
     runtime.function(&mut store, "test", Vec::new());
 }
+
+#[test]
+fn string_concat() {
+    // concat literal and literal. This should be compile-time thing
+    let (runtime, mut store) = build_solidity(
+        r##"
+        contract foo {
+            function test() public {
+                assert(hex"41424344" == "AB" + "CD");
+            }
+        }"##,
+    );
+
+    runtime.function(&mut store, "test", Vec::new());
+
+    let (runtime, mut store) = build_solidity(
+        r##"
+        contract foo {
+            function test() public {
+                string s1 = "x";
+                string s2 = "asdfasdf";
+
+                assert(s1 + " foo" == "x foo");
+                assert("bar " + s1 == "bar x");
+
+                assert(s1 + s2 == "xasdfasdf");
+            }
+        }"##,
+    );
+
+    runtime.function(&mut store, "test", Vec::new());
+}