Browse Source

Add simple test

Signed-off-by: Sean Young <sean@mess.org>
Sean Young 6 years ago
parent
commit
1594f2c311
5 changed files with 240 additions and 9 deletions
  1. 3 1
      Cargo.toml
  2. 26 8
      src/emit.rs
  3. 164 0
      src/link.rs
  4. 4 0
      src/main.rs
  5. 43 0
      src/test.rs

+ 3 - 1
Cargo.toml

@@ -13,4 +13,6 @@ lalrpop-util = "0.16.1"
 regex = "0.2.1"
 llvm-sys = "70.0.1"
 num-bigint = "0.2"
-num-traits = "0.2.6"
+num-traits = "0.2.6"
+parity-wasm = "0.35"
+wasmi = "0.4.3"

+ 26 - 8
src/emit.rs

@@ -6,6 +6,9 @@ use std::str;
 use vartable::*;
 use std::cell::Cell;
 use std::slice;
+use link;
+use std::io::prelude::*;
+use std::fs::File;
 
 use llvm_sys::LLVMIntPredicate;
 use llvm_sys::core::*;
@@ -54,22 +57,33 @@ impl Contract {
 
     pub fn wasm_file(&self, emitter: &Emitter, filename: String) -> Result<(), String> {
         let mut obj_error = null_mut();
+        let mut memory_buffer = null_mut();
 
         unsafe {
-            let result = LLVMTargetMachineEmitToFile(emitter.tm,
+            let result = LLVMTargetMachineEmitToMemoryBuffer(emitter.tm,
                                                     self.module,
-                                                    filename.as_ptr() as *mut i8,
                                                     LLVMCodeGenFileType::LLVMObjectFile,
-                                                    &mut obj_error);
+                                                    &mut obj_error,
+                                                    &mut memory_buffer);
 
             if result != 0 {
                 Err(CStr::from_ptr(obj_error as *const _).to_string_lossy().to_string())
             } else {
+                let obj = slice::from_raw_parts(
+                    LLVMGetBufferStart(memory_buffer) as *const u8,
+                    LLVMGetBufferSize(memory_buffer) as usize
+                );
+                let res = link::link(&obj);
+                LLVMDisposeMemoryBuffer(memory_buffer);
+                
+                let mut file = File::create(filename).unwrap();
+                file.write_all(&res).unwrap();
                 Ok(())
             }
         }
     }
 
+    #[cfg(test)]
     pub fn wasm(&self, emitter: &Emitter) -> Result<Vec<u8>, String> {
         let mut obj_error = null_mut();
         let mut memory_buffer = null_mut();
@@ -84,9 +98,13 @@ impl Contract {
             if result != 0 {
                 Err(CStr::from_ptr(obj_error as *const _).to_string_lossy().to_string())
             } else {
-                let v = slice::from_raw_parts(LLVMGetBufferStart(memory_buffer) as *const u8, LLVMGetBufferSize(memory_buffer) as usize).to_vec();
+                let obj = slice::from_raw_parts(
+                    LLVMGetBufferStart(memory_buffer) as *const u8,
+                    LLVMGetBufferSize(memory_buffer) as usize
+                );
+                let res = link::link(&obj);
                 LLVMDisposeMemoryBuffer(memory_buffer);
-                Ok(v)
+                Ok(res)
             }
         }
     }
@@ -136,14 +154,14 @@ impl Emitter {
                     }
 
                     e.contracts.push(Contract{
-                        name: contract.name.to_string(), 
+                        name: contract.name.to_string(),
                         module: module,
                     });
 
                     LLVMDisposeBuilder(builder);
                 }
             }
-        
+
         }
 
         e
@@ -442,7 +460,7 @@ impl<'a> FunctionEmitter<'a> {
             },
             Statement::While(cond, body) => {
                 let mut changeset = HashMap::new();
- 
+
                 cond.written_vars(&mut changeset);
                 body.written_vars(&mut changeset);
 

+ 164 - 0
src/link.rs

@@ -0,0 +1,164 @@
+
+use parity_wasm;
+use parity_wasm::elements::{Internal, Module, ExportEntry};
+use parity_wasm::builder;
+
+use parity_wasm::elements::{VarUint7, VarUint32, Deserialize};
+use parity_wasm::elements;
+
+pub fn link(input: &[u8]) -> Vec<u8> {
+    let mut module : Module = parity_wasm::deserialize_buffer(input).expect("cannot deserialize llvm wasm");
+
+    let mut exports = Vec::new();
+
+    for c in module.custom_sections() {
+        if c.name() != "linking" {
+            continue;
+        }
+
+        let mut payload = c.payload();
+
+        for sym in read_linking_section(&mut payload).expect("cannot read linking section") {
+            match sym {
+                Symbol::Function(SymbolFunction { flags: _, index, name}) => {
+                    exports.push(ExportEntry::new(name, Internal::Function(index)));
+                },
+                _ => {}
+            }
+        }
+    }
+
+    module.import_section_mut().unwrap().entries_mut().truncate(0);
+    module.clear_custom_section("linking");
+
+    let mut linked = builder::module().with_module(module);
+    
+    for e in exports {
+        linked.push_export(e);
+    }
+
+    parity_wasm::serialize(linked.build()).expect("cannot serialize linked wasm")
+}
+
+pub struct SymbolFunction {
+    pub flags: u32,
+    pub index: u32,
+    pub name: String
+}
+
+pub struct SymbolGlobal {
+    pub flags: u32,
+    pub index: u32,
+    pub name: String
+}
+
+pub struct SymbolEvent {
+    pub flags: u32,
+    pub index: u32,
+    pub name: String
+}
+
+pub struct SymbolData {
+    pub flags: u32,
+    pub name: String,
+    pub index: u32,
+    pub offset: u32,
+    pub size: u32,
+}
+
+pub struct SymbolSection {
+    pub flags: u32,
+    pub section: u32
+}
+
+pub enum Symbol {
+    Function(SymbolFunction),
+    Global(SymbolGlobal),
+    Event(SymbolEvent),
+    Data(SymbolData),
+    Section(SymbolSection),
+}
+
+fn read_linking_section<R: std::io::Read>(input: &mut R) ->  Result<Vec<Symbol>, elements::Error> {
+	let meta_data_version = u32::from(VarUint32::deserialize(input)?);
+
+    if meta_data_version != 1 {
+        return Err(elements::Error::Other("unsupported meta data version"));
+    }
+
+    let mut symbol_table = Vec::new();
+
+	let subsection_id = u8::from(VarUint7::deserialize(input)?);
+
+    if subsection_id != 8 {
+        return Err(elements::Error::Other("symbol table id is wrong"));
+    }
+
+    let _length = u32::from(VarUint32::deserialize(input)?);
+    let count = u32::from(VarUint32::deserialize(input)?);
+
+    for _ in 0..count {
+        let kind = u8::from(VarUint7::deserialize(input)?);
+    	let flags = u32::from(VarUint32::deserialize(input)?);
+
+        symbol_table.push(match kind {
+            0 => {
+                let index = u32::from(VarUint32::deserialize(input)?);
+                let name = String::deserialize(input)?;
+
+                Symbol::Function(SymbolFunction{
+                    flags,
+                    index,
+                    name,
+                })
+            },
+            1 => {
+                let name = String::deserialize(input)?;
+                let index = u32::from(VarUint32::deserialize(input)?);
+                let offset = u32::from(VarUint32::deserialize(input)?);
+                let size = u32::from(VarUint32::deserialize(input)?);
+
+                Symbol::Data(SymbolData{
+                    flags,
+                    name,
+                    index,
+                    offset,
+                    size,
+                })
+            },
+            2 => {
+                let index = u32::from(VarUint32::deserialize(input)?);
+                let name = String::deserialize(input)?;
+
+                Symbol::Global(SymbolGlobal{
+                    flags,
+                    index,
+                    name,
+                })
+            },
+            3 => {
+                let section = u32::from(VarUint32::deserialize(input)?);
+                
+                Symbol::Section(SymbolSection{
+                    flags,
+                    section,
+                })
+            },
+            4 => {
+                let index = u32::from(VarUint32::deserialize(input)?);
+                let name = String::deserialize(input)?;
+
+                Symbol::Event(SymbolEvent{
+                    flags,
+                    index,
+                    name,
+                })
+            },
+            _ => {
+                return Err(elements::Error::Other("invalid symbol table kind"));
+            }
+        });
+    }
+
+    Ok(symbol_table)
+}

+ 4 - 0
src/main.rs

@@ -5,12 +5,16 @@ extern crate num_bigint;
 extern crate lalrpop_util;
 extern crate llvm_sys;
 extern crate num_traits;
+extern crate parity_wasm;
+extern crate wasmi;
 
 mod ast;
 mod solidity;
 mod resolve;
 mod emit;
+mod link;
 mod vartable;
+mod test;
 
 use std::env;
 use std::fs::File;

+ 43 - 0
src/test.rs

@@ -0,0 +1,43 @@
+
+#[cfg(test)]
+mod tests {
+    use solidity;
+    use resolve;
+    use emit::Emitter;
+    use wasmi::{ImportsBuilder, Module, ModuleInstance, NopExternals, RuntimeValue};
+
+    #[test]
+    fn simple_solidiy_compile_and_run() {
+        // parse
+        let mut s = solidity::SourceUnitParser::new()
+            .parse("
+            contract test {
+                function foo() returns (uint32) {
+                    return 2;
+                }
+            }").expect("parse should succeed");
+        
+        // resolve
+        resolve::resolve(&mut s).expect("resolve should succeed");
+
+        // codegen
+        Emitter::init();
+
+        let res = Emitter::new(s);
+
+        assert_eq!(res.contracts.len(), 1);
+
+        let bc = res.contracts[0].wasm(&res).expect("llvm wasm emit should work");
+
+        let module = Module::from_buffer(bc).expect("parse wasm should work");
+
+        let main = ModuleInstance::new(&module, &ImportsBuilder::default())
+            .expect("Failed to instantiate module")
+            .run_start(&mut NopExternals)
+            .expect("Failed to run start function in module");
+
+        let ret = main.invoke_export("foo", &[], &mut NopExternals).expect("failed to call function");
+
+        assert_eq!(ret, Some(RuntimeValue::I32(2)));
+    }
+}