diff --git a/products/bluebell/Cargo.toml b/products/bluebell/Cargo.toml index 5bb6d82c5..90ea13387 100644 --- a/products/bluebell/Cargo.toml +++ b/products/bluebell/Cargo.toml @@ -1,5 +1,5 @@ [workspace] - +resolver = "2" members = [ "core", "evm_assembly", diff --git a/products/bluebell/README.md b/products/bluebell/README.md new file mode 100644 index 000000000..7fe9bc32d --- /dev/null +++ b/products/bluebell/README.md @@ -0,0 +1,36 @@ +# Bluebell Scilla Compiler + +This README provides examples of how to use the Bluebell command line tool. + +## Running a Scilla File + +To run a Scilla file, use the `Run` command followed by the `--entry_point` flag to specify the function to invoke, the `--args` flag to pass arguments to the function, and the `--backend` flag to specify the backend to use. Here is an example: + +```bash +cargo run --bin cli -- examples/hello-world.scilla --runtime-enable debug run --backend evm --entry-point "HelloWorld::setHello" --args "[\"Zilliqa ❤️ Rocks\"]" +``` + +This command will run the `main` function of the `hello-world.scilla` file with the argument `[\"Zilliqa ❤️ Rocks\"]` using the EVM backend. This should produce an output similar to + +```bash +Zilliqa ❤️ Rocks + + +Exit reason: Succeed( + Returned, +)Result: []% +``` + +This is a basic example of compiling a Scilla contract into EVM bytecode and running the code in a EVM instance. Note that this example makes use of external precompiles which provides the `print` command to print `Zilliqa ❤️ Rocks` to the terminal. + +## Running the playground + +To set up Rust to run the playground, first follow these steps: + +1. Install Rust by following the instructions on the official Rust website. +2. Add WebAssembly (Wasm) support to your Rust setup by running `rustup target add wasm32-unknown-unknown`. +3. Install Trunk by running `cargo install trunk`. +4. Navigate to your project `playground/`. +5. Run `trunk serve` to start the development server. + +A more detailed guide can be found in the playground's [README.md](playground/README.md) file. diff --git a/products/bluebell/cargo-webdev/src/main.rs b/products/bluebell/cargo-webdev/src/main.rs index 84df67b24..74d854710 100644 --- a/products/bluebell/cargo-webdev/src/main.rs +++ b/products/bluebell/cargo-webdev/src/main.rs @@ -1,10 +1,15 @@ +use std::{ + path::Path, + sync::{Arc, Mutex}, +}; + use notify::{recommended_watcher, RecursiveMode, Watcher}; -use std::path::Path; -use std::sync::{Arc, Mutex}; -use tokio::io::{self, AsyncReadExt, AsyncWriteExt}; -use tokio::process::{Child, Command}; -use tokio::sync::broadcast; -use tokio::time::{sleep, Duration}; +use tokio::{ + io::{self, AsyncReadExt, AsyncWriteExt}, + process::{Child, Command}, + sync::broadcast, + time::{sleep, Duration}, +}; async fn stream_output(mut child: Child) -> Result> { let mut stdout = child diff --git a/products/bluebell/cli/src/main.rs b/products/bluebell/cli/src/main.rs index 51178dce8..1ff521277 100644 --- a/products/bluebell/cli/src/main.rs +++ b/products/bluebell/cli/src/main.rs @@ -1,33 +1,14 @@ -// Importing necessary modules and types -use bluebell::support::modules::BluebellModule; -use bluebell::support::modules::ScillaDebugBuiltins; -use std::ffi::CStr; - +use std::{fs::File, io::Read, process}; + +use bluebell::{ + ast::nodes::NodeProgram, + parser::{lexer, lexer::Lexer, parser, ParserError}, + support::{ + evm::EvmCompiler, + modules::{ScillaDebugBuiltins, ScillaDefaultBuiltins, ScillaDefaultTypes}, + }, +}; use clap::{Parser, Subcommand, ValueEnum}; -// DEPRECATED -// use inkwell::context::Context; -// use inkwell::targets::{InitializationConfig, Target}; -use std::fs::File; -use std::io::Read; -use std::os::raw::c_char; -use std::process; - -use bluebell::ast::nodes::NodeProgram; -use bluebell::contract_executor::UnsafeContractExecutor; -use bluebell::passes::debug_printer::DebugPrinter; - -use bluebell::support::evm::EvmCompiler; -use bluebell::support::modules::{ScillaDefaultBuiltins, ScillaDefaultTypes}; - -// use bluebell::llvm_ir_generator::LlvmIrGenerator; -// use bluebell::support::llvm::{LlvmBackend, UnsafeLlvmTestExecutor}; - -use bluebell::intermediate_representation::emitter::IrEmitter; -use bluebell::intermediate_representation::pass_manager::PassManager; - -use bluebell::parser::lexer; -use bluebell::parser::lexer::Lexer; -use bluebell::parser::{parser, ParserError}; use evm_assembly::types::EvmTypeValue; use log::{Log, Metadata, Record}; @@ -71,14 +52,12 @@ fn setup_logger() { // Enum to define the output format of Bluebell #[derive(Clone, Debug, Subcommand)] enum BluebellOutputFormat { - LlvmIr, FormattedScilla, } // Enum to define the backend of Bluebell #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)] enum BluebellBackend { - Llvm, Evm, } @@ -104,7 +83,7 @@ enum BluebellCommand { entry_point: String, /// Arguments to pass to function - #[arg(short, long, default_value_t= ("".to_string()))] + #[arg(short, long, default_value_t= String::new())] args: String, }, } @@ -180,131 +159,9 @@ fn bluebell_evm_run( serde_json::from_str(&args).expect("Failed to deserialize arguments") }; - print!("Arguments: {:?}", args); executable.execute(&entry_point, arguments); } -// Function to run Bluebell with LLVM backend -fn bluebell_llvm_run(ast: &NodeProgram, entry_point: String, debug: bool) { - // DEPRECATED - panic!("LLVM support is DEPRECATED for now."); - /* - /****** Executable *****/ - /////// - let backend = LlvmBackend::new(); - // TODO: runtime is a poor name. - let mut specification = backend.create_backend_specification(); - - specification.declare_integer("Int8", 8); - specification.declare_integer("Int16", 16); - specification.declare_integer("Int32", 32); - specification.declare_integer("Int64", 64); - specification.declare_unsigned_integer("Uint8", 8); - specification.declare_unsigned_integer("Uint16", 16); - specification.declare_unsigned_integer("Uint32", 32); - specification.declare_unsigned_integer("Uint64", 64); - - let _ = specification - .declare_intrinsic("add", ["Int32", "Int32"].to_vec(), "Int32") - .attach_runtime(|| { - extern "C" fn addi32(a: i32, b: i32) -> i32 { - a + b - } - - addi32 as usize - }); - - // let _executable = backend.create_executable("test"); - // let executable = backend.compile(name, script); - - let context = Context::create(); - let mut module = context.create_module("main"); - - // Runtime struct <- contains Context - // VM / Executor - // Executable <- contains Module - // Compiler - - // Declaring runtime - let ft = context.f64_type(); - let i8_ptr_type = context.i8_type().ptr_type(inkwell::AddressSpace::default()); - let fn_type = context.void_type().fn_type(&[i8_ptr_type.into()], false); - - module.add_function("sumf", ft.fn_type(&[ft.into(), ft.into()], false), None); - module.add_function("builtin__print", fn_type, None); - - let setup_runtime = |contract_executor: &UnsafeLlvmTestExecutor| { - Target::initialize_native(&InitializationConfig::default()).unwrap(); - - ////// - // Defining runtime - - extern "C" fn sumf(a: f64, b: f64) -> f64 { - a + b - } - extern "C" fn print_string(s: *const c_char) { - let c_str = unsafe { CStr::from_ptr(s) }; - let str_slice: &str = c_str.to_str().unwrap(); - } - unsafe { - contract_executor.link_symbol("sumf", sumf as usize); - contract_executor.link_symbol("builtin__print", print_string as usize); - } - }; - - /*** Compiling ***/ - - ///// - // Frontend: AST -> Highlevel IR - let mut generator = IrEmitter::new(); - let mut ir = generator.emit(ast).expect("Failed generating highlevel IR"); - - /*** Analysis ***/ - let mut pass_manager = PassManager::default_pipeline(); - - if let Err(err) = pass_manager.run(&mut ir) { - panic!("{}", err); - } - - let mut debug_printer = DebugPrinter::new(); - let _ = ir.run_pass(&mut debug_printer); - - /////// - // Lowering/"backend": Generating LLVM IR - let mut generator = LlvmIrGenerator::new(&context, ir, &mut module); - - match generator.build_module() { - Err(e) => { - let llvm_str = module.print_to_string(); - let output = llvm_str.to_str().expect("Failed converting to UTF8"); - println!("{}", output); - - panic!("Error: {:?}", e); - } - Ok(_) => (), - }; - - if debug { - let llvm_str = module.print_to_string(); - let output = llvm_str.to_str().expect("Failed converting to UTF8"); - println!("{}", output); - } - - /****** Execution *****/ - ////// - - ////// - // Executing - - let contract_executor = UnsafeLlvmTestExecutor::new(&mut module); - setup_runtime(&contract_executor); - - unsafe { - contract_executor.execute(&entry_point); - } - */ -} - // Main function fn main() { // Setting up the logger @@ -339,8 +196,6 @@ fn main() { args: arguments, backend, } => match backend { - // Running with LLVM backend - BluebellBackend::Llvm => bluebell_llvm_run(&ast, entry_point, args.debug), // Running with EVM backend BluebellBackend::Evm => { bluebell_evm_run(&ast, entry_point, arguments, features, args.debug) diff --git a/products/bluebell/core/src/ast/converting.rs b/products/bluebell/core/src/ast/converting.rs index 14af7295c..9f8b15f73 100644 --- a/products/bluebell/core/src/ast/converting.rs +++ b/products/bluebell/core/src/ast/converting.rs @@ -1,235 +1,338 @@ -use crate::ast::nodes::*; -use crate::constants::{TraversalResult, TreeTraversalMode}; -use crate::parser::lexer::SourcePosition; +use crate::{ + ast::nodes::*, + constants::{TraversalResult, TreeTraversalMode}, + parser::lexer::SourcePosition, +}; + +/// The `AstConverting` trait is used for converting an Abstract Syntax Tree (AST) +/// to some other form, such as an internal or intermediate representation. +/// Each method corresponds to a specific node in the AST and is responsible for +/// converting that node and its children. The methods are called upon entering and +/// exiting the tree traversal and the return result informs the visitor algorithm +/// how to proceed. pub trait AstConverting { + /// Pushes the source position of the current node onto a stack. fn push_source_position(&mut self, start: &SourcePosition, end: &SourcePosition) -> (); + + /// Pops the source position of the current node from the stack. fn pop_source_position(&mut self) -> (); + /// Converts a `NodeByteStr` node. fn emit_byte_str( &mut self, mode: TreeTraversalMode, node: &NodeByteStr, ) -> Result; + + /// Converts a `NodeTypeNameIdentifier` node. fn emit_type_name_identifier( &mut self, mode: TreeTraversalMode, node: &NodeTypeNameIdentifier, ) -> Result; + + /// Converts a `NodeImportedName` node. fn emit_imported_name( &mut self, mode: TreeTraversalMode, node: &NodeImportedName, ) -> Result; + + /// Converts a `NodeImportDeclarations` node. fn emit_import_declarations( &mut self, mode: TreeTraversalMode, node: &NodeImportDeclarations, ) -> Result; + + /// Converts a `NodeMetaIdentifier` node. fn emit_meta_identifier( &mut self, mode: TreeTraversalMode, node: &NodeMetaIdentifier, ) -> Result; + + /// Converts a `NodeVariableIdentifier` node. fn emit_variable_identifier( &mut self, mode: TreeTraversalMode, node: &NodeVariableIdentifier, ) -> Result; + + /// Converts a `NodeBuiltinArguments` node. fn emit_builtin_arguments( &mut self, mode: TreeTraversalMode, node: &NodeBuiltinArguments, ) -> Result; + + /// Converts a `NodeTypeMapKey` node. fn emit_type_map_key( &mut self, mode: TreeTraversalMode, node: &NodeTypeMapKey, ) -> Result; + + /// Converts a `NodeTypeMapValue` node. fn emit_type_map_value( &mut self, mode: TreeTraversalMode, node: &NodeTypeMapValue, ) -> Result; + + /// Converts a `NodeTypeArgument` node. fn emit_type_argument( &mut self, mode: TreeTraversalMode, node: &NodeTypeArgument, ) -> Result; + + /// Converts a `NodeScillaType` node. fn emit_scilla_type( &mut self, mode: TreeTraversalMode, node: &NodeScillaType, ) -> Result; + + /// Converts a `NodeTypeMapEntry` node. fn emit_type_map_entry( &mut self, mode: TreeTraversalMode, node: &NodeTypeMapEntry, ) -> Result; + + /// Converts a `NodeAddressTypeField` node. fn emit_address_type_field( &mut self, mode: TreeTraversalMode, node: &NodeAddressTypeField, ) -> Result; + + /// Converts a `NodeAddressType` node. fn emit_address_type( &mut self, mode: TreeTraversalMode, node: &NodeAddressType, ) -> Result; + + /// Converts a `NodeFullExpression` node. fn emit_full_expression( &mut self, mode: TreeTraversalMode, node: &NodeFullExpression, ) -> Result; + + /// Converts a `NodeMessageEntry` node. fn emit_message_entry( &mut self, mode: TreeTraversalMode, node: &NodeMessageEntry, ) -> Result; + + /// Converts a `NodePatternMatchExpressionClause` node. fn emit_pattern_match_expression_clause( &mut self, mode: TreeTraversalMode, node: &NodePatternMatchExpressionClause, ) -> Result; + + /// Converts a `NodeAtomicExpression` node. fn emit_atomic_expression( &mut self, mode: TreeTraversalMode, node: &NodeAtomicExpression, ) -> Result; + + /// Converts a `NodeContractTypeArguments` node. fn emit_contract_type_arguments( &mut self, mode: TreeTraversalMode, node: &NodeContractTypeArguments, ) -> Result; + + /// Converts a `NodeValueLiteral` node. fn emit_value_literal( &mut self, mode: TreeTraversalMode, node: &NodeValueLiteral, ) -> Result; + + /// Converts a `NodeMapAccess` node. fn emit_map_access( &mut self, mode: TreeTraversalMode, node: &NodeMapAccess, ) -> Result; + + /// Converts a `NodePattern` node. fn emit_pattern( &mut self, mode: TreeTraversalMode, node: &NodePattern, ) -> Result; + + /// Converts a `NodeArgumentPattern` node. fn emit_argument_pattern( &mut self, mode: TreeTraversalMode, node: &NodeArgumentPattern, ) -> Result; + + /// Converts a `NodePatternMatchClause` node. fn emit_pattern_match_clause( &mut self, mode: TreeTraversalMode, node: &NodePatternMatchClause, ) -> Result; + + /// Converts a `NodeBlockchainFetchArguments` node. fn emit_blockchain_fetch_arguments( &mut self, mode: TreeTraversalMode, node: &NodeBlockchainFetchArguments, ) -> Result; + + /// Converts a `NodeStatement` node. fn emit_statement( &mut self, mode: TreeTraversalMode, node: &NodeStatement, ) -> Result; + + /// Converts a `NodeRemoteFetchStatement` node. fn emit_remote_fetch_statement( &mut self, mode: TreeTraversalMode, node: &NodeRemoteFetchStatement, ) -> Result; + + /// Converts a `NodeComponentId` node. fn emit_component_id( &mut self, mode: TreeTraversalMode, node: &NodeComponentId, ) -> Result; + + /// Converts a `NodeComponentParameters` node. fn emit_component_parameters( &mut self, mode: TreeTraversalMode, node: &NodeComponentParameters, ) -> Result; + + /// Converts a `NodeParameterPair` node. fn emit_parameter_pair( &mut self, mode: TreeTraversalMode, node: &NodeParameterPair, ) -> Result; + + /// Converts a `NodeComponentBody` node. fn emit_component_body( &mut self, mode: TreeTraversalMode, node: &NodeComponentBody, ) -> Result; + + /// Converts a `NodeStatementBlock` node. fn emit_statement_block( &mut self, mode: TreeTraversalMode, node: &NodeStatementBlock, ) -> Result; + + /// Converts a `NodeTypedIdentifier` node. fn emit_typed_identifier( &mut self, mode: TreeTraversalMode, node: &NodeTypedIdentifier, ) -> Result; + + /// Converts a `NodeTypeAnnotation` node. fn emit_type_annotation( &mut self, mode: TreeTraversalMode, node: &NodeTypeAnnotation, ) -> Result; + + /// Converts a `NodeProgram` node. fn emit_program( &mut self, mode: TreeTraversalMode, node: &NodeProgram, ) -> Result; + + /// Converts a `NodeLibraryDefinition` node. fn emit_library_definition( &mut self, mode: TreeTraversalMode, node: &NodeLibraryDefinition, ) -> Result; + + /// Converts a `NodeLibrarySingleDefinition` node. fn emit_library_single_definition( &mut self, mode: TreeTraversalMode, node: &NodeLibrarySingleDefinition, ) -> Result; + + /// Converts a `NodeContractDefinition` node. fn emit_contract_definition( &mut self, mode: TreeTraversalMode, node: &NodeContractDefinition, ) -> Result; + + /// Converts a `NodeContractField` node. fn emit_contract_field( &mut self, mode: TreeTraversalMode, node: &NodeContractField, ) -> Result; + + /// Converts a `NodeWithConstraint` node. fn emit_with_constraint( &mut self, mode: TreeTraversalMode, node: &NodeWithConstraint, ) -> Result; + + /// Converts a `NodeComponentDefinition` node. fn emit_component_definition( &mut self, mode: TreeTraversalMode, node: &NodeComponentDefinition, ) -> Result; + + /// Converts a `NodeProcedureDefinition` node. fn emit_procedure_definition( &mut self, mode: TreeTraversalMode, node: &NodeProcedureDefinition, ) -> Result; + + /// Converts a `NodeTransitionDefinition` node. fn emit_transition_definition( &mut self, mode: TreeTraversalMode, node: &NodeTransitionDefinition, ) -> Result; + + /// Converts a `NodeTypeAlternativeClause` node. fn emit_type_alternative_clause( &mut self, mode: TreeTraversalMode, node: &NodeTypeAlternativeClause, ) -> Result; + + /// Converts a `NodeTypeMapValueArguments` node. fn emit_type_map_value_arguments( &mut self, mode: TreeTraversalMode, node: &NodeTypeMapValueArguments, ) -> Result; + + /// Converts a `NodeTypeMapValueAllowingTypeArguments` node. fn emit_type_map_value_allowing_type_arguments( &mut self, mode: TreeTraversalMode, diff --git a/products/bluebell/core/src/ast/nodes.rs b/products/bluebell/core/src/ast/nodes.rs index de56651e7..e0931fba3 100644 --- a/products/bluebell/core/src/ast/nodes.rs +++ b/products/bluebell/core/src/ast/nodes.rs @@ -1,32 +1,39 @@ -use crate::parser::lexer::SourcePosition; use std::fmt; -/* -Things that need renaming: -AtomicSid -> ???? -EventType -> AutoType ;; Essentially a JSON dict -*/ +use crate::parser::lexer::SourcePosition; +/// A wrapper struct that adds source position to an AST node. #[derive(Clone, Debug, PartialEq, PartialOrd, Eq)] pub struct WithMetaData { + /// The AST node pub node: T, + /// The starting position of the AST node in the source code pub start: SourcePosition, + /// The ending position of the AST node in the source code pub end: SourcePosition, } +/// Implementing Display trait for WithMetaData struct impl fmt::Display for WithMetaData { + /// Formats the WithMetaData instance into a string fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.node.to_string()) } } +/// NodeByteStr represents a byte string node in the AST #[derive(Clone, Debug, PartialEq, PartialOrd, Eq)] pub enum NodeByteStr { - Constant(WithMetaData), // TODO: Aparently not used anywhere + /// Represents a constant byte string + /// Example: `let x = "constant";` + Constant(WithMetaData), // TODO: Apparently not used anywhere + /// Represents a byte string type + /// Example: `let x: ByStr = "type";` Type(WithMetaData), } impl NodeByteStr { + /// Converts the NodeByteStr to a string pub fn to_string(&self) -> String { match self { NodeByteStr::Constant(s) => s.node.clone(), @@ -40,14 +47,22 @@ impl fmt::Display for NodeByteStr { } } +/// NodeTypeNameIdentifier represents a type name identifier node in the AST #[derive(Clone, Debug, PartialEq, PartialOrd, Eq)] pub enum NodeTypeNameIdentifier { + /// Represents a byte string type + /// Example: `let x: ByStr = "type";` ByteStringType(WithMetaData), + /// Represents an event type + /// Example: `event e;` EventType, + /// Represents a type or enum-like identifier + /// Example: `let x: CustomType = "type";` TypeOrEnumLikeIdentifier(WithMetaData), } impl NodeTypeNameIdentifier { + /// Converts the NodeTypeNameIdentifier to a string pub fn to_string(&self) -> String { match self { NodeTypeNameIdentifier::ByteStringType(byte_str) => { @@ -66,32 +81,48 @@ impl fmt::Display for NodeTypeNameIdentifier { } } +/// NodeImportedName represents an imported name node in the AST #[derive(Clone, Debug, PartialEq, PartialOrd, Eq)] pub enum NodeImportedName { + /// Represents a regular import + /// Example: `import CustomType;` RegularImport(WithMetaData), + /// Represents an aliased import + /// Example: `import CustomType as Alias;` AliasedImport( WithMetaData, WithMetaData, ), } +/// NodeImportDeclarations represents a list of import declarations in the AST #[derive(Clone, Debug, PartialEq, PartialOrd, Eq)] pub struct NodeImportDeclarations { pub import_list: Vec>, } +/// NodeMetaIdentifier represents a meta identifier node in the AST #[derive(Clone, Debug, PartialEq, PartialOrd, Eq)] pub enum NodeMetaIdentifier { + /// Represents a meta name + /// Example: `let x: MetaName = "type";` MetaName(WithMetaData), + /// Represents a meta name in a namespace + /// Example: `let x: Namespace.MetaName = "type";` MetaNameInNamespace( WithMetaData, WithMetaData, ), + /// Represents a meta name in a hexspace + /// Example: `let x: 0x123.MetaName = "type";` MetaNameInHexspace(WithMetaData, WithMetaData), + /// Represents a byte string + /// Example: `let x: ByStr = "type";` ByteString, } impl NodeMetaIdentifier { + /// Converts the NodeMetaIdentifier to a string pub fn to_string(&self) -> String { match self { NodeMetaIdentifier::MetaName(name) => { @@ -114,14 +145,22 @@ impl fmt::Display for NodeMetaIdentifier { } } +/// NodeVariableIdentifier represents a variable identifier node in the AST #[derive(Clone, Debug, PartialEq, PartialOrd, Eq)] pub enum NodeVariableIdentifier { + /// Represents a variable name + /// Example: `let x = "variable";` VariableName(WithMetaData), + /// Represents a special identifier + /// Example: `let _ = "special";` SpecialIdentifier(WithMetaData), + /// Represents a variable in a namespace + /// Example: `let x: Namespace.Variable = "variable";` VariableInNamespace(WithMetaData, WithMetaData), } impl NodeVariableIdentifier { + /// Converts the NodeVariableIdentifier to a string pub fn to_string(&self) -> String { match self { NodeVariableIdentifier::VariableName(name) => format!("{}", name), @@ -139,65 +178,113 @@ impl fmt::Display for NodeVariableIdentifier { } } +/// NodeBuiltinArguments represents a list of arguments for a built-in function in the AST #[derive(Clone, Debug, PartialEq, PartialOrd, Eq)] pub struct NodeBuiltinArguments { pub arguments: Vec>, } +/// NodeTypeMapKey represents a type map key node in the AST #[derive(Clone, Debug, PartialEq, PartialOrd, Eq)] pub enum NodeTypeMapKey { + /// Represents a generic map key + /// Example: `let x: Map (KeyType, ValueType) = Emp;` GenericMapKey(WithMetaData), + /// Represents an enclosed generic id + /// Example: `let x: Map ((KeyType), ValueType) = Emp;` EnclosedGenericId(WithMetaData), + /// Represents an enclosed address map key type + /// Example: `let x: Map ((ByStr20), ValueType) = Emp;` EnclosedAddressMapKeyType(WithMetaData), + /// Represents an address map key type + /// Example: `let x: Map (ByStr20, ValueType) = Emp;` AddressMapKeyType(WithMetaData), } +/// NodeTypeMapValue represents a type map value node in the AST #[derive(Clone, Debug, PartialEq, PartialOrd, Eq)] pub enum NodeTypeMapValue { + /// Represents a map value type or enum-like identifier + /// Example: `let x: Map (KeyType, ValueType) = Emp;` MapValueTypeOrEnumLikeIdentifier(WithMetaData), + /// Represents a map key value type + /// Example: `let x: Map (KeyType, (KeyType, ValueType)) = Emp;` MapKeyValue(Box>), + /// Represents a map value paranthesized type + /// Example: `let x: Map (KeyType, (ValueType)) = Emp;` MapValueParanthesizedType(Box>), + /// Represents a map value address type + /// Example: `let x: Map (KeyType, ByStr20) = Emp;` MapValueAddressType(Box>), } +/// NodeTypeArgument represents a type argument node in the AST #[derive(Clone, Debug, PartialEq, PartialOrd, Eq)] pub enum NodeTypeArgument { + /// Represents an enclosed type argument + /// Example: `let x: CustomType (ArgType) = "type";` EnclosedTypeArgument(Box>), + /// Represents a generic type argument + /// Example: `let x: CustomType ArgType = "type";` GenericTypeArgument(WithMetaData), + /// Represents a template type argument + /// Example: `let x: CustomType "ArgType" = "type";` TemplateTypeArgument(WithMetaData), + /// Represents an address type argument + /// Example: `let x: CustomType ByStr20 = "type";` AddressTypeArgument(WithMetaData), + /// Represents a map type argument + /// Example: `let x: CustomType (KeyType, ValueType) = "type";` MapTypeArgument(WithMetaData, WithMetaData), } +/// NodeScillaType represents a Scilla type node in the AST #[derive(Clone, Debug, PartialEq, PartialOrd, Eq)] pub enum NodeScillaType { + /// Represents a generic type with arguments + /// Example: `let x: CustomType ArgType = "type";` GenericTypeWithArgs( WithMetaData, Vec>, ), + /// Represents a map type + /// Example: `let x: Map (KeyType, ValueType) = Emp;` MapType(WithMetaData, WithMetaData), + /// Represents a function type + /// Example: `let x: Fun (ArgType) ReturnType = fun (arg : ArgType) => arg;` FunctionType( Box>, Box>, ), + /// Represents an enclosed type + /// Example: `let x: (CustomType) = "type";` EnclosedType(Box>), + /// Represents a Scilla address type + /// Example: `let x: ByStr20 = "0x123";` ScillaAddresseType(Box>), + /// Represents a poly function type + /// Example: `let x: forall 'A. ('A -> 'A) = fun (arg : 'A) => arg;` PolyFunctionType(WithMetaData, Box>), + /// Represents a type var type + /// Example: `let x: 'A = "type";` TypeVarType(WithMetaData), } +/// NodeTypeMapEntry represents a type map entry node in the AST #[derive(Clone, Debug, PartialEq, PartialOrd, Eq)] pub struct NodeTypeMapEntry { pub key: WithMetaData, pub value: WithMetaData, } +/// NodeAddressTypeField represents an address type field node in the AST #[derive(Clone, Debug, PartialEq, PartialOrd, Eq)] pub struct NodeAddressTypeField { pub identifier: WithMetaData, pub type_name: WithMetaData, } +/// NodeAddressType represents an address type node in the AST #[derive(Clone, Debug, PartialEq, PartialOrd, Eq)] pub struct NodeAddressType { pub identifier: WithMetaData, @@ -205,207 +292,332 @@ pub struct NodeAddressType { pub address_fields: Vec>, } +/// NodeFullExpression represents a full expression node in the AST #[derive(Clone, Debug, PartialEq, PartialOrd, Eq)] pub enum NodeFullExpression { + /// Represents a local variable declaration + /// Example: `let x = "variable";` LocalVariableDeclaration { identifier_name: WithMetaData, expression: Box>, type_annotation: Option>, containing_expression: Box>, }, + /// Represents a function declaration + /// Example: `let f = fun (arg : ArgType) => arg;` FunctionDeclaration { identier_value: WithMetaData, type_annotation: WithMetaData, expression: Box>, }, + /// Represents a function call + /// Example: `f(arg);` FunctionCall { function_name: WithMetaData, argument_list: Vec>, }, + /// Represents an atomic expression + /// Example: `let x = "atomic";` ExpressionAtomic(Box>), + /// Represents a built-in expression + /// Example: `let x = builtin f arg;` ExpressionBuiltin { b: WithMetaData, targs: Option>, xs: WithMetaData, }, + /// Represents a message + /// Example: `msg = { _tag : "tag", _recipient : "0x123", _amount : "0", param : "value" };` Message(Vec>), + /// Represents a match expression + /// Example: `match x with | Nil => "nil" | Cons a b => "cons" end` Match { match_expression: WithMetaData, clauses: Vec>, }, + /// Represents a constructor call + /// Example: `let x = CustomType arg;` ConstructorCall { identifier_name: WithMetaData, contract_type_arguments: Option>, argument_list: Vec>, }, + /// Represents a template function + /// Example: `let x = tfun 'A => fun (arg : 'A) => arg;` TemplateFunction { identifier_name: WithMetaData, expression: Box>, }, + /// Represents a type application + /// Example: `let x = @CustomType arg;` TApp { identifier_name: WithMetaData, type_arguments: Vec>, }, } +/// NodeMessageEntry represents a message entry node in the AST +/// It can either be a MessageLiteral or a MessageVariable +/// Example: `msg = { _tag : "tag", _recipient : "0x123", _amount : "0", param : "value" };` #[derive(Clone, Debug, PartialEq, PartialOrd, Eq)] pub enum NodeMessageEntry { + /// Represents a message literal + /// Example: `msg = { _tag : "tag", _recipient : "0x123", _amount : "0", param : "value" };` MessageLiteral( WithMetaData, WithMetaData, ), + /// Represents a message variable + /// Example: `msg = { _tag : "tag", _recipient : "0x123", _amount : "0", param : variable };` MessageVariable( WithMetaData, WithMetaData, ), } +/// NodePatternMatchExpressionClause represents a pattern match expression clause node in the AST +/// It contains a pattern and an expression #[derive(Clone, Debug, PartialEq, PartialOrd, Eq)] pub struct NodePatternMatchExpressionClause { + /// The pattern of the clause pub pattern: WithMetaData, + /// The expression of the clause pub expression: WithMetaData, } +/// NodeAtomicExpression represents an atomic expression node in the AST +/// It can either be an AtomicSid or an AtomicLit #[derive(Clone, Debug, PartialEq, PartialOrd, Eq)] pub enum NodeAtomicExpression { + /// Represents an atomic sid + /// Example: `let x = sid;` AtomicSid(WithMetaData), + /// Represents an atomic literal + /// Example: `let x = "literal";` AtomicLit(WithMetaData), } +/// NodeContractTypeArguments represents a contract type arguments node in the AST +/// It contains a vector of type arguments #[derive(Clone, Debug, PartialEq, PartialOrd, Eq)] pub struct NodeContractTypeArguments { + /// The type arguments of the contract pub type_arguments: Vec>, } +/// NodeValueLiteral represents a value literal node in the AST +/// It can either be a LiteralInt, LiteralHex, LiteralString or LiteralEmptyMap #[derive(Clone, Debug, PartialEq, PartialOrd, Eq)] pub enum NodeValueLiteral { + /// Represents a literal integer + /// Example: `let x = 10;` LiteralInt(WithMetaData, WithMetaData), + /// Represents a literal hexadecimal + /// Example: `let x = 0x123;` LiteralHex(WithMetaData), + /// Represents a literal string + /// Example: `let x = "string";` LiteralString(WithMetaData), + /// Represents a literal empty map + /// Example: `let x: Map (KeyType, ValueType) = Emp;` LiteralEmptyMap(WithMetaData, WithMetaData), } +/// NodeMapAccess represents a map access node in the AST +/// It contains an identifier name #[derive(Clone, Debug, PartialEq, PartialOrd, Eq)] pub struct NodeMapAccess { + /// The identifier name of the map access pub identifier_name: WithMetaData, } +/// NodePattern represents a pattern node in the AST +/// It can either be a Wildcard, Binder or Constructor #[derive(Clone, Debug, PartialEq, PartialOrd, Eq)] pub enum NodePattern { + /// Represents a wildcard pattern + /// Example: `match x with | _ => "wildcard" end` Wildcard, + /// Represents a binder pattern + /// Example: `match x with | a => "binder" end` Binder(WithMetaData), + /// Represents a constructor pattern + /// Example: `match x with | Cons a b => "constructor" end` Constructor( WithMetaData, Vec>, ), } +/// NodeArgumentPattern represents an argument pattern node in the AST +/// It can either be a WildcardArgument, BinderArgument, ConstructorArgument or PatternArgument #[derive(Clone, Debug, PartialEq, PartialOrd, Eq)] pub enum NodeArgumentPattern { + /// Represents a wildcard argument + /// Example: `match x with | Cons _ _ => "wildcard argument" end` WildcardArgument, + /// Represents a binder argument + /// Example: `match x with | Cons a _ => "binder argument" end` BinderArgument(WithMetaData), + /// Represents a constructor argument + /// Example: `match x with | Cons (Cons a b) _ => "constructor argument" end` ConstructorArgument(WithMetaData), + /// Represents a pattern argument + /// Example: `match x with | Cons (Cons a _) _ => "pattern argument" end` PatternArgument(Box>), } +/// NodePatternMatchClause represents a pattern match clause node in the AST +/// It contains a pattern expression and an optional statement block #[derive(Clone, Debug, PartialEq, PartialOrd, Eq)] pub struct NodePatternMatchClause { + /// The pattern expression of the clause pub pattern_expression: Box>, + /// The statement block of the clause pub statement_block: Option>, } +/// NodeBlockchainFetchArguments represents a blockchain fetch arguments node in the AST +/// It contains a vector of arguments #[derive(Clone, Debug, PartialEq, PartialOrd, Eq)] pub struct NodeBlockchainFetchArguments { + /// The arguments of the blockchain fetch pub arguments: Vec>, } +/// NodeStatement represents a statement node in the AST +/// It can be one of many different types of statements #[derive(Clone, Debug, PartialEq, PartialOrd, Eq)] pub enum NodeStatement { + /// Represents a load statement + /// Example: `load x;` Load { left_hand_side: WithMetaData, right_hand_side: WithMetaData, }, + /// Represents a remote fetch statement + /// Example: `fetch x from remote;` RemoteFetch(Box), + /// Represents a store statement + /// Example: `store x;` Store { left_hand_side: WithMetaData, right_hand_side: WithMetaData, }, + /// Represents a bind statement + /// Example: `bind x = y;` Bind { left_hand_side: WithMetaData, right_hand_side: Box>, }, + /// Represents a read from blockchain statement + /// Example: `read x from bc;` ReadFromBC { left_hand_side: WithMetaData, type_name: WithMetaData, arguments: Option, }, + /// Represents a map get statement + /// Example: `get x from map;` MapGet { left_hand_side: WithMetaData, keys: Vec>, right_hand_side: WithMetaData, }, + /// Represents a map get exists statement + /// Example: `get x from map if exists;` MapGetExists { left_hand_side: WithMetaData, keys: Vec>, right_hand_side: WithMetaData, }, + /// Represents a map update statement + /// Example: `update x in map;` MapUpdate { left_hand_side: WithMetaData, keys: Vec>, right_hand_side: WithMetaData, }, + /// Represents a map update delete statement + /// Example: `delete x from map;` MapUpdateDelete { left_hand_side: WithMetaData, keys: Vec>, }, + /// Represents an accept statement + /// Example: `accept;` Accept, + /// Represents a send statement + /// Example: `send x;` Send { identifier_name: WithMetaData, }, + /// Represents a create event statement + /// Example: `create event x;` CreateEvnt { identifier_name: WithMetaData, }, + /// Represents a throw statement + /// Example: `throw x;` Throw { error_variable: Option>, }, + /// Represents a match statement + /// Example: `match x with | Nil => "nil" | Cons a b => "cons" end` MatchStmt { variable: WithMetaData, clauses: Vec>, }, + /// Represents a call procedure statement + /// Example: `call proc x;` CallProc { component_id: WithMetaData, arguments: Vec>, }, + /// Represents an iterate statement + /// Example: `iterate x over y;` Iterate { identifier_name: WithMetaData, component_id: WithMetaData, }, } +/// NodeRemoteFetchStatement represents a remote fetch statement node in the AST +/// It can be one of many different types of remote fetch statements #[derive(Clone, Debug, PartialEq, PartialOrd, Eq)] pub enum NodeRemoteFetchStatement { + /// Represents a read state mutable statement + /// Example: `read x from state;` ReadStateMutable( WithMetaData, WithMetaData, WithMetaData, ), + /// Represents a read state mutable special id statement + /// Example: `read x from state with id;` ReadStateMutableSpecialId( WithMetaData, WithMetaData, WithMetaData, ), + /// Represents a read state mutable map access statement + /// Example: `read x from state with map access;` ReadStateMutableMapAccess( WithMetaData, WithMetaData, WithMetaData, Vec>, ), + /// Represents a read state mutable map access exists statement + /// Example: `read x from state with map access if exists;` ReadStateMutableMapAccessExists( WithMetaData, WithMetaData, WithMetaData, Vec>, ), + /// Represents a read state mutable cast address statement + /// Example: `read x from state with cast address;` ReadStateMutableCastAddress( WithMetaData, WithMetaData, @@ -413,64 +625,105 @@ pub enum NodeRemoteFetchStatement { ), } +/// NodeComponentId represents a component id node in the AST +/// It can either be a WithTypeLikeName or a WithRegularId #[derive(Clone, Debug, PartialEq, PartialOrd, Eq)] pub enum NodeComponentId { + /// Represents a component id with a type like name + /// Example: `component WithTypeLikeName;` WithTypeLikeName(WithMetaData), + /// Represents a component id with a regular id + /// Example: `component WithRegularId;` WithRegularId(WithMetaData), } +/// NodeComponentParameters represents a component parameters node in the AST +/// It contains a vector of parameters #[derive(Clone, Debug, PartialEq, PartialOrd, Eq)] pub struct NodeComponentParameters { + /// The parameters of the component pub parameters: Vec>, } +/// NodeParameterPair represents a parameter pair node in the AST +/// It contains an identifier with type #[derive(Clone, Debug, PartialEq, PartialOrd, Eq)] pub struct NodeParameterPair { + /// The identifier with type of the parameter pair pub identifier_with_type: WithMetaData, } +/// NodeComponentBody represents a component body node in the AST +/// It contains an optional statement block #[derive(Clone, Debug, PartialEq, PartialOrd, Eq)] pub struct NodeComponentBody { + /// The statement block of the component body pub statement_block: Option>, } +/// NodeStatementBlock represents a statement block node in the AST +/// It contains a vector of statements #[derive(Clone, Debug, PartialEq, PartialOrd, Eq)] pub struct NodeStatementBlock { + /// The statements of the statement block pub statements: Vec, } +/// NodeTypedIdentifier represents a typed identifier node in the AST +/// It contains an identifier name and an annotation #[derive(Clone, Debug, PartialEq, PartialOrd, Eq)] pub struct NodeTypedIdentifier { + /// The identifier name of the typed identifier pub identifier_name: WithMetaData, + /// The annotation of the typed identifier pub annotation: WithMetaData, } +/// NodeTypeAnnotation represents a type annotation node in the AST +/// It contains a type name #[derive(Clone, Debug, PartialEq, PartialOrd, Eq)] pub struct NodeTypeAnnotation { + /// The type name of the type annotation pub type_name: WithMetaData, } +/// NodeProgram represents a program node in the AST +/// It contains a version, optional import declarations, optional library definition and a contract definition #[derive(Clone, Debug, PartialEq, PartialOrd, Eq)] pub struct NodeProgram { + /// The version of the program pub version: WithMetaData, + /// The import declarations of the program pub import_declarations: Option>, + /// The library definition of the program pub library_definition: Option>, + /// The contract definition of the program pub contract_definition: WithMetaData, } +/// NodeLibraryDefinition represents a library definition node in the AST +/// It contains a name and a vector of definitions #[derive(Clone, Debug, PartialEq, PartialOrd, Eq)] pub struct NodeLibraryDefinition { + /// The name of the library definition pub name: WithMetaData, + /// The definitions of the library definition pub definitions: Vec>, } +/// NodeLibrarySingleDefinition represents a library single definition node in the AST +/// It can either be a LetDefinition or a TypeDefinition #[derive(Clone, Debug, PartialEq, PartialOrd, Eq)] pub enum NodeLibrarySingleDefinition { + /// Represents a let definition + /// Example: `let x = y;` LetDefinition { variable_name: WithMetaData, type_annotation: Option>, expression: WithMetaData, }, + /// Represents a type definition + /// Example: `type x = y;` TypeDefinition( // TODO: Enum definition WithMetaData, @@ -478,65 +731,116 @@ pub enum NodeLibrarySingleDefinition { ), } +/// NodeContractDefinition represents a contract definition node in the AST +/// It contains a contract name, parameters, optional constraint, fields and components #[derive(Clone, Debug, PartialEq, PartialOrd, Eq)] pub struct NodeContractDefinition { + /// The contract name of the contract definition pub contract_name: WithMetaData, + /// The parameters of the contract definition pub parameters: WithMetaData, + /// The constraint of the contract definition pub constraint: Option>, + /// The fields of the contract definition pub fields: Vec>, + /// The components of the contract definition pub components: Vec>, } +/// NodeContractField represents a contract field node in the AST +/// It contains a typed identifier and a right hand side #[derive(Clone, Debug, PartialEq, PartialOrd, Eq)] pub struct NodeContractField { + /// The typed identifier of the contract field pub typed_identifier: WithMetaData, + /// The right hand side of the contract field pub right_hand_side: WithMetaData, } +/// NodeWithConstraint represents a with constraint node in the AST +/// It contains an expression #[derive(Clone, Debug, PartialEq, PartialOrd, Eq)] pub struct NodeWithConstraint { + /// The expression of the with constraint pub expression: Box>, } +/// NodeComponentDefinition represents a component definition node in the AST +/// It can either be a TransitionComponent or a ProcedureComponent #[derive(Clone, Debug, PartialEq, PartialOrd, Eq)] pub enum NodeComponentDefinition { + /// Represents a transition component + /// Example: `transition x;` TransitionComponent(Box>), + /// Represents a procedure component + /// Example: `procedure x;` ProcedureComponent(Box>), } +/// NodeProcedureDefinition represents a procedure definition node in the AST +/// It contains a name, parameters and a body #[derive(Clone, Debug, PartialEq, PartialOrd, Eq)] pub struct NodeProcedureDefinition { + /// The name of the procedure definition pub name: WithMetaData, + /// The parameters of the procedure definition pub parameters: WithMetaData, + /// The body of the procedure definition pub body: WithMetaData, } +/// NodeTransitionDefinition represents a transition definition node in the AST +/// It contains a name, parameters and a body +/// Example: `transition Transfer (from: ByStr20, to: ByStr20, amount: Uint128) = ...` #[derive(Clone, Debug, PartialEq, PartialOrd, Eq)] pub struct NodeTransitionDefinition { + /// The name of the transition definition pub name: WithMetaData, + /// The parameters of the transition definition pub parameters: WithMetaData, + /// The body of the transition definition pub body: WithMetaData, } +/// NodeTypeAlternativeClause represents an alternative clause node in the AST +/// It can either be a ClauseType or a ClauseTypeWithArgs #[derive(Clone, Debug, PartialEq, PartialOrd, Eq)] pub enum NodeTypeAlternativeClause { + /// Represents a clause type + /// Example: `match x with | ClauseType => ...` ClauseType(WithMetaData), + /// Represents a clause type with arguments + /// Example: `match x with | ClauseType arg1 arg2 => ...` ClauseTypeWithArgs( WithMetaData, Vec>, ), } +/// NodeTypeMapValueArguments represents map value arguments node in the AST +/// It can either be an EnclosedTypeMapValue, a GenericMapValueArgument or a MapKeyValueType #[derive(Clone, Debug, PartialEq, PartialOrd, Eq)] pub enum NodeTypeMapValueArguments { + /// Represents an enclosed type map value + /// Example: `let x: Map ((KeyType), ValueType) = Emp;` EnclosedTypeMapValue(Box>), + /// Represents a generic map value argument + /// Example: `let x: Map (KeyType, ValueType) = Emp;` GenericMapValueArgument(WithMetaData), + /// Represents a map key value type + /// Example: `let x: Map ((ByStr20), ValueType) = Emp;` MapKeyValueType(WithMetaData, WithMetaData), } +/// NodeTypeMapValueAllowingTypeArguments represents a map value allowing type arguments node in the AST +/// It can either be a TypeMapValueNoArgs or a TypeMapValueWithArgs #[derive(Clone, Debug, PartialEq, PartialOrd, Eq)] pub enum NodeTypeMapValueAllowingTypeArguments { + /// Represents a type map value with no arguments + /// Example: `let x: Map (KeyType, ValueType) = Emp;` TypeMapValueNoArgs(WithMetaData), + /// Represents a type map value with arguments + /// Example: `let x: Map ((KeyType), ValueType) = Emp;` TypeMapValueWithArgs( WithMetaData, Vec>, diff --git a/products/bluebell/core/src/ast/visitor.rs b/products/bluebell/core/src/ast/visitor.rs index 5b1982da4..ee81353b9 100644 --- a/products/bluebell/core/src/ast/visitor.rs +++ b/products/bluebell/core/src/ast/visitor.rs @@ -1,7 +1,12 @@ -use crate::ast::converting::AstConverting; -use crate::ast::nodes::*; -use crate::constants::{TraversalResult, TreeTraversalMode}; - +use crate::{ + ast::{converting::AstConverting, nodes::*}, + constants::{TraversalResult, TreeTraversalMode}, +}; + +/// The `AstVisitor` trait is used for implementing the visiting behaviour for each AST node of the Scilla AST. +/// Each node in the AST implements this trait to define how it should be visited during the tree traversal. +/// The `visit` method is called with an `emitter` that implements the `AstConverting` trait, which is responsible for converting the AST to some other form. +/// The `visit` method returns a `Result` with a `TraversalResult` that informs the visitor algorithm how to proceed, or a `String` in case of an error. pub trait AstVisitor { fn visit(&self, emitter: &mut dyn AstConverting) -> Result; } diff --git a/products/bluebell/core/src/errors.rs b/products/bluebell/core/src/errors.rs index c4ebb7559..c907a5666 100644 --- a/products/bluebell/core/src/errors.rs +++ b/products/bluebell/core/src/errors.rs @@ -1,7 +1,14 @@ use crate::parser::lexer; +// Struct to represent source errors +// Contains the position of the error in the source code and the error message pub struct SourceError { + /// Source position of the error pub position: lexer::SourcePosition, + + /// Associated error message pub message: String, } + +// Type alias for a list of source errors pub type ErrorList = Vec; diff --git a/products/bluebell/core/src/evm_bytecode_generator.rs b/products/bluebell/core/src/evm_bytecode_generator.rs index be1345678..03f8ec48c 100644 --- a/products/bluebell/core/src/evm_bytecode_generator.rs +++ b/products/bluebell/core/src/evm_bytecode_generator.rs @@ -1,27 +1,25 @@ -use crate::constants::TreeTraversalMode; -use crate::intermediate_representation::pass::IrPass; -use crate::intermediate_representation::primitives::Operation; -use crate::intermediate_representation::primitives::{ - ConcreteFunction, ConcreteType, IntermediateRepresentation, IrLowering, +use std::{collections::BTreeSet, mem, str::FromStr}; + +use evm_assembly::{ + block::EvmBlock, + compiler_context::EvmCompilerContext, + executable::EvmExecutable, + instruction::EvmSourcePosition, + types::{EvmType, EvmTypeValue}, + EvmByteCodeBuilder, }; -use crate::intermediate_representation::symbol_table::StateLayoutEntry; -use crate::passes::debug_printer::DebugPrinter; -use evm_assembly::block::EvmBlock; -use evm_assembly::compiler_context::EvmCompilerContext; -use evm_assembly::executable::EvmExecutable; -use evm_assembly::instruction::EvmSourcePosition; -use evm_assembly::types::EvmType; -use log::warn; use primitive_types::U256; -use std::collections::BTreeSet; -use std::str::FromStr; - -use evm_assembly::types::EvmTypeValue; -use evm_assembly::EvmAssemblyGenerator; -use evm_assembly::EvmByteCodeBuilder; -use log::info; use sha3::{Digest, Keccak256}; -use std::mem; + +use crate::{ + constants::TreeTraversalMode, + intermediate_representation::{ + pass::IrPass, + primitives::{IntermediateRepresentation, Operation}, + symbol_table::StateLayoutEntry, + }, + passes::debug_printer::DebugPrinter, +}; /// `EvmBytecodeGenerator` is a structure responsible for generating Ethereum Virtual Machine (EVM) bytecode. /// It stores an EVM bytecode builder and an intermediate representation (IR) of the program to be compiled. @@ -78,7 +76,6 @@ impl<'ctx> EvmBytecodeGenerator<'ctx> { } Ok(()) - // unimplemented!() } /// This function writes function definitions from the IR to the EVM module. @@ -463,7 +460,7 @@ impl<'ctx> EvmBytecodeGenerator<'ctx> { evm_block.push_u256(address); evm_block.set_next_rust_position(file!().to_string(), line!() as usize); evm_block.external_sload(); - evm_block.register_stack_name(value_name); + let _ = evm_block.register_stack_name(value_name); } Operation::Return(ref _value) => { // Assumes that the next element on the stack is return pointer @@ -494,13 +491,18 @@ impl<'ctx> EvmBytecodeGenerator<'ctx> { } }; - // TODO: Assumes that a static call just produces the Keccak of the name - // The "correct" to do would be to make this spec dependant - let hash = Keccak256::digest(name); - let mut selector = Vec::new(); - selector.extend_from_slice(&hash[..4]); - evm_block.set_next_rust_position(file!().to_string(), line!() as usize); - evm_block.push(selector); + let ctx = &mut code_builder.context; + if let Some(constructor) = &ctx.default_constructors.get(name) { + constructor(&mut evm_block); + } else { + // Falling back to plain enum type naming with no data associated + // for custom types. + let hash = Keccak256::digest(name); + let mut selector = Vec::new(); + selector.extend_from_slice(&hash[..4]); + evm_block.set_next_rust_position(file!().to_string(), line!() as usize); + evm_block.push(selector); + } } Operation::IsEqual { ref left, @@ -526,30 +528,6 @@ impl<'ctx> EvmBytecodeGenerator<'ctx> { evm_block.set_next_rust_position(file!().to_string(), line!() as usize); evm_block.eq(); } - Operation::Switch { - // TODO: Deprecated? - ref cases, - ref on_default, - } => { - for case in cases { - let label = match &case.label.resolved { - Some(l) => l, - None => panic!("Could not resolve case label"), - }; - // TODO: This assumes order in cases - evm_block.set_next_rust_position(file!().to_string(), line!() as usize); - evm_block.jump_if_to(&code_builder.add_scope_to_label(label)); - } - - let label = match &on_default.resolved { - Some(l) => l, - None => panic!("Could not resolve default label"), - }; - - evm_block.set_next_rust_position(file!().to_string(), line!() as usize); - evm_block.jump_to(&code_builder.add_scope_to_label(label)); - // unimplemented!() // Add handling for other operations here - } Operation::Jump(label) => { let label = match &label.resolved { Some(l) => l, @@ -676,7 +654,7 @@ impl<'ctx> EvmBytecodeGenerator<'ctx> { // Ignore terminating ref as this will just be pop at the end of the block. } _ => { - unimplemented!() // Add handling for other operations here + panic!("Unhandled operation {:#?}",instr); } } @@ -734,35 +712,3 @@ impl<'ctx> EvmBytecodeGenerator<'ctx> { Ok(self.builder.build()) } } - -/// This impl block provides the lowering operations for our `EvmBytecodeGenerator`. -/// Here we translate high-level intermediate representation (IR) constructs into -/// lower-level constructs that are suitable for generating EVM bytecode. -impl<'ctx> IrLowering for EvmBytecodeGenerator<'ctx> { - /// This function takes a `ConcreteType` and lowers it into a form suitable for generating - /// EVM bytecode. How exactly this is done will depend on the concrete type in question. - fn lower_concrete_type(&mut self, _con_type: &ConcreteType) { - // TODO: Implement - unimplemented!() - } - - /// This function takes a `ConcreteFunction` and lowers it into a form suitable - /// for generating EVM bytecode. This typically involves translating the function's - /// high-level operations into equivalent sequences of low-level EVM operations. - fn lower_concrete_function(&mut self, _con_function: &ConcreteFunction) { - // TODO: Move write_function_definitions_to_module into this structure - unimplemented!() - } - - /// This is the main interface for lowering. It takes an intermediate representation (IR) - /// and lowers all its types and function definitions. - fn lower(&mut self, primitives: &IntermediateRepresentation) { - for con_type in &primitives.type_definitions { - self.lower_concrete_type(con_type); - } - - for con_function in &primitives.function_definitions { - self.lower_concrete_function(con_function); - } - } -} diff --git a/products/bluebell/core/src/formatter.rs b/products/bluebell/core/src/formatter.rs index a6cc4ab0b..6081a14de 100644 --- a/products/bluebell/core/src/formatter.rs +++ b/products/bluebell/core/src/formatter.rs @@ -1,8 +1,8 @@ -use crate::ast::converting::AstConverting; -use crate::ast::nodes::*; -use crate::ast::visitor::AstVisitor; -use crate::constants::{TraversalResult, TreeTraversalMode}; -use crate::parser::lexer::SourcePosition; +use crate::{ + ast::{converting::AstConverting, nodes::*, visitor::AstVisitor}, + constants::{TraversalResult, TreeTraversalMode}, + parser::lexer::SourcePosition, +}; /// `BluebellFormatter` is a structure responsible for generating a formatted script from an AST. /// It stores the current indentation level for the partially generated script `script`. diff --git a/products/bluebell/core/src/intermediate_representation/emitter.rs b/products/bluebell/core/src/intermediate_representation/emitter.rs index 6acd48b3c..493d2142d 100644 --- a/products/bluebell/core/src/intermediate_representation/emitter.rs +++ b/products/bluebell/core/src/intermediate_representation/emitter.rs @@ -1,46 +1,97 @@ -use crate::ast::converting::AstConverting; -use crate::ast::nodes::*; -use crate::ast::visitor::AstVisitor; -use crate::constants::{TraversalResult, TreeTraversalMode}; -use crate::intermediate_representation::primitives::*; -use crate::parser::lexer::SourcePosition; - -use log::warn; use std::mem; +use log::info; + +use crate::{ + ast::{converting::AstConverting, nodes::*, visitor::AstVisitor}, + constants::{TraversalResult, TreeTraversalMode}, + intermediate_representation::{primitives::*, symbol_table::SymbolTable}, + parser::lexer::SourcePosition, +}; + +/// Byte Code Generation Process +/// +/// The process of generating byte code from Scilla source code involves several steps and transformations. +/// Here is a high-level overview of the process: +/// +/// ```plaintext +/// [Scilla source code] +/// | +/// v +/// [Abstract Syntax Tree (AST)] +/// | +/// | (AstConverting) +/// v +/// [Intermediate Representation (IR)] +/// | +/// | (PassManager) +/// v +/// [Optimized Intermediate Representation] +/// | +/// | (EvmBytecodeGenerator) +/// v +/// [EVM Bytecode] +/// ``` +/// +/// Each arrow (`| v`) represents a transformation or a step in the process. +/// The name in parentheses (e.g., `(AstConverting)`) is the component or the process that performs the transformation. +/// +/// 1. Scilla source code is parsed into an Abstract Syntax Tree (AST). +/// 2. The AST is converted into an Intermediate Representation (IR) using the `AstConverting` trait. +/// 3. The IR is optimized using the `PassManager`. +/// 4. The optimized IR is then converted into EVM bytecode using the `EvmBytecodeGenerator`. +/// + +/// `StackObject` is an enum representing the different types of objects that can be placed on the stack during the conversion process. +/// It includes EnumValue, IrIdentifier, Instruction, VariableDeclaration, FunctionBody, and FunctionBlock. #[derive(Debug, Clone)] enum StackObject { + /// Represents an EnumValue object on the stack. EnumValue(EnumValue), + /// Represents an IrIdentifier object on the stack. IrIdentifier(IrIdentifier), + + /// Represents an Instruction object on the stack. Instruction(Box), + /// Represents a VariableDeclaration object on the stack. VariableDeclaration(VariableDeclaration), + + /// Represents a FunctionBody object on the stack. FunctionBody(Box), + + /// Represents a FunctionBlock object on the stack. FunctionBlock(Box), } +/// The `IrEmitter` struct is used for bookkeeping during the conversion of a Scilla AST to an intermediate representation. +/// It implements the `AstConverting` trait, which is a generic trait for AST conversions. pub struct IrEmitter { + /// Stack of objects used during the conversion process. stack: Vec, - // Used for transition and procedure + /// Current function block being processed. current_block: Box, - current_body: Box, - // Used for let function declarations - // current_state: Option< Box< ComputableState > >, + /// Current function body being processed. + current_body: Box, - // Other + /// Current namespace being processed. current_namespace: IrIdentifier, + + /// Stack of namespaces used during the conversion process. namespace_stack: Vec, + /// Intermediate representation of the AST. ir: Box, + /// Source positions of the AST nodes. source_positions: Vec<(SourcePosition, SourcePosition)>, } impl IrEmitter { - pub fn new() -> Self { + pub fn new(symbol_table: SymbolTable) -> Self { let current_block = FunctionBlock::new("dummy".to_string()); let current_body = FunctionBody::new(); let ns = IrIdentifier { @@ -62,7 +113,7 @@ impl IrEmitter { current_namespace: ns.clone(), namespace_stack: [ns].to_vec(), /// current_function: None, - ir: Box::new(IntermediateRepresentation::new()), + ir: Box::new(IntermediateRepresentation::new(symbol_table)), source_positions: [( SourcePosition::invalid_position(), SourcePosition::invalid_position(), @@ -213,6 +264,10 @@ impl IrEmitter { } pub fn emit(&mut self, node: &NodeProgram) -> Result, String> { + // Copying original symbol table to create a new instance of the IR at the end + // of traversing + let symbol_table = self.ir.symbol_table.clone(); + let result = node.visit(self); match result { Err(m) => panic!("{}", m), @@ -224,7 +279,7 @@ impl IrEmitter { // Annotating symbols with types // Returning - let mut ret = Box::new(IntermediateRepresentation::new()); + let mut ret = Box::new(IntermediateRepresentation::new(symbol_table)); mem::swap(&mut self.ir, &mut ret); Ok(ret) @@ -466,7 +521,7 @@ impl AstConverting for IrEmitter { ) -> Result { match node { NodeFullExpression::LocalVariableDeclaration { - identifier_name, + identifier_name: _, expression, type_annotation: _, containing_expression, @@ -539,152 +594,158 @@ impl AstConverting for IrEmitter { unimplemented!(); } NodeFullExpression::Match { - match_expression, - clauses, + match_expression: _, + clauses: _, } => { - let _ = match_expression.visit(self)?; - let expression = self.pop_instruction()?; - let source_location = expression.source_location.clone(); - - let main_expression_symbol = self.convert_instruction_to_symbol(expression); - - let finally_exit_label = self - .ir - .symbol_table - .name_generator - .new_block_label("match_finally"); - - // Checking for catch all - let mut catch_all: Option<&NodePatternMatchExpressionClause> = None; - for clause in clauses.iter() { - match clause.node.pattern.node { - NodePattern::Wildcard => { - catch_all = Some(&clause.node); - break; - } - _ => {} - } - } - - let mut phi_results: Vec = Vec::new(); - - for clause in clauses.iter() { - clause.node.pattern.visit(self)?; - - // Creating compare instruction - // TODO: Pop instruction or symbol - let expected_value = self.pop_ir_identifier()?; - assert!(expected_value.kind == IrIndentifierKind::Unknown); - - let source_location = expected_value.source_location.clone(); - - let compare_instr = Box::new(Instruction { - ssa_name: None, - result_type: None, - operation: Operation::IsEqual { - left: main_expression_symbol.clone(), - right: expected_value, - }, - source_location: source_location.clone(), - }); - let case = self.convert_instruction_to_symbol(compare_instr); - - // Blocks for success and fail - let fail_label = self - .ir - .symbol_table - .name_generator - .new_block_label("match_fail"); - - let success_label = self - .ir - .symbol_table - .name_generator - .new_block_label("match_success"); - let mut success_block = FunctionBlock::new_from_symbol(success_label.clone()); - - // Terminating current block - let op = Operation::ConditionalJump { - expression: case, - on_success: success_label, - on_failure: fail_label.clone(), - }; - self.current_block - .instructions - .push_back(Box::new(Instruction { - ssa_name: None, - result_type: None, - operation: op, - source_location, - })); - self.current_block.terminated = true; - - // Finishing current_block and moving it onto - // to the current body while preparing the success block - // as current - mem::swap(&mut success_block, &mut self.current_block); - self.current_body.blocks.push(success_block); - - let _ = clause.node.expression.visit(self)?; - let expr_instr = self.pop_instruction()?; - let source_location = expr_instr.source_location.clone(); - - let result_sym = self.convert_instruction_to_symbol(expr_instr); - phi_results.push(result_sym); - - let exit_instruction = Box::new(Instruction { - ssa_name: None, - result_type: None, - operation: Operation::Jump(finally_exit_label.clone()), - source_location, - }); - self.current_block.instructions.push_back(exit_instruction); - - // Pushing sucess block and creating fail block + unimplemented!(); + } /* TODO: { - let mut fail_block = FunctionBlock::new_from_symbol(fail_label.clone()); - mem::swap(&mut fail_block, &mut self.current_block); - self.current_body.blocks.push(fail_block); + info!("Match statement"); + let _ = match_expression.visit(self)?; + let expression = self.pop_instruction()?; + let source_location = expression.source_location.clone(); - // let fail_label = self.ir.symbol_table.name_generator.new_block_label("match_case"); - // let fail_block = FunctionBlock::new_from_symbol(fail_label); - } + let main_expression_symbol = self.convert_instruction_to_symbol(expression); - // Currently in the last fail block - // TODO: Catch all if needed + let finally_exit_label = self + .ir + .symbol_table + .name_generator + .new_block_label("match_finally"); - let exit_instruction = Box::new(Instruction { - ssa_name: None, - result_type: None, - operation: Operation::Jump(finally_exit_label.clone()), - source_location: source_location.clone(), - }); - self.current_block.instructions.push_back(exit_instruction); + let mut phi_results: Vec = Vec::new(); - if let Some(_) = catch_all { - unimplemented!(); - } + for clause in clauses.iter() { + info!("Next clause"); + let fail_label = self + .ir + .symbol_table + .name_generator + .new_block_label("match_fail"); + todo!("Catch all is untested."); // - // Attaching exit block - let mut finally_exit_block = - FunctionBlock::new_from_symbol(finally_exit_label.clone()); - mem::swap(&mut finally_exit_block, &mut self.current_block); - self.current_body.blocks.push(finally_exit_block); - - self.stack - .push(StackObject::Instruction(Box::new(Instruction { - ssa_name: None, - result_type: None, - operation: Operation::PhiNode(phi_results), - source_location: source_location.clone(), - }))); - // unimplemented!(); + match &clause.node.pattern.node { + NodePattern::Wildcard => { + info!("Dealing with wildcard"); + // Doing nothing as we will just write the instructions to the current block + } + NodePattern::Binder(_) => { + unimplemented!() } + NodePattern::Constructor(name, args) => { + info!("Setting {} up", name); + clause.node.pattern.visit(self)?; + + // Creating compare instruction + // TODO: Pop instruction or symbol + let expected_value = self.pop_ir_identifier()?; + assert!(expected_value.kind == IrIndentifierKind::Unknown); + + let source_location = expected_value.source_location.clone(); + + let compare_instr = Box::new(Instruction { + ssa_name: None, + result_type: None, + operation: Operation::IsEqual { + left: main_expression_symbol.clone(), + right: expected_value, + }, + source_location: source_location.clone(), + }); + let case = self.convert_instruction_to_symbol(compare_instr); + + // Blocks for success + + let success_label = self + .ir + .symbol_table + .name_generator + .new_block_label("match_success"); + let mut success_block = + FunctionBlock::new_from_symbol(success_label.clone()); + + // Terminating current block + let op = Operation::ConditionalJump { + expression: case, + on_success: success_label, + on_failure: fail_label.clone(), + }; + self.current_block + .instructions + .push_back(Box::new(Instruction { + ssa_name: None, + result_type: None, + operation: op, + source_location, + })); + + // Finishing current_block and moving it onto + // to the current body while preparing the success block + // as current + mem::swap(&mut success_block, &mut self.current_block); + self.current_body.blocks.push(success_block); + } + } + + let _ = clause.node.expression.visit(self)?; + let expr_instr = self.pop_instruction()?; + let source_location = expr_instr.source_location.clone(); + + let result_sym = self.convert_instruction_to_symbol(expr_instr); + phi_results.push(result_sym); + + let exit_instruction = Box::new(Instruction { + ssa_name: None, + result_type: None, + operation: Operation::Jump(finally_exit_label.clone()), + source_location, + }); + self.current_block.instructions.push_back(exit_instruction); + self.current_block.terminated = true; + // Pushing sucess block and creating fail block + + let mut fail_block = FunctionBlock::new_from_symbol(fail_label.clone()); + mem::swap(&mut fail_block, &mut self.current_block); + self.current_body.blocks.push(fail_block); + + // let fail_label = self.ir.symbol_table.name_generator.new_block_label("match_case"); + // let fail_block = FunctionBlock::new_from_symbol(fail_label); + } + + // TODO: Catch all if needed + + // Exiting + let exit_instruction = Box::new(Instruction { + ssa_name: None, + result_type: None, + operation: Operation::Jump(finally_exit_label.clone()), + source_location: source_location.clone(), + }); + self.current_block.instructions.push_back(exit_instruction); + + // Attaching exit block + let mut finally_exit_block = + FunctionBlock::new_from_symbol(finally_exit_label.clone()); + mem::swap(&mut finally_exit_block, &mut self.current_block); + self.current_body.blocks.push(finally_exit_block); + + self.stack + .push(StackObject::Instruction(Box::new(Instruction { + ssa_name: None, + result_type: None, + operation: Operation::PhiNode(phi_results), + source_location: source_location.clone(), + }))); + // unimplemented!(); + } + */ NodeFullExpression::ConstructorCall { identifier_name, contract_type_arguments, argument_list, } => { + self.push_source_position(&identifier_name.start, &identifier_name.end); + let _ = identifier_name.visit(self)?; // Expecting function name symbol @@ -706,13 +767,14 @@ impl AstConverting for IrEmitter { owner: None, // We cannot deduce the type from the AST arguments, }; + let instr = Box::new(Instruction { ssa_name: None, result_type: None, operation, source_location: self.current_location(), }); - + self.pop_source_position(); self.stack.push(StackObject::Instruction(instr)); } NodeFullExpression::TemplateFunction { @@ -784,8 +846,19 @@ impl AstConverting for IrEmitter { }); self.stack.push(StackObject::Instruction(instr)); } - NodeValueLiteral::LiteralHex(_value) => { - unimplemented!(); + NodeValueLiteral::LiteralHex(value) => { + let typename = self.ir.symbol_table.name_generator.hex_type(); + let operation = Operation::Literal { + data: value.to_string(), + typename, + }; + let instr = Box::new(Instruction { + ssa_name: None, + result_type: None, + operation, + source_location: self.current_location(), + }); + self.stack.push(StackObject::Instruction(instr)); } NodeValueLiteral::LiteralString(value) => { let typename = self.ir.symbol_table.name_generator.string_type(); @@ -821,7 +894,8 @@ impl AstConverting for IrEmitter { ) -> Result { match &node { NodePattern::Wildcard => { - unimplemented!() + info!("Visiting wildcard!"); + // Wild card does not change anything } NodePattern::Binder(_name) => { unimplemented!() @@ -1002,12 +1076,28 @@ impl AstConverting for IrEmitter { } => { unimplemented!() } - NodeStatement::Accept => Some(Box::new(Instruction { - ssa_name: None, - result_type: None, - operation: Operation::AcceptTransfer, - source_location: self.current_location(), - })), + NodeStatement::Accept => { + let arguments: Vec = [].to_vec(); + let name = IrIdentifier { + unresolved: "__intrinsic_accept_transfer".to_string(), // TODO: Register somewhere globally + resolved: None, + type_reference: None, + kind: IrIndentifierKind::ProcedureName, + is_definition: false, + source_location: self.current_location(), + }; + + let operation = Operation::CallFunction { name, arguments }; + // TODO: Location from component_id + let instr = Box::new(Instruction { + ssa_name: None, + result_type: None, + operation, + source_location: self.current_location(), + }); + + Some(instr) + } NodeStatement::Send { identifier_name: _ } => { unimplemented!() } @@ -1034,7 +1124,7 @@ impl AstConverting for IrEmitter { ssa_name: None, result_type: None, operation: Operation::Jump(match_exit.clone()), - source_location, + source_location: source_location.clone(), }); self.current_block.instructions.push_back(jump); @@ -1051,6 +1141,14 @@ impl AstConverting for IrEmitter { .name_generator .new_block_label(&format!("clause_{}_block", i)); + let next_jump_label = match &clause.node.pattern_expression.node { + NodePattern::Wildcard => label_block.clone(), + NodePattern::Binder(_) => { + unimplemented!() + } + NodePattern::Constructor(_, _) => label_condition.clone(), + }; + let last_instruction = &mut self.current_block.instructions.back_mut().unwrap(); match &mut last_instruction.operation { @@ -1062,45 +1160,56 @@ impl AstConverting for IrEmitter { on_success: _, ref mut on_failure, } => { - *on_failure = label_condition.clone(); + *on_failure = next_jump_label; } _ => { panic!("Expected previous block to be a terminating jump."); } } - // Instating condition checking block as self.current_block - let mut clause_condition_block = - FunctionBlock::new_from_symbol(label_condition); - mem::swap(&mut clause_condition_block, &mut self.current_block); - self.current_body.blocks.push(clause_condition_block); - - clause.node.pattern_expression.visit(self)?; - let expected_value = self.pop_ir_identifier()?; - assert!(expected_value.kind == IrIndentifierKind::Unknown); - let source_location = expected_value.source_location.clone(); - - let jump_condition = Box::new(Instruction { - ssa_name: None, - result_type: None, - operation: Operation::IsEqual { - left: main_expression_symbol.clone(), - right: expected_value, - }, - source_location: source_location.clone(), - }); - - let jump_if = Box::new(Instruction { - ssa_name: None, - result_type: None, - operation: Operation::ConditionalJump { - expression: self.convert_instruction_to_symbol(jump_condition), - on_success: label_block.clone(), - on_failure: match_exit.clone(), // Exit or Placeholder - will be overwritten in next cycle - }, - source_location: source_location.clone(), - }); - self.current_block.instructions.push_back(jump_if); + match &clause.node.pattern_expression.node { + NodePattern::Wildcard => { + // In the event of a wildcard, we jump right to the clause block. + // TODO: Check that the wildcard is last block in the match statement. + } + NodePattern::Binder(_) => { + unimplemented!() + } + NodePattern::Constructor(_, _) => { + // Instating condition checking block as self.current_block + let mut clause_condition_block = + FunctionBlock::new_from_symbol(label_condition); + mem::swap(&mut clause_condition_block, &mut self.current_block); + self.current_body.blocks.push(clause_condition_block); + + clause.node.pattern_expression.visit(self)?; + let expected_value = self.pop_ir_identifier()?; + assert!(expected_value.kind == IrIndentifierKind::Unknown); + let source_location = expected_value.source_location.clone(); + + let jump_condition = Box::new(Instruction { + ssa_name: None, + result_type: None, + operation: Operation::IsEqual { + left: main_expression_symbol.clone(), + right: expected_value, + }, + source_location: source_location.clone(), + }); + + let jump_if = Box::new(Instruction { + ssa_name: None, + result_type: None, + operation: Operation::ConditionalJump { + expression: self.convert_instruction_to_symbol(jump_condition), + on_success: label_block.clone(), + on_failure: match_exit.clone(), // Exit or Placeholder - will be overwritten in next cycle + }, + source_location: source_location.clone(), + }); + self.current_block.instructions.push_back(jump_if); + } + }; let mut clause_block = match &clause.node.statement_block { Some(statement_block) => { @@ -1110,7 +1219,8 @@ impl AstConverting for IrEmitter { } None => FunctionBlock::new("empty_block".to_string()), }; - + // TODO: Get source location properly + let source_location = source_location.clone(); clause_block.name = label_block.clone(); let terminator_instr = Box::new(Instruction { @@ -1134,8 +1244,9 @@ impl AstConverting for IrEmitter { component_id, arguments: call_args, } => { + self.push_source_position(&component_id.start, &component_id.end); + let mut arguments: Vec = [].to_vec(); - warn!("Emitter call '{:?}' with {:#?}", component_id, call_args); for arg in call_args.iter() { // TODO: xs should be rename .... not clear what this is, but it means function arguments let _ = arg.visit(self)?; @@ -1160,7 +1271,7 @@ impl AstConverting for IrEmitter { }; let operation = Operation::CallFunction { name, arguments }; - + // TODO: Location from component_id let instr = Box::new(Instruction { ssa_name: None, result_type: None, @@ -1168,6 +1279,7 @@ impl AstConverting for IrEmitter { source_location: self.current_location(), }); + self.pop_source_position(); // self.stack.push(StackObject::Instruction(instr)); Some(instr) } diff --git a/products/bluebell/core/src/intermediate_representation/name_generator.rs b/products/bluebell/core/src/intermediate_representation/name_generator.rs index 447e94c56..bee127930 100644 --- a/products/bluebell/core/src/intermediate_representation/name_generator.rs +++ b/products/bluebell/core/src/intermediate_representation/name_generator.rs @@ -1,7 +1,7 @@ -use crate::intermediate_representation::primitives::{ - FunctionBlock, IrIdentifier, IrIndentifierKind, +use crate::{ + intermediate_representation::primitives::{FunctionBlock, IrIdentifier, IrIndentifierKind}, + parser::lexer::SourcePosition, }; -use crate::parser::lexer::SourcePosition; #[derive(Debug, Clone)] pub struct NameGenerator { @@ -34,6 +34,20 @@ impl NameGenerator { } } + pub fn hex_type(&self) -> IrIdentifier { + IrIdentifier { + unresolved: "String".to_string(), // TODO: Correct structure would be Dynamic Byte String, see https://scilla-cookbook.org/recipes/scilla-recipes/addresses + resolved: None, + type_reference: None, + kind: IrIndentifierKind::TypeName, + is_definition: false, + source_location: ( + SourcePosition::invalid_position(), + SourcePosition::invalid_position(), + ), + } + } + pub fn generate_anonymous_type_id(&mut self, prefix: String) -> IrIdentifier { let n = self.anonymous_type_number; self.anonymous_type_number += 1; diff --git a/products/bluebell/core/src/intermediate_representation/pass.rs b/products/bluebell/core/src/intermediate_representation/pass.rs index 2386ee4bb..802373cbd 100644 --- a/products/bluebell/core/src/intermediate_representation/pass.rs +++ b/products/bluebell/core/src/intermediate_representation/pass.rs @@ -1,32 +1,44 @@ -use crate::constants::{TraversalResult, TreeTraversalMode}; -use crate::intermediate_representation::primitives::*; -use crate::intermediate_representation::symbol_table::SymbolTable; +use crate::{ + constants::{TraversalResult, TreeTraversalMode}, + intermediate_representation::{primitives::*, symbol_table::SymbolTable}, +}; +/// `IrPass` is an abstract pass that is used by the `PassManager` to manipulate the Intermediate Representation (IR). +/// It provides methods to visit and potentially alter different parts of the IR during the traversal. pub trait IrPass { + /// Visit and potentially alter a symbol kind in the IR. fn visit_symbol_kind( &mut self, mode: TreeTraversalMode, symbol_kind: &mut IrIndentifierKind, symbol_table: &mut SymbolTable, ) -> Result; + + /// Visit and potentially alter a symbol name in the IR. fn visit_symbol_name( &mut self, mode: TreeTraversalMode, symbol_name: &mut IrIdentifier, symbol_table: &mut SymbolTable, ) -> Result; + + /// Visit and potentially alter an enum value in the IR. fn visit_enum_value( &mut self, mode: TreeTraversalMode, enum_value: &mut EnumValue, symbol_table: &mut SymbolTable, ) -> Result; + + /// Visit and potentially alter a tuple in the IR. fn visit_tuple( &mut self, mode: TreeTraversalMode, tuple: &mut Tuple, symbol_table: &mut SymbolTable, ) -> Result; + + /// Visit and potentially alter a variant in the IR. fn visit_variant( &mut self, mode: TreeTraversalMode, @@ -34,54 +46,71 @@ pub trait IrPass { symbol_table: &mut SymbolTable, ) -> Result; + /// Visit and potentially alter a variable declaration in the IR. fn visit_variable_declaration( &mut self, mode: TreeTraversalMode, var_dec: &mut VariableDeclaration, symbol_table: &mut SymbolTable, ) -> Result; + + /// Visit and potentially alter an operation in the IR. fn visit_operation( &mut self, mode: TreeTraversalMode, operation: &mut Operation, symbol_table: &mut SymbolTable, ) -> Result; + + /// Visit and potentially alter an instruction in the IR. fn visit_instruction( &mut self, mode: TreeTraversalMode, instruction: &mut Instruction, symbol_table: &mut SymbolTable, ) -> Result; + + /// Visit and potentially alter a function block in the IR. fn visit_function_block( &mut self, mode: TreeTraversalMode, function_block: &mut FunctionBlock, symbol_table: &mut SymbolTable, ) -> Result; + + /// Visit and potentially alter a function body in the IR. fn visit_function_body( &mut self, mode: TreeTraversalMode, function_body: &mut FunctionBody, symbol_table: &mut SymbolTable, ) -> Result; + + /// Visit and potentially alter a concrete type in the IR. fn visit_concrete_type( &mut self, mode: TreeTraversalMode, con_type: &mut ConcreteType, symbol_table: &mut SymbolTable, ) -> Result; + + /// Visit and potentially alter a contract field in the IR. fn visit_contract_field( &mut self, mode: TreeTraversalMode, function_kind: &mut ContractField, symbol_table: &mut SymbolTable, ) -> Result; + + /// Visit and potentially alter a function kind in the IR. fn visit_function_kind( &mut self, mode: TreeTraversalMode, function_kind: &mut FunctionKind, symbol_table: &mut SymbolTable, ) -> Result; + + /// Visit and potentially alter a concrete function in the IR. fn visit_concrete_function( &mut self, mode: TreeTraversalMode, @@ -89,12 +118,15 @@ pub trait IrPass { symbol_table: &mut SymbolTable, ) -> Result; + /// Visit and potentially alter a case clause in the IR. fn visit_case_clause( &mut self, mode: TreeTraversalMode, con_function: &mut CaseClause, symbol_table: &mut SymbolTable, ) -> Result; + + /// Visit and potentially alter primitives in the IR. fn visit_primitives( // TODO Remove &mut self, @@ -103,6 +135,9 @@ pub trait IrPass { symbol_table: &mut SymbolTable, ) -> Result; + /// Initiate the pass. fn initiate(&mut self); + + /// Finalize the pass. fn finalize(&mut self); } diff --git a/products/bluebell/core/src/intermediate_representation/pass_executor.rs b/products/bluebell/core/src/intermediate_representation/pass_executor.rs index 76adab9ba..8af941ee9 100644 --- a/products/bluebell/core/src/intermediate_representation/pass_executor.rs +++ b/products/bluebell/core/src/intermediate_representation/pass_executor.rs @@ -1,8 +1,16 @@ -use crate::constants::{TraversalResult, TreeTraversalMode}; -use crate::intermediate_representation::pass::IrPass; -use crate::intermediate_representation::primitives::*; -use crate::intermediate_representation::symbol_table::SymbolTable; - +use crate::{ + constants::{TraversalResult, TreeTraversalMode}, + intermediate_representation::{pass::IrPass, primitives::*, symbol_table::SymbolTable}, +}; + +/// `PassExecutor` is a trait that provides a method for visiting and altering +/// the Intermediate Representation (IR) primitives. It is used by the `PassManager` +/// to traverse the IR and apply transformations. +/// +/// The `visit` method takes a mutable reference to the `IrPass` and the `SymbolTable`. +/// It returns a `Result` with the `TraversalResult` and a `String` in case of an error. +/// The traversult result determines how the algorithm proceeds in visiting subsequent nodes +/// in the IR. pub trait PassExecutor { fn visit( &mut self, @@ -11,6 +19,33 @@ pub trait PassExecutor { ) -> Result; } +/// The `IntermediateRepresentation` struct represents the Scilla Intermediate Representation (IR). +/// It contains definitions for types, contract fields, and functions. +/// +/// The `run_pass` method is used to run a pass on the IR. It takes a mutable reference to the `IrPass` +/// and initiates the pass, visits each type definition, contract field, and function definition in the IR, +/// and finalizes the pass. It returns a `Result` with the `TraversalResult` and a `String` in case of an error. +impl IntermediateRepresentation { + pub fn run_pass(&mut self, pass: &mut dyn IrPass) -> Result { + pass.initiate(); + + for type_def in &mut self.type_definitions { + type_def.visit(pass, &mut self.symbol_table)?; + } + + for contract_field in &mut self.fields_definitions { + contract_field.visit(pass, &mut self.symbol_table)?; + } + + for function_def in &mut self.function_definitions { + function_def.visit(pass, &mut self.symbol_table)?; + } + + pass.finalize(); + Ok(TraversalResult::Continue) + } +} + impl PassExecutor for IrIndentifierKind { fn visit( &mut self, @@ -164,12 +199,6 @@ impl PassExecutor for Operation { let children_ret = if let Ok(TraversalResult::Continue) = ret { match self { Operation::TerminatingRef(identifier) => identifier.visit(pass, symbol_table), - Operation::Switch { cases, on_default } => { - for case in cases.iter_mut() { - case.visit(pass, symbol_table)?; - } - on_default.visit(pass, symbol_table) - } Operation::Jump(identifier) => identifier.visit(pass, symbol_table), Operation::StateStore { address, value } => { let ret = value.visit(pass, symbol_table); @@ -181,10 +210,9 @@ impl PassExecutor for Operation { //let ret = value.visit(pass, symbol_table); address.name.visit(pass, symbol_table) } - Operation::MemLoad - | Operation::MemStore - | Operation::AcceptTransfer - | Operation::PhiNode(_) => Ok(TraversalResult::Continue), + Operation::MemLoad | Operation::MemStore | Operation::PhiNode(_) => { + Ok(TraversalResult::Continue) + } Operation::ConditionalJump { expression, on_success, @@ -481,24 +509,3 @@ impl PassExecutor for CaseClause { } } } - -impl IntermediateRepresentation { - pub fn run_pass(&mut self, pass: &mut dyn IrPass) -> Result { - pass.initiate(); - - for type_def in &mut self.type_definitions { - type_def.visit(pass, &mut self.symbol_table)?; - } - - for contract_field in &mut self.fields_definitions { - contract_field.visit(pass, &mut self.symbol_table)?; - } - - for function_def in &mut self.function_definitions { - function_def.visit(pass, &mut self.symbol_table)?; - } - - pass.finalize(); - Ok(TraversalResult::Continue) - } -} diff --git a/products/bluebell/core/src/intermediate_representation/pass_manager.rs b/products/bluebell/core/src/intermediate_representation/pass_manager.rs index 994654bf5..e72056082 100644 --- a/products/bluebell/core/src/intermediate_representation/pass_manager.rs +++ b/products/bluebell/core/src/intermediate_representation/pass_manager.rs @@ -1,11 +1,12 @@ -use crate::intermediate_representation::pass::IrPass; -use crate::intermediate_representation::primitives::IntermediateRepresentation; -use crate::passes::annotate_base_types::AnnotateBaseTypes; -use crate::passes::balance_block_args::BalanceBlockArguments; -use crate::passes::block_dependencies::DeduceBlockDependencies; -use crate::passes::collect_type_definitions::CollectTypeDefinitionsPass; -use crate::passes::debug_printer::DebugPrinter; -use crate::passes::state_allocator::StateCollector; +use crate::{ + intermediate_representation::{pass::IrPass, primitives::IntermediateRepresentation}, + passes::{ + annotate_base_types::AnnotateBaseTypes, balance_block_args::BalanceBlockArguments, + block_dependencies::DeduceBlockDependencies, + collect_type_definitions::CollectTypeDefinitionsPass, debug_printer::DebugPrinter, + state_allocator::StateCollector, + }, +}; pub struct PassManager { passes: Vec>, diff --git a/products/bluebell/core/src/intermediate_representation/primitives.rs b/products/bluebell/core/src/intermediate_representation/primitives.rs index 75c576f4a..8c7ce5887 100644 --- a/products/bluebell/core/src/intermediate_representation/primitives.rs +++ b/products/bluebell/core/src/intermediate_representation/primitives.rs @@ -1,11 +1,10 @@ -use crate::intermediate_representation::symbol_table::SymbolTable; -use crate::parser::lexer::SourcePosition; +use std::collections::{BTreeSet, HashMap, VecDeque}; -use std::collections::HashMap; - -use std::collections::BTreeSet; -use std::collections::VecDeque; +use crate::{ + intermediate_representation::symbol_table::SymbolTable, parser::lexer::SourcePosition, +}; +/// Enum representing the different kinds of identifiers in the intermediate representation. #[derive(Debug, Clone, PartialEq)] pub enum IrIndentifierKind { FunctionName, @@ -33,6 +32,7 @@ pub enum IrIndentifierKind { Unknown, } +/// Struct representing an identifier in the intermediate representation. #[derive(Debug, Clone, PartialEq)] pub struct IrIdentifier { pub unresolved: String, @@ -44,6 +44,7 @@ pub struct IrIdentifier { } impl IrIdentifier { + /// Constructor for the IrIdentifier struct. pub fn new( unresolved: String, kind: IrIndentifierKind, @@ -59,6 +60,7 @@ impl IrIdentifier { } } + /// Method to get the qualified name of the identifier. pub fn qualified_name(&self) -> Result { // TODO: Change to resolved or throw if let Some(resolved) = &self.resolved { @@ -69,6 +71,7 @@ impl IrIdentifier { } } +/// Struct representing an enum value in the intermediate representation. #[derive(Debug, Clone)] pub struct EnumValue { pub name: IrIdentifier, @@ -78,14 +81,17 @@ pub struct EnumValue { } impl EnumValue { + /// Constructor for the EnumValue struct. pub fn new(name: IrIdentifier, data: Option) -> Self { Self { name, id: 0, data } } + /// Method to set the id of the enum value. pub fn set_id(&mut self, v: u64) { self.id = v } } +/// Struct representing a tuple in the intermediate representation. #[derive(Debug, Clone)] pub struct Tuple { pub fields: Vec, @@ -93,15 +99,18 @@ pub struct Tuple { } impl Tuple { + /// Constructor for the Tuple struct. pub fn new() -> Self { Self { fields: Vec::new() } } + /// Method to add a field to the tuple. pub fn add_field(&mut self, value: IrIdentifier) { self.fields.push(value); } } +/// Struct representing a variant in the intermediate representation. #[derive(Debug, Clone)] pub struct Variant { pub fields: Vec, // (name, id, data) @@ -109,12 +118,12 @@ pub struct Variant { } impl Variant { - // Constructor method for our struct + /// Constructor for the Variant struct. pub fn new() -> Self { Self { fields: Vec::new() } } - // Method to add a field into our Variant struct + /// Method to add a field to the variant. pub fn add_field(&mut self, field: EnumValue) { let id: u64 = match self.fields.last() { // if we have at least one field, use the id of the last field + 1 @@ -128,16 +137,7 @@ impl Variant { } } -/* -#[derive(Debug, Clone, PartialEq)] -pub enum Identifier { - // TODO: Replace with symbol reference - ComponentName(String), - TypeName(String), - Event(String), -} -*/ - +/// Struct representing a variable declaration in the intermediate representation. #[derive(Debug, Clone)] pub struct VariableDeclaration { pub name: IrIdentifier, @@ -147,6 +147,7 @@ pub struct VariableDeclaration { } impl VariableDeclaration { + /// Constructor for the VariableDeclaration struct. pub fn new(name: String, mutable: bool, typename: IrIdentifier) -> Self { Self { name: IrIdentifier { @@ -170,6 +171,7 @@ impl VariableDeclaration { } } +/// Struct representing a field address in the intermediate representation. #[derive(Debug, Clone)] pub struct FieldAddress { pub name: IrIdentifier, @@ -177,6 +179,7 @@ pub struct FieldAddress { // TODO: pub source_location: (SourcePosition,SourcePosition) } +/// Struct representing a case clause in the intermediate representation. #[derive(Debug, Clone)] pub struct CaseClause { pub expression: IrIdentifier, @@ -184,6 +187,7 @@ pub struct CaseClause { // TODO: pub source_location: (SourcePosition,SourcePosition) } +/// Enum representing the different kinds of operations in the intermediate representation. #[derive(Debug, Clone)] pub enum Operation { Noop, @@ -194,11 +198,6 @@ pub enum Operation { on_success: IrIdentifier, on_failure: IrIdentifier, }, - - Switch { - cases: Vec, - on_default: IrIdentifier, - }, MemLoad, MemStore, StateLoad { @@ -240,13 +239,13 @@ pub enum Operation { data: String, typename: IrIdentifier, }, - AcceptTransfer, PhiNode(Vec), Return(Option), Revert(Option), } +/// Struct representing an instruction in the intermediate representation. #[derive(Debug, Clone)] pub struct Instruction { pub ssa_name: Option, @@ -255,6 +254,7 @@ pub struct Instruction { pub source_location: (SourcePosition, SourcePosition), } +/// Struct representing a function block in the intermediate representation. #[derive(Debug, Clone)] pub struct FunctionBlock { pub name: IrIdentifier, @@ -269,10 +269,12 @@ pub struct FunctionBlock { } impl FunctionBlock { + /// Constructor for the FunctionBlock struct. pub fn new(name: String) -> Box { Self::new_from_symbol(Self::new_label(name)) } + /// Method to create a new FunctionBlock from a symbol. pub fn new_from_symbol(name: IrIdentifier) -> Box { Box::new(Self { name, @@ -286,6 +288,7 @@ impl FunctionBlock { }) } + /// Method to create a new label for a FunctionBlock. pub fn new_label(label: String) -> IrIdentifier { IrIdentifier { unresolved: label.clone(), @@ -301,6 +304,7 @@ impl FunctionBlock { } } +/// Struct representing a function body in the intermediate representation. #[derive(Debug, Clone)] pub struct FunctionBody { pub blocks: Vec>, @@ -308,11 +312,13 @@ pub struct FunctionBody { } impl FunctionBody { + /// Constructor for the FunctionBody struct. pub fn new() -> Box { Box::new(Self { blocks: Vec::new() }) } } +/// Enum representing the different kinds of concrete types in the intermediate representation. #[derive(Debug, Clone)] pub enum ConcreteType { Tuple { @@ -327,6 +333,7 @@ pub enum ConcreteType { }, } +/// Enum representing the different kinds of functions in the intermediate representation. #[derive(Debug, Clone)] pub enum FunctionKind { Procedure, @@ -334,6 +341,7 @@ pub enum FunctionKind { Function, } +/// Struct representing a concrete function in the intermediate representation. #[derive(Debug, Clone)] pub struct ConcreteFunction { pub name: IrIdentifier, @@ -344,6 +352,7 @@ pub struct ConcreteFunction { pub body: Box, } +/// Struct representing a lambda function with a single argument in the intermediate representation. #[derive(Debug, Clone)] pub struct LambdaFunctionSingleArgument { pub name: IrIdentifier, @@ -353,6 +362,7 @@ pub struct LambdaFunctionSingleArgument { pub block: FunctionBlock, } +/// Struct representing a contract field in the intermediate representation. #[derive(Debug)] pub struct ContractField { pub namespace: IrIdentifier, @@ -360,6 +370,7 @@ pub struct ContractField { pub initializer: Box, } +/// Struct representing the intermediate representation of a program. #[derive(Debug)] pub struct IntermediateRepresentation { // Program IR @@ -374,20 +385,15 @@ pub struct IntermediateRepresentation { } impl IntermediateRepresentation { - pub fn new() -> Self { + /// Constructor for the IntermediateRepresentation struct. + pub fn new(symbol_table: SymbolTable) -> Self { IntermediateRepresentation { version: "".to_string(), type_definitions: Vec::new(), function_definitions: Vec::new(), fields_definitions: Vec::new(), lambda_functions: Vec::new(), - symbol_table: SymbolTable::new(), + symbol_table, } } } - -pub trait IrLowering { - fn lower_concrete_type(&mut self, con_type: &ConcreteType); - fn lower_concrete_function(&mut self, con_function: &ConcreteFunction); - fn lower(&mut self, primitives: &IntermediateRepresentation); -} diff --git a/products/bluebell/core/src/intermediate_representation/symbol_table.rs b/products/bluebell/core/src/intermediate_representation/symbol_table.rs index 468cb5aaf..2a8110c3f 100644 --- a/products/bluebell/core/src/intermediate_representation/symbol_table.rs +++ b/products/bluebell/core/src/intermediate_representation/symbol_table.rs @@ -1,28 +1,37 @@ -use crate::constants::NAMESPACE_SEPARATOR; -use crate::intermediate_representation::name_generator::NameGenerator; -use primitive_types::U256; use std::collections::HashMap; +use primitive_types::U256; + +use crate::{ + constants::NAMESPACE_SEPARATOR, intermediate_representation::name_generator::NameGenerator, +}; + +/// Struct representing the type information of a symbol. #[derive(Debug, Clone)] pub struct TypeInfo { - pub name: String, + pub symbol_name: String, + pub typename: String, pub return_type: Option, pub arguments: Vec, pub constructor: bool, } +/// Implementation of TypeInfo struct. impl TypeInfo { + /// Checks if the TypeInfo is a function. pub fn is_function(&self) -> bool { match self.return_type { Some(_) => true, None => false, } } + /// Checks if the TypeInfo is a constructor. pub fn is_constructor(&self) -> bool { self.constructor } } +/// Struct representing the state layout entry. #[derive(Debug, Clone)] pub struct StateLayoutEntry { pub address_offset: U256, @@ -30,6 +39,7 @@ pub struct StateLayoutEntry { pub initializer: U256, } +/// Struct representing the symbol table. #[derive(Debug, Clone)] pub struct SymbolTable { pub aliases: HashMap, @@ -38,39 +48,19 @@ pub struct SymbolTable { pub state_layout: HashMap, } -impl SymbolTable { - pub fn new() -> Self { - let type_of_table = HashMap::new(); - - let mut ret = SymbolTable { - aliases: HashMap::new(), - type_of_table, - name_generator: NameGenerator::new(), - state_layout: HashMap::new(), - }; - - // TODO: Get types from RuntimeModule - // TODO: Deal with potential errors - let _ = ret.declare_type("Int8"); - let _ = ret.declare_type("Int16"); - let _ = ret.declare_type("Int32"); - let _ = ret.declare_type("Int64"); - let _ = ret.declare_type("Uint8"); - let _ = ret.declare_type("Uint16"); - let _ = ret.declare_type("Uint32"); - let _ = ret.declare_type("Uint64"); - let _ = ret.declare_type("String"); - let _ = ret.declare_type("ByStr20"); - - let _ = ret.declare_special_variable("_sender", "ByStr20"); - - ret - } +/// Trait for constructing a new symbol table. +pub trait SymbolTableConstructor { + fn new_symbol_table(&self) -> SymbolTable; +} +/// Implementation of SymbolTable struct. +impl SymbolTable { + /// Checks if the given name is a state. pub fn is_state(&self, name: &String) -> bool { self.state_layout.get(name).is_some() } + /// Resolves the qualified name of a symbol. pub fn resolve_qualified_name( &mut self, basename: &String, @@ -118,31 +108,50 @@ impl SymbolTable { None } + /// Returns a mutable reference to the name generator. pub fn get_name_generator(&mut self) -> &mut NameGenerator { &mut self.name_generator } + /// Creates a plain typename. pub fn create_plain_typename(&self, typename: &str) -> Box { Box::new(TypeInfo { - name: typename.to_string(), + symbol_name: "".to_string(), + typename: typename.to_string(), return_type: None, arguments: Vec::new(), constructor: false, }) } - pub fn type_of(&self, name: &str) -> Option> { + /// Returns the type of a symbol. + pub fn type_of(&self, name: &str, namespace: &Option) -> Option> { + if let Some(namespace) = &namespace { + // Split the namespace into parts + let parts: Vec<&str> = namespace.split("::").collect(); + + // Iterate over the parts from most specific to least specific + for i in (0..=parts.len()).rev() { + let qualified_name = format!("{}::{}", parts[0..i].join("::"), name); + if let Some(value) = self.type_of_table.get(&qualified_name) { + return Some(value.clone()); + } + } + } + self.type_of_table.get(name).cloned() } + /// Returns the typename of a symbol. pub fn typename_of(&self, name: &str) -> Option { if let Some(ti) = self.type_of_table.get(name) { - Some(ti.name.clone()) + Some(ti.typename.clone()) } else { None } } + /// Checks if a symbol is a function. pub fn is_function(&self, name: &str) -> bool { if let Some(ti) = self.type_of_table.get(name) { ti.is_function() @@ -151,6 +160,7 @@ impl SymbolTable { } } + /// Declares a function or constructor type. pub fn declare_function_or_constructor_type( &mut self, symbol: &str, @@ -166,7 +176,8 @@ impl SymbolTable { signature.push_str(return_type); let typeinfo = Box::new(TypeInfo { - name: signature.clone(), + symbol_name: symbol.to_string(), + typename: signature.clone(), return_type: Some(return_type.to_string()), arguments: Vec::new(), constructor, @@ -180,6 +191,7 @@ impl SymbolTable { Ok(symbol.to_string()) } + /// Declares a function type. pub fn declare_function_type( &mut self, symbol: &str, @@ -189,6 +201,7 @@ impl SymbolTable { self.declare_function_or_constructor_type(symbol, arguments, return_type, false) } + /// Declares a constructor. pub fn declare_constructor( &mut self, symbol: &str, @@ -198,6 +211,7 @@ impl SymbolTable { self.declare_function_or_constructor_type(symbol, arguments, return_type, true) } + /// Declares the type of a symbol. pub fn declare_type_of(&mut self, symbol: &str, typename: &str) -> Result { let typeinfo = self.create_plain_typename(typename); @@ -206,6 +220,7 @@ impl SymbolTable { Ok(symbol.to_string()) } + /// Declares a special variable. pub fn declare_special_variable( &mut self, name: &str, @@ -214,10 +229,12 @@ impl SymbolTable { self.declare_type_of(name, typename) } + /// Declares a type. pub fn declare_type(&mut self, symbol: &str) -> Result { self.declare_type_of(symbol, symbol) } + /// Declares an alias for a symbol. pub fn declare_alias(&mut self, alias: &str, symbol: &str) -> Result { self.aliases.insert(alias.to_string(), symbol.to_string()); Ok(symbol.to_string()) diff --git a/products/bluebell/core/src/parser/lexer.rs b/products/bluebell/core/src/parser/lexer.rs index 63ed2172e..76fca3d48 100644 --- a/products/bluebell/core/src/parser/lexer.rs +++ b/products/bluebell/core/src/parser/lexer.rs @@ -1,8 +1,6 @@ +use std::{convert::From, iter::Peekable, str::CharIndices, string::String}; + use regex::Regex; -use std::convert::From; -use std::iter::Peekable; -use std::str::CharIndices; -use std::string::String; pub type Spanned = Result<(Loc, Tok, Loc), Error>; diff --git a/products/bluebell/core/src/passes/annotate_base_types.rs b/products/bluebell/core/src/passes/annotate_base_types.rs index 2be343b1d..09d3b27fd 100644 --- a/products/bluebell/core/src/passes/annotate_base_types.rs +++ b/products/bluebell/core/src/passes/annotate_base_types.rs @@ -1,22 +1,20 @@ -use crate::constants::NAMESPACE_SEPARATOR; -use crate::constants::{TraversalResult, TreeTraversalMode}; -use crate::intermediate_representation::pass::IrPass; -use crate::intermediate_representation::pass_executor::PassExecutor; -use crate::intermediate_representation::primitives::CaseClause; -use crate::intermediate_representation::primitives::ContractField; -use crate::intermediate_representation::primitives::Instruction; -use crate::intermediate_representation::primitives::{ - ConcreteFunction, ConcreteType, EnumValue, FunctionBlock, FunctionBody, FunctionKind, - IntermediateRepresentation, IrIdentifier, IrIndentifierKind, Operation, Tuple, - VariableDeclaration, Variant, -}; -use crate::intermediate_representation::symbol_table::SymbolTable; -use crate::intermediate_representation::symbol_table::TypeInfo; -use crate::parser::lexer::SourcePosition; - -use log::info; use std::mem; +use crate::{ + constants::{TraversalResult, TreeTraversalMode, NAMESPACE_SEPARATOR}, + intermediate_representation::{ + pass::IrPass, + pass_executor::PassExecutor, + primitives::{ + CaseClause, ConcreteFunction, ConcreteType, ContractField, EnumValue, FunctionBlock, + FunctionBody, FunctionKind, Instruction, IntermediateRepresentation, IrIdentifier, + IrIndentifierKind, Operation, Tuple, VariableDeclaration, Variant, + }, + symbol_table::{SymbolTable, TypeInfo}, + }, + parser::lexer::SourcePosition, +}; + pub struct AnnotateBaseTypes { previous_namespaces: Vec, namespace: Option, @@ -52,7 +50,7 @@ impl AnnotateBaseTypes { symbol_table: &mut SymbolTable, ) -> Option> { if let Some(name) = &symbol.resolved { - symbol_table.type_of(name) + symbol_table.type_of(name, &self.namespace) } else { None } @@ -225,7 +223,7 @@ impl IrPass for AnnotateBaseTypes { match symbol.kind { IrIndentifierKind::Unknown => { if let Some(typeinfo) = self.type_of(symbol, symbol_table) { - symbol.type_reference = Some(typeinfo.name.clone()); + symbol.type_reference = Some(typeinfo.typename.clone()); // We only move constructors out of line if !typeinfo.is_constructor() { @@ -320,18 +318,7 @@ impl IrPass for AnnotateBaseTypes { symbol_table.resolve_qualified_name(&symbol.unresolved, &self.namespace) { symbol.resolved = Some(resolved_name); - } else { - info!("Not resolved!!"); } - - /* - // In the event of a definition we make a qualified name - if let Some(ns) = &self.namespace { - symbol.resolved = Some( - format!("{}{}{}", ns, NAMESPACE_SEPARATOR, symbol.unresolved).to_string(), - ); - } - */ } } IrIndentifierKind::VirtualRegister @@ -393,18 +380,11 @@ impl IrPass for AnnotateBaseTypes { ) -> Result { // TODO: These types should be stored somewhere (in the symbol table maybe?) let typename = match &mut instr.operation { - Operation::TerminatingRef(identifier) => { + Operation::TerminatingRef(_identifier) => { "Void".to_string() // TODO: Fetch from somewhere } Operation::Noop => "Void".to_string(), // TODO: Fetch from somewhere Operation::Jump(_) => "Void".to_string(), // TODO: Fetch from somewhere - Operation::Switch { cases, on_default } => { - for case in cases.iter_mut() { - case.visit(self, symbol_table)?; - } - on_default.visit(self, symbol_table)?; - "TODO".to_string() - } Operation::ConditionalJump { expression, on_success, @@ -521,13 +501,34 @@ impl IrPass for AnnotateBaseTypes { }; let function_type = format!("{}::<{}>", name_value, argument_type_args).to_string(); + + let function_type = if let Some(typeinfo)= symbol_table.type_of(&function_type, &self.namespace) { + typeinfo.symbol_name + } else { + panic!("Unable to find symbol {}", function_type); + }; + name.resolved = Some(function_type.clone()); + // The value of the SSA is the return type of the function // TODO: To this end we need to resolve the type refernce from the resolved name name.type_reference = Some(function_type.clone()); // TODO: Should contain return type as this is a function pointer - "TODO-lookup".to_string() + let type_info = match symbol_table.type_of(&function_type, &self.namespace) { + Some(v) => { + match v.return_type { + Some(r) => r, + None => "Void".to_string() // TODO: Get value from somewhere + } + } + None => { + println!("{:#?}", symbol_table); + panic!("Undeclared function {}", function_type) + } + }; + + type_info } Operation::CallStaticFunction { name, @@ -540,7 +541,7 @@ impl IrPass for AnnotateBaseTypes { } let return_type = if let Some(function_type) = &name.type_reference { - let function_typeinfo = symbol_table.type_of(function_type); + let function_typeinfo = symbol_table.type_of(function_type, &self.namespace); if let Some(function_typeinfo) = function_typeinfo { function_typeinfo.return_type.expect("").clone() @@ -549,7 +550,6 @@ impl IrPass for AnnotateBaseTypes { .to_string()); } } else { - println!("Extra detail: {:#?}, {:#?}", name, arguments); return Err(format!( "Unable to determine return type of {:?}", name.unresolved @@ -569,7 +569,7 @@ impl IrPass for AnnotateBaseTypes { arg.visit(self, symbol_table)?; } - "TODO-lookup".to_string() + unimplemented!() } Operation::ResolveSymbol { symbol } => { symbol.visit(self, symbol_table)?; @@ -608,7 +608,6 @@ impl IrPass for AnnotateBaseTypes { } } } - Operation::AcceptTransfer => "Void".to_string(), // TODO: Fetch from somewhere Operation::PhiNode(inputs) => { let mut type_name = None; for input in inputs.iter_mut() { diff --git a/products/bluebell/core/src/passes/balance_block_args.rs b/products/bluebell/core/src/passes/balance_block_args.rs index d41283aa8..ebcd1c3ed 100644 --- a/products/bluebell/core/src/passes/balance_block_args.rs +++ b/products/bluebell/core/src/passes/balance_block_args.rs @@ -1,24 +1,21 @@ -use crate::parser::lexer::SourcePosition; - -use crate::constants::{TraversalResult, TreeTraversalMode}; -use crate::intermediate_representation::pass::IrPass; -use std::collections::BTreeSet; - -use crate::intermediate_representation::primitives::ConcreteFunction; -use crate::intermediate_representation::primitives::IrIdentifier; -use crate::intermediate_representation::primitives::IrIndentifierKind; -use std::collections::HashMap; +use std::{ + collections::{BTreeSet, HashMap, VecDeque}, + mem, +}; -use crate::intermediate_representation::primitives::CaseClause; -use crate::intermediate_representation::primitives::ContractField; -use crate::intermediate_representation::primitives::Instruction; -use crate::intermediate_representation::primitives::{ - ConcreteType, EnumValue, FunctionBlock, FunctionBody, FunctionKind, IntermediateRepresentation, - Operation, Tuple, VariableDeclaration, Variant, +use crate::{ + constants::{TraversalResult, TreeTraversalMode}, + intermediate_representation::{ + pass::IrPass, + primitives::{ + CaseClause, ConcreteFunction, ConcreteType, ContractField, EnumValue, FunctionBlock, + FunctionBody, FunctionKind, Instruction, IntermediateRepresentation, IrIdentifier, + IrIndentifierKind, Operation, Tuple, VariableDeclaration, Variant, + }, + symbol_table::SymbolTable, + }, + parser::lexer::SourcePosition, }; -use crate::intermediate_representation::symbol_table::SymbolTable; -use std::collections::VecDeque; -use std::mem; pub struct BalanceBlockArguments { blocks: HashMap, diff --git a/products/bluebell/core/src/passes/block_dependencies.rs b/products/bluebell/core/src/passes/block_dependencies.rs index 740651e57..e5e1e5548 100644 --- a/products/bluebell/core/src/passes/block_dependencies.rs +++ b/products/bluebell/core/src/passes/block_dependencies.rs @@ -1,23 +1,21 @@ -use crate::constants::{TraversalResult, TreeTraversalMode}; -use crate::intermediate_representation::pass::IrPass; -use crate::intermediate_representation::pass_executor::PassExecutor; -use crate::intermediate_representation::primitives::ConcreteFunction; -use crate::intermediate_representation::primitives::IrIdentifier; -use crate::intermediate_representation::primitives::IrIndentifierKind; - -use std::collections::BTreeSet; -use std::collections::HashMap; - -use crate::intermediate_representation::primitives::CaseClause; -use crate::intermediate_representation::primitives::ContractField; -use crate::intermediate_representation::primitives::Instruction; -use crate::intermediate_representation::primitives::{ - ConcreteType, EnumValue, FunctionBlock, FunctionBody, FunctionKind, IntermediateRepresentation, - Operation, Tuple, VariableDeclaration, Variant, +use std::{ + collections::{BTreeSet, HashMap, VecDeque}, + mem, +}; + +use crate::{ + constants::{TraversalResult, TreeTraversalMode}, + intermediate_representation::{ + pass::IrPass, + pass_executor::PassExecutor, + primitives::{ + CaseClause, ConcreteFunction, ConcreteType, ContractField, EnumValue, FunctionBlock, + FunctionBody, FunctionKind, Instruction, IntermediateRepresentation, IrIdentifier, + IrIndentifierKind, Operation, Tuple, VariableDeclaration, Variant, + }, + symbol_table::SymbolTable, + }, }; -use crate::intermediate_representation::symbol_table::SymbolTable; -use std::collections::VecDeque; -use std::mem; pub struct DeduceBlockDependencies { blocks: HashMap, @@ -249,7 +247,7 @@ impl IrPass for DeduceBlockDependencies { fn visit_function_body( &mut self, mode: TreeTraversalMode, - function_body: &mut FunctionBody, + _function_body: &mut FunctionBody, _symbol_table: &mut SymbolTable, ) -> Result { match mode { diff --git a/products/bluebell/core/src/passes/collect_type_definitions.rs b/products/bluebell/core/src/passes/collect_type_definitions.rs index 9ce832aaf..9455d874f 100644 --- a/products/bluebell/core/src/passes/collect_type_definitions.rs +++ b/products/bluebell/core/src/passes/collect_type_definitions.rs @@ -1,17 +1,16 @@ -use crate::constants::NAMESPACE_SEPARATOR; -use crate::constants::{TraversalResult, TreeTraversalMode}; -use crate::intermediate_representation::pass::IrPass; -use crate::intermediate_representation::pass_executor::PassExecutor; -use crate::intermediate_representation::primitives::CaseClause; -use crate::intermediate_representation::primitives::ContractField; -use crate::intermediate_representation::primitives::Instruction; -use crate::intermediate_representation::primitives::{ - ConcreteFunction, ConcreteType, EnumValue, FunctionBlock, FunctionBody, FunctionKind, - IntermediateRepresentation, IrIdentifier, IrIndentifierKind, Operation, Tuple, - VariableDeclaration, Variant, +use crate::{ + constants::{TraversalResult, TreeTraversalMode, NAMESPACE_SEPARATOR}, + intermediate_representation::{ + pass::IrPass, + pass_executor::PassExecutor, + primitives::{ + CaseClause, ConcreteFunction, ConcreteType, ContractField, EnumValue, FunctionBlock, + FunctionBody, FunctionKind, Instruction, IntermediateRepresentation, IrIdentifier, + IrIndentifierKind, Operation, Tuple, VariableDeclaration, Variant, + }, + symbol_table::SymbolTable, + }, }; -use crate::intermediate_representation::symbol_table::SymbolTable; -use log::info; pub struct CollectTypeDefinitionsPass { namespace_stack: Vec, @@ -219,6 +218,7 @@ impl IrPass for CollectTypeDefinitionsPass { fnc.body.visit(self, symbol_table)?; // Declaring + let qualified_name = format!("{}::<{}>", qualified_name, args_types.join(",")); let return_type = "TODO"; symbol_table.declare_function_type(&qualified_name, &args_types, return_type)?; @@ -276,7 +276,7 @@ impl IrPass for CollectTypeDefinitionsPass { &mut self, mode: TreeTraversalMode, _primitives: &mut IntermediateRepresentation, - symbol_table: &mut SymbolTable, + _symbol_table: &mut SymbolTable, ) -> Result { match mode { TreeTraversalMode::Enter => (), diff --git a/products/bluebell/core/src/passes/debug_printer.rs b/products/bluebell/core/src/passes/debug_printer.rs index b00c37297..a1cc47250 100644 --- a/products/bluebell/core/src/passes/debug_printer.rs +++ b/products/bluebell/core/src/passes/debug_printer.rs @@ -1,16 +1,16 @@ -use crate::constants::{TraversalResult, TreeTraversalMode}; -use crate::intermediate_representation::pass::IrPass; -use crate::intermediate_representation::pass_executor::PassExecutor; -use crate::intermediate_representation::primitives::CaseClause; -use crate::intermediate_representation::primitives::ContractField; -use crate::intermediate_representation::primitives::Instruction; -use crate::intermediate_representation::primitives::{ - ConcreteFunction, ConcreteType, EnumValue, FunctionBlock, FunctionBody, FunctionKind, - IntermediateRepresentation, IrIdentifier, IrIndentifierKind, Operation, Tuple, - VariableDeclaration, Variant, +use crate::{ + constants::{TraversalResult, TreeTraversalMode}, + intermediate_representation::{ + pass::IrPass, + pass_executor::PassExecutor, + primitives::{ + CaseClause, ConcreteFunction, ConcreteType, ContractField, EnumValue, FunctionBlock, + FunctionBody, FunctionKind, Instruction, IntermediateRepresentation, IrIdentifier, + IrIndentifierKind, Operation, Tuple, VariableDeclaration, Variant, + }, + symbol_table::SymbolTable, + }, }; -use crate::intermediate_representation::symbol_table::SymbolTable; -use log::info; pub struct DebugPrinter { script: String, @@ -156,13 +156,6 @@ impl IrPass for DebugPrinter { Operation::Noop => { self.script.push_str("noop"); } - Operation::Switch { cases, on_default } => { - self.script.push_str("switch "); - for case in cases.iter_mut() { - case.visit(self, symbol_table)?; - } - on_default.visit(self, symbol_table)?; - } Operation::Jump(identifier) => { self.script.push_str("jmp "); identifier.visit(self, symbol_table)?; @@ -249,7 +242,6 @@ impl IrPass for DebugPrinter { self.script.push_str(" "); self.script.push_str(&data); } - Operation::AcceptTransfer => self.script.push_str("accept"), Operation::PhiNode(arguments) => { self.script.push_str("phi ["); for (i, arg) in arguments.iter_mut().enumerate() { diff --git a/products/bluebell/core/src/passes/state_allocator.rs b/products/bluebell/core/src/passes/state_allocator.rs index 58c4e7078..490ecac08 100644 --- a/products/bluebell/core/src/passes/state_allocator.rs +++ b/products/bluebell/core/src/passes/state_allocator.rs @@ -1,23 +1,23 @@ -use crate::intermediate_representation::symbol_table::StateLayoutEntry; use primitive_types::U256; -use crate::constants::{TraversalResult, TreeTraversalMode}; -use crate::intermediate_representation::pass::IrPass; -use crate::intermediate_representation::pass_executor::PassExecutor; -use crate::intermediate_representation::primitives::CaseClause; -use crate::intermediate_representation::primitives::ContractField; -use crate::intermediate_representation::primitives::Instruction; -use crate::intermediate_representation::primitives::{ - ConcreteFunction, ConcreteType, EnumValue, FunctionBlock, FunctionBody, FunctionKind, - IntermediateRepresentation, IrIdentifier, IrIndentifierKind, Operation, Tuple, - VariableDeclaration, Variant, +use crate::{ + constants::{TraversalResult, TreeTraversalMode}, + intermediate_representation::{ + pass::IrPass, + pass_executor::PassExecutor, + primitives::{ + CaseClause, ConcreteFunction, ConcreteType, ContractField, EnumValue, FunctionBlock, + FunctionBody, FunctionKind, Instruction, IntermediateRepresentation, IrIdentifier, + IrIndentifierKind, Operation, Tuple, VariableDeclaration, Variant, + }, + symbol_table::{StateLayoutEntry, SymbolTable}, + }, }; -use crate::intermediate_representation::symbol_table::SymbolTable; pub struct StateCollector { namespace_stack: Vec, current_namespace: Option, - current_type: Option, + // current_type: Option, address_offset: u64, } @@ -26,7 +26,7 @@ impl StateCollector { StateCollector { namespace_stack: Vec::new(), current_namespace: None, - current_type: None, + // current_type: None, address_offset: 4919, // TODO: } } @@ -153,7 +153,7 @@ impl IrPass for StateCollector { fn visit_symbol_name( &mut self, _mode: TreeTraversalMode, - symbol: &mut IrIdentifier, + _symbol: &mut IrIdentifier, _symbol_table: &mut SymbolTable, ) -> Result { Ok(TraversalResult::Continue) @@ -161,9 +161,9 @@ impl IrPass for StateCollector { fn visit_primitives( &mut self, - mode: TreeTraversalMode, + _mode: TreeTraversalMode, _primitives: &mut IntermediateRepresentation, - symbol_table: &mut SymbolTable, + _symbol_table: &mut SymbolTable, ) -> Result { Ok(TraversalResult::Continue) } diff --git a/products/bluebell/core/src/support/evm.rs b/products/bluebell/core/src/support/evm.rs index 9929a8f69..b32252292 100644 --- a/products/bluebell/core/src/support/evm.rs +++ b/products/bluebell/core/src/support/evm.rs @@ -1,21 +1,19 @@ -use crate::ast::nodes::NodeProgram; -use crate::support::modules::BluebellModule; +use evm_assembly::{ + compiler_context::EvmCompilerContext, executable::EvmExecutable, executor::EvmExecutor, +}; -use crate::evm_bytecode_generator::EvmBytecodeGenerator; - -use crate::intermediate_representation::emitter::IrEmitter; -use crate::intermediate_representation::pass_manager::PassManager; - -use crate::parser::lexer; -use crate::parser::lexer::Lexer; -use crate::parser::parser; -use evm_assembly::compiler_context::EvmCompilerContext; -use evm_assembly::executable::EvmExecutable; -use evm_assembly::executor::EvmExecutor; +use crate::{ + ast::nodes::NodeProgram, + evm_bytecode_generator::EvmBytecodeGenerator, + intermediate_representation::{ + emitter::IrEmitter, pass_manager::PassManager, symbol_table::SymbolTableConstructor, + }, + parser::{lexer, lexer::Lexer, parser}, + support::modules::BluebellModule, +}; pub struct EvmCompiler { pub context: EvmCompilerContext, - ir_emitter: IrEmitter, pass_manager: PassManager, abi_support: bool, } @@ -24,7 +22,6 @@ impl EvmCompiler { pub fn new() -> Self { EvmCompiler { context: EvmCompilerContext::new(), - ir_emitter: IrEmitter::new(), pass_manager: PassManager::default_pipeline(), abi_support: true, } @@ -33,7 +30,6 @@ impl EvmCompiler { pub fn new_no_abi_support() -> Self { EvmCompiler { context: EvmCompilerContext::new(), - ir_emitter: IrEmitter::new(), pass_manager: PassManager::default_pipeline(), abi_support: false, } @@ -64,7 +60,9 @@ impl EvmCompiler { // TODO: Remove &mut self - needs to be removed from a number of places first pub fn compile_ast(&mut self, ast: &NodeProgram) -> Result { - let mut ir = self.ir_emitter.emit(ast)?; + let symbol_table = self.context.new_symbol_table(); + let mut ir_emitter = IrEmitter::new(symbol_table); + let mut ir = ir_emitter.emit(ast)?; self.pass_manager.run(&mut ir)?; let mut generator = EvmBytecodeGenerator::new(&mut self.context, ir, self.abi_support); diff --git a/products/bluebell/core/src/support/modules.rs b/products/bluebell/core/src/support/modules.rs index 942d65ff6..928ff4437 100644 --- a/products/bluebell/core/src/support/modules.rs +++ b/products/bluebell/core/src/support/modules.rs @@ -1,23 +1,75 @@ -use evm::backend::Backend; -use evm::executor::stack::{PrecompileFailure, PrecompileOutput, PrecompileOutputType}; -use evm::{Context as EvmContext, ExitError, ExitSucceed}; -use evm_assembly::block::EvmBlock; -use evm_assembly::compiler_context::EvmCompilerContext; -use evm_assembly::types::EvmType; -use log::{error, info}; -use std::collections::BTreeSet; -use std::mem; -use std::str::FromStr; +use std::{ + collections::{BTreeSet, HashMap}, + mem, + str::FromStr, +}; + +use evm::{ + backend::Backend, + executor::stack::{PrecompileFailure, PrecompileOutput, PrecompileOutputType}, + Context as EvmContext, ExitError, ExitSucceed, +}; +use evm_assembly::{block::EvmBlock, compiler_context::EvmCompilerContext, types::EvmType}; +use log::info; + +use crate::intermediate_representation::{ + name_generator::NameGenerator, + symbol_table::{SymbolTable, SymbolTableConstructor}, +}; + // TODO: Generalize to support both EVM and LLVM pub trait BluebellModule { fn attach(&self, context: &mut EvmCompilerContext); } +impl SymbolTableConstructor for EvmCompilerContext { + fn new_symbol_table(&self) -> SymbolTable { + let type_of_table = HashMap::new(); + + let mut ret = SymbolTable { + aliases: HashMap::new(), + type_of_table, + name_generator: NameGenerator::new(), + state_layout: HashMap::new(), + }; + + // TODO: Get types from self + let _ = ret.declare_type("Int8"); + let _ = ret.declare_type("Int16"); + let _ = ret.declare_type("Int32"); + let _ = ret.declare_type("Int64"); + let _ = ret.declare_type("Uint8"); + let _ = ret.declare_type("Uint16"); + let _ = ret.declare_type("Uint32"); + let _ = ret.declare_type("Uint64"); + let _ = ret.declare_type("String"); + let _ = ret.declare_type("ByStr20"); + + let _ = ret.declare_special_variable("_sender", "ByStr20"); + + ret.aliases + .insert("True".to_string(), "Bool::True".to_string()); + ret.aliases + .insert("False".to_string(), "Bool::False".to_string()); + let _ = ret.declare_constructor("Bool::True", &[].to_vec(), "Bool"); + let _ = ret.declare_constructor("Bool::False", &[].to_vec(), "Bool"); + + // Adding function types + for (name, (args, return_type)) in self.raw_function_declarations.iter() { + // info!("Declaring {:#?}",f); + let _ = ret.declare_function_type(&name, args, &return_type); + } + + ret + } +} + pub struct ScillaDefaultTypes; impl BluebellModule for ScillaDefaultTypes { // TODO: Generalise to support both LLVM and EVM fn attach(&self, context: &mut EvmCompilerContext) { + context.declare_integer("Bool", 1); context.declare_integer("Int8", 8); context.declare_integer("Int16", 16); context.declare_integer("Int32", 32); @@ -36,6 +88,15 @@ impl BluebellModule for ScillaDefaultTypes { } context.declare_dynamic_string("String"); + + context.declare_default_constructor("Bool::False", |block| { + block.push([0].to_vec()); + }); + context.declare_default_constructor("Bool::True", |block| { + block.push([1].to_vec()); + }); + + // TODO: Functions to be moved out to another } } @@ -66,11 +127,7 @@ impl BluebellModule for ScillaDebugBuiltins { }); let _ = specification - .declare_function( - "builtin__print__impl::", - ["Uint64"].to_vec(), - "Uint256", - ) + .declare_function("print::", ["Uint64"].to_vec(), "Uint256") .attach_runtime(|| { fn custom_runtime( input: &[u8], @@ -99,6 +156,31 @@ impl BluebellModule for ScillaDebugBuiltins { custom_runtime }); + // TODO: Make runtime module that does this for the real chain + let _ = specification + .declare_function("__intrinsic_accept_transfer::<>", Vec::new(), "Uint256") + .attach_runtime(|| { + fn custom_runtime( + input: &[u8], + _gas_limit: Option, + _context: &EvmContext, + _backend: &dyn Backend, + _is_static: bool, + ) -> Result<(PrecompileOutput, u64), PrecompileFailure> { + info!("--- ACCEPTING FUNDS [DEBUG FUNCTION] ---"); + println!("--- ACCEPTING FUNDS [DEBUG FUNCTION] ---"); + Ok(( + PrecompileOutput { + output_type: PrecompileOutputType::Exit(ExitSucceed::Returned), + output: input.to_vec(), + }, + 0, + )) + } + + custom_runtime + }); + let _ = specification .declare_function("print::", ["ByStr20"].to_vec(), "Uint256") .attach_runtime(|| { @@ -199,6 +281,28 @@ impl BluebellModule for ScillaDebugBuiltins { format!("{}\n", hex::encode(input)) ), }; + } + + custom_runtime + }); + + let _ = specification + .declare_function("print::", ["Bool"].to_vec(), "Uint256") + .attach_runtime(|| { + fn custom_runtime( + input: &[u8], + _gas_limit: Option, + _context: &EvmContext, + _backend: &dyn Backend, + _is_static: bool, + ) -> Result<(PrecompileOutput, u64), PrecompileFailure> { + info!("Was here??"); + + if input.iter().all(|&byte| byte == 0) { + info!("false"); + } else { + info!("true"); + } Ok(( PrecompileOutput { @@ -326,9 +430,9 @@ impl BluebellModule for ScillaDefaultBuiltins { fn custom_runtime( input: &[u8], gas_limit: Option, - context: &EvmContext, + _context: &EvmContext, _backend: &dyn Backend, - is_static: bool, + _is_static: bool, ) -> Result<(PrecompileOutput, u64), PrecompileFailure> { let gas_needed = 20; @@ -352,6 +456,26 @@ impl BluebellModule for ScillaDefaultBuiltins { custom_runtime }); + let _ = specification + .declare_function( + "builtin__eq::", + ["Uint64", "Uint64"].to_vec(), + "Bool", + ) + .attach_assembly(|block| { + block.eq(); + }); + + let _ = specification + .declare_function( + "builtin__eq::", + ["Uint64", "Uint64"].to_vec(), + "Bool", + ) + .attach_assembly(|block| { + block.eq(); + }); + // Memory management /* let _ = specification.declare_inline_generics("alloca", |_ctx, block, arg_types| { @@ -415,11 +539,6 @@ impl BluebellModule for ScillaDefaultBuiltins { Ok([].to_vec()) }); - let _ = specification.declare_inline_generics("builtin__eq", |_ctx, block, _arg_types| { - block.eq(); - Ok([].to_vec()) - }); - let _ = specification.declare_inline_generics("builtin__gt", |_ctx, block, _arg_types| { block.gt(); Ok([].to_vec()) @@ -436,11 +555,10 @@ impl BluebellModule for ScillaDefaultBuiltins { }); // Implementing boolean builtins: - let _ = - specification.declare_inline_generics("builtin__andb", |_ctx, block, _arg_types| { - block.and(); - Ok([].to_vec()) - }); + let _ = specification.declare_inline_generics("builtin__and", |_ctx, block, _arg_types| { + block.and(); + Ok([].to_vec()) + }); let _ = specification.declare_inline_generics("builtin__orb", |_ctx, block, _arg_types| { block.or(); diff --git a/products/bluebell/core/src/testing.rs b/products/bluebell/core/src/testing.rs index ca5b02c1d..68ac57418 100644 --- a/products/bluebell/core/src/testing.rs +++ b/products/bluebell/core/src/testing.rs @@ -1,15 +1,15 @@ -use crate::support::evm::EvmCompiler; -use crate::support::modules::ScillaDebugBuiltins; -use crate::support::modules::ScillaDefaultBuiltins; -use crate::support::modules::ScillaDefaultTypes; -use evm_assembly::executable::EvmExecutable; -use evm_assembly::observable_machine::ObservableMachine; -use evm_assembly::types::EvmTypeValue; -use primitive_types::H256; -use std::str::FromStr; +use std::{rc::Rc, str::FromStr}; +use evm_assembly::{ + executable::EvmExecutable, observable_machine::ObservableMachine, types::EvmTypeValue, +}; +use primitive_types::H256; use serde_json; -use std::rc::Rc; + +use crate::support::{ + evm::EvmCompiler, + modules::{ScillaDebugBuiltins, ScillaDefaultBuiltins, ScillaDefaultTypes}, +}; pub fn create_vm_and_run_code( function_name: &str, @@ -86,13 +86,13 @@ fn format_hex_string(input: &str) -> Result { if input.contains("...") { // 66 = 2 for "0x" + 64 for H256 - let required_zeros = 66 - input.len() + 3; // +3 for the length of "..." + let required_zeros: i64 = 66 - input.len() as i64 + 3; // +3 for the length of "..." if required_zeros < 0 { return Err("Input string is too long to be a valid H256 value."); } - let zeros = "0".repeat(required_zeros); + let zeros = "0".repeat(required_zeros as usize); Ok(input.replace("...", &zeros)) } else { Ok(input.to_string()) diff --git a/products/bluebell/core/test_all.py b/products/bluebell/core/test_all.py deleted file mode 100644 index 9e8799d03..000000000 --- a/products/bluebell/core/test_all.py +++ /dev/null @@ -1,31 +0,0 @@ -import glob -import subprocess -import sys - -success = 0 -failed = 0 -total = 0 -for f in glob.glob(f"{sys.argv[1]}/*.scilla"): - _, fname = f.rsplit("/", 1) - process = subprocess.run( - ["./target/release/bluebell", f], capture_output=True, text=True - ) - dots = "." * (80 - len(fname)) - sys.stdout.write(f"Testing '{fname}' {dots} ") - sys.stdout.flush() - total += 1 - if process.returncode != 0: - sys.stdout.write("[ FAILED ]\n") - sys.stdout.flush() - - print("Output:") - print(process.stdout) - print("") - success += 1 - else: - sys.stdout.write("[ OK ]\n") - sys.stdout.flush() - failed += 1 - - -print(f"{success} successful and {failed} failed out of {total}") diff --git a/products/bluebell/core/tests/balance_transfer_tests.rs b/products/bluebell/core/tests/balance_transfer_tests.rs index c8563276a..217a4a66d 100644 --- a/products/bluebell/core/tests/balance_transfer_tests.rs +++ b/products/bluebell/core/tests/balance_transfer_tests.rs @@ -1,6 +1,6 @@ #[cfg(test)] mod tests { - use bluebell::testing::test_execution_path; + /* TODO: #[test] diff --git a/products/bluebell/core/tests/error_checking_tests.rs b/products/bluebell/core/tests/error_checking_tests.rs index 25f1f5fa5..25cad44de 100644 --- a/products/bluebell/core/tests/error_checking_tests.rs +++ b/products/bluebell/core/tests/error_checking_tests.rs @@ -13,7 +13,7 @@ mod tests { --| contract HelloWorldExample() --> transition TriggerHelloWorld() --> msg = "Hello world."; ---| print msg (* TODO: Fix position propagation to such this line is captured *) +--> print msg --| end "#, "", diff --git a/products/bluebell/core/tests/evm_compiler_full_tests.rs b/products/bluebell/core/tests/evm_compiler_full_tests.rs index 906aece56..a7bf3a497 100644 --- a/products/bluebell/core/tests/evm_compiler_full_tests.rs +++ b/products/bluebell/core/tests/evm_compiler_full_tests.rs @@ -1,11 +1,10 @@ #[cfg(test)] mod tests { - use bluebell::support::evm::EvmCompiler; - use bluebell::support::modules::ScillaDebugBuiltins; - use bluebell::support::modules::ScillaDefaultBuiltins; - use bluebell::support::modules::ScillaDefaultTypes; - use evm_assembly::executor::ExecutorResult; - use evm_assembly::types::EvmTypeValue; + use bluebell::support::{ + evm::EvmCompiler, + modules::{ScillaDebugBuiltins, ScillaDefaultBuiltins, ScillaDefaultTypes}, + }; + use evm_assembly::{executor::ExecutorResult, types::EvmTypeValue}; use serde_json; fn result_to_string(ret: ExecutorResult) -> String { @@ -133,6 +132,39 @@ transition setHello (msg : Uint64) | True => welcome_msg := msg end end +"#, + "+0x1000000000000000000000000000000000000000.0x0000000000000000000000000000000000000000000000000000000000001337=0x000000000000000000000000000000000000000000000000000000000000002a" + ); + } + + #[test] + fn test_conditional_set_state_combined_logic() { + // TODO: Test case not working + + test_compile_and_execute_full_evm!( + "HelloWorld::setHello", + "[42]", + r#"scilla_version 0 + + library HelloWorld + + contract HelloWorld() + field welcome_msg : Uint64 = Uint64 0 + + transition setHello (msg: Uint64) + zero = Uint64 0; + test = Uint64 42; + is_owner = builtin eq msg test; + test2 = False; + is_false = builtin eq test2 is_owner; + match is_false with + | True => + welcome_msg := zero + | _ => + welcome_msg := msg + end + end + "#, "+0x1000000000000000000000000000000000000000.0x0000000000000000000000000000000000000000000000000000000000001337=0x000000000000000000000000000000000000000000000000000000000000002a" ); diff --git a/products/bluebell/core/tests/evm_compiler_no_abi_tests.rs b/products/bluebell/core/tests/evm_compiler_no_abi_tests.rs index 4dfc8d820..1cdfee77f 100644 --- a/products/bluebell/core/tests/evm_compiler_no_abi_tests.rs +++ b/products/bluebell/core/tests/evm_compiler_no_abi_tests.rs @@ -1,18 +1,17 @@ #[cfg(test)] mod tests { - use bluebell::support::evm::EvmCompiler; - use bluebell::support::modules::ScillaDebugBuiltins; - use bluebell::support::modules::ScillaDefaultBuiltins; - use bluebell::support::modules::ScillaDefaultTypes; - use evm_assembly::executable::EvmExecutable; - use evm_assembly::executor::ExecutorResult; - use evm_assembly::observable_machine::ObservableMachine; - use evm_assembly::types::EvmTypeValue; + use std::rc::Rc; + use bluebell::support::{ + evm::EvmCompiler, + modules::{ScillaDebugBuiltins, ScillaDefaultBuiltins, ScillaDefaultTypes}, + }; + use evm_assembly::{ + executable::EvmExecutable, executor::ExecutorResult, observable_machine::ObservableMachine, + types::EvmTypeValue, + }; use serde_json; - use std::rc::Rc; - fn result_to_string(ret: ExecutorResult) -> String { let mut result = "".to_string(); let mut sorted_changeset: Vec<(String, Option)> = @@ -53,10 +52,6 @@ mod tests { compiler.attach(&default_builtins); compiler.attach(&debug); let executable = compiler.executable_from_script(script.to_string())?; - println!( - "Produced code: {}", - hex::encode(&executable.executable.bytecode.clone()) - ); let arguments: Vec = if args == "" { [].to_vec() @@ -259,10 +254,10 @@ transition setHello () x = False; match x with | True => - a = builtin print__impl msg + print msg | False => - a = builtin print__impl msg; - b = builtin print__impl msg + print msg; + print msg end end "# @@ -289,8 +284,8 @@ contract HelloWorld() transition setHello () msg = Uint64 12; - x = builtin print__impl msg; - y = builtin print__impl msg + print msg; + print msg end "#, "" @@ -319,10 +314,10 @@ transition setHello () is_owner = False; match is_owner with | True => - x = builtin print__impl msg + print msg | False => - x = builtin print__impl msg; - y = builtin print__impl msg + print msg; + print msg end end @@ -351,7 +346,7 @@ transition setHello (msg : Uint64) is_owner = True; match is_owner with | True => - x = builtin print__impl msg + print msg end end "#, @@ -393,7 +388,7 @@ end transition setHello (msg : Uint64) setHelloImpl msg; msg <- welcome_msg; - x = builtin print msg + print msg end "#, "" diff --git a/products/bluebell/core/tests/evm_line_visit_test.rs b/products/bluebell/core/tests/evm_line_visit_test.rs index a21d20d95..af7826b43 100644 --- a/products/bluebell/core/tests/evm_line_visit_test.rs +++ b/products/bluebell/core/tests/evm_line_visit_test.rs @@ -14,8 +14,8 @@ mod tests { --| --> transition setHello () --> msg = Uint64 12; ---> x = builtin print__impl msg; ---> y = builtin print__impl msg +--> print msg; +--> print msg --| end "#, "", diff --git a/products/bluebell/core/tests/execution_path_tests.rs b/products/bluebell/core/tests/execution_path_tests.rs index 906aece56..4ee5d0748 100644 --- a/products/bluebell/core/tests/execution_path_tests.rs +++ b/products/bluebell/core/tests/execution_path_tests.rs @@ -1,11 +1,10 @@ #[cfg(test)] mod tests { - use bluebell::support::evm::EvmCompiler; - use bluebell::support::modules::ScillaDebugBuiltins; - use bluebell::support::modules::ScillaDefaultBuiltins; - use bluebell::support::modules::ScillaDefaultTypes; - use evm_assembly::executor::ExecutorResult; - use evm_assembly::types::EvmTypeValue; + use bluebell::support::{ + evm::EvmCompiler, + modules::{ScillaDebugBuiltins, ScillaDefaultBuiltins, ScillaDefaultTypes}, + }; + use evm_assembly::{executor::ExecutorResult, types::EvmTypeValue}; use serde_json; fn result_to_string(ret: ExecutorResult) -> String { diff --git a/products/bluebell/core/tests/formatter_preformatted_test.rs b/products/bluebell/core/tests/formatter_preformatted_test.rs index 64d8b44c4..11e6bb99a 100644 --- a/products/bluebell/core/tests/formatter_preformatted_test.rs +++ b/products/bluebell/core/tests/formatter_preformatted_test.rs @@ -1,17 +1,17 @@ #[cfg(test)] mod tests { extern crate diffy; - use bluebell::formatter::BluebellFormatter; - use bluebell::parser::lexer; - use bluebell::parser::lexer::Lexer; - use bluebell::parser::lexer::SourcePosition; - use bluebell::parser::parser; - use bluebell::parser::ParserError; - + use std::{fs, fs::File, io::Read}; + + use bluebell::{ + formatter::BluebellFormatter, + parser::{ + lexer, + lexer::{Lexer, SourcePosition}, + parser, ParserError, + }, + }; use diffy::{create_patch, PatchFormatter}; - use std::fs; - use std::fs::File; - use std::io::Read; fn strip_comments(input: &str) -> String { let re = regex::Regex::new(r"[ ]*\(\*([^*]|\*+[^*)])*\*+\)\n*").unwrap(); @@ -40,7 +40,6 @@ mod tests { let formatted = formatter.emit(&mut ast2); if formatted != script { - println!("AST: {:#?}\n\n", ast2); println!("Orignial:\n{}\n\n", script); println!("Formatted:\n{}\n\n", formatted); let diff = create_patch(&script, &formatted); diff --git a/products/bluebell/evm_assembly/src/block.rs b/products/bluebell/evm_assembly/src/block.rs index a9d1d3557..0d425f120 100644 --- a/products/bluebell/evm_assembly/src/block.rs +++ b/products/bluebell/evm_assembly/src/block.rs @@ -1,15 +1,18 @@ -use crate::function_signature::EvmFunctionSignature; -use crate::instruction::{EvmInstruction, EvmSourcePosition, RustPosition}; -use crate::opcode_spec::{OpcodeSpec, OpcodeSpecification}; -use crate::types::EvmType; -use crate::types::EvmTypeValue; +use std::{ + collections::{BTreeSet, HashMap}, + mem, +}; + use evm::Opcode; use log::info; - use primitive_types::U256; -use std::collections::BTreeSet; -use std::collections::HashMap; -use std::mem; + +use crate::{ + function_signature::EvmFunctionSignature, + instruction::{EvmInstruction, EvmSourcePosition, RustPosition}, + opcode_spec::{OpcodeSpec, OpcodeSpecification}, + types::{EvmType, EvmTypeValue}, +}; pub const ALLOCATION_POINTER: u8 = 0x40; pub const MEMORY_OFFSET: u8 = 0x80; @@ -522,9 +525,20 @@ impl EvmBlock { } pub fn call(&mut self, function: &EvmFunctionSignature, args: Vec) -> &mut Self { + if let Some(generator) = function.inline_assembly_generator { + generator(self); + return self; + } + let address = match function.external_address { Some(a) => a, - None => panic!("TODO: Internal calls not supported yet."), + None => { + info!("{:#?}", function); + panic!( + "TODO: Internal calls' not supported yet. Attempted to call {}", + function.name + ) + } }; // TODO: Deal with internal calls // See https://medium.com/@rbkhmrcr/precompiles-solidity-e5d29bd428c4 diff --git a/products/bluebell/evm_assembly/src/bytecode_ir.rs b/products/bluebell/evm_assembly/src/bytecode_ir.rs index 144c58ca0..8370d0a20 100644 --- a/products/bluebell/evm_assembly/src/bytecode_ir.rs +++ b/products/bluebell/evm_assembly/src/bytecode_ir.rs @@ -1,7 +1,7 @@ -use crate::block::EvmBlock; -use crate::function::EvmFunction; use std::collections::VecDeque; +use crate::{block::EvmBlock, function::EvmFunction}; + #[derive(Debug, Clone)] pub struct EvmBytecodeIr { pub functions: VecDeque, diff --git a/products/bluebell/evm_assembly/src/compiler_context.rs b/products/bluebell/evm_assembly/src/compiler_context.rs index 1b3e62351..76e4cf65a 100644 --- a/products/bluebell/evm_assembly/src/compiler_context.rs +++ b/products/bluebell/evm_assembly/src/compiler_context.rs @@ -1,12 +1,17 @@ -use crate::block::EvmBlock; -use crate::evm_bytecode_builder::EvmByteCodeBuilder; -use crate::function_signature::EvmFunctionSignature; -use crate::types::EvmType; +use std::{ + collections::{BTreeMap, HashMap}, + str::FromStr, +}; + use evm::executor::stack::PrecompileFn; use primitive_types::H160; -use std::collections::BTreeMap; -use std::collections::HashMap; -use std::str::FromStr; + +use crate::{ + block::EvmBlock, + evm_bytecode_builder::EvmByteCodeBuilder, + function_signature::{AssemblyBuilderFn, EvmFunctionSignature}, + types::EvmType, +}; type InlineGenericsFn = fn(&mut EvmCompilerContext, &mut EvmBlock, Vec) -> Result, String>; @@ -14,8 +19,10 @@ type SpecialVariableFn = fn(&mut EvmCompilerContext, &mut EvmBlock) -> Result, String>; pub struct EvmCompilerContext { - type_declarations: HashMap, + pub raw_function_declarations: HashMap, String)>, + pub type_declarations: HashMap, + pub default_constructors: HashMap, pub function_declarations: HashMap, pub inline_generics: HashMap, pub special_variables: HashMap, @@ -48,7 +55,8 @@ impl<'a> EvmPrecompileBuilder<'a> { let address = { let value = index; - let padded_string = format!("{:0>40}", value.to_string()); // Pad with leading zeros to 40 characters + // Convert `value: u32` to a hexadecimal string, pad it with leading zeros to 40 characters, and then convert it to `H160` + let padded_string = format!("{:0>40}", format!("{:x}", value)); // Pad with leading zeros to 40 characters H160::from_str(&padded_string).unwrap() }; @@ -61,11 +69,24 @@ impl<'a> EvmPrecompileBuilder<'a> { Ok(()) } + + pub fn attach_assembly(&mut self, builder: AssemblyBuilderFn) -> Result<(), String> { + let name = self.signature.name.clone(); + self.signature.inline_assembly_generator = Some(builder); + self.context + .function_declarations + .insert(name.clone(), self.signature.clone()); + + Ok(()) + } } impl EvmCompilerContext { pub fn new() -> Self { Self { + raw_function_declarations: HashMap::new(), + + default_constructors: HashMap::new(), type_declarations: HashMap::new(), function_declarations: HashMap::new(), inline_generics: HashMap::new(), @@ -115,7 +136,7 @@ impl EvmCompilerContext { pub fn declare_special_variable( &mut self, name: &str, - typename: &str, + _typename: &str, builder: SpecialVariableFn, ) -> Result<(), String> { if self.special_variables.contains_key(name) { @@ -137,6 +158,11 @@ impl EvmCompilerContext { Ok(()) } + pub fn declare_default_constructor(&mut self, name: &str, constructor: AssemblyBuilderFn) { + self.default_constructors + .insert(name.to_string(), constructor); + } + pub fn declare_function( &mut self, name: &str, @@ -145,6 +171,14 @@ impl EvmCompilerContext { ) -> EvmPrecompileBuilder { // TODO: check if the function already exists + self.raw_function_declarations.insert( + name.to_string(), + ( + arg_types.iter().map(|x| x.to_string()).collect(), + return_type.to_string(), + ), + ); + let return_type = self .type_declarations .get(return_type) diff --git a/products/bluebell/evm_assembly/src/evm_bytecode_builder.rs b/products/bluebell/evm_assembly/src/evm_bytecode_builder.rs index 6a0f988a1..f35339236 100644 --- a/products/bluebell/evm_assembly/src/evm_bytecode_builder.rs +++ b/products/bluebell/evm_assembly/src/evm_bytecode_builder.rs @@ -1,20 +1,20 @@ -use crate::bytecode_ir::EvmBytecodeIr; -use crate::compiler_context::EvmCompilerContext; -use crate::executable::EvmExecutable; -use crate::opcode_spec::OpcodeSpec; -use std::collections::BTreeSet; -use std::collections::HashSet; -use std::mem; +use std::{ + collections::{BTreeSet, HashMap, HashSet}, + mem, +}; use evm::Opcode; -use std::collections::HashMap; - -use crate::block::EvmBlock; -use crate::evm_decompiler::EvmAssemblyGenerator; -use crate::function::EvmFunction; -use crate::opcode_spec::{create_opcode_spec, OpcodeSpecification}; -use crate::types::EvmType; +use crate::{ + block::EvmBlock, + bytecode_ir::EvmBytecodeIr, + compiler_context::EvmCompilerContext, + evm_decompiler::EvmAssemblyGenerator, + executable::EvmExecutable, + function::EvmFunction, + opcode_spec::{create_opcode_spec, OpcodeSpec, OpcodeSpecification}, + types::EvmType, +}; pub struct FunctionBuilder<'a, 'ctx> { pub builder: &'a mut EvmByteCodeBuilder<'ctx>, @@ -464,6 +464,7 @@ impl<'ctx> EvmByteCodeBuilder<'ctx> { instruction.u32_to_arg_big_endian(*p); } None => { + println!("Available labels: {:#?}", self.label_positions); panic!("Label not found {:#?}!", name); } } diff --git a/products/bluebell/evm_assembly/src/executor.rs b/products/bluebell/evm_assembly/src/executor.rs index 6eefbca38..a181291cd 100644 --- a/products/bluebell/evm_assembly/src/executor.rs +++ b/products/bluebell/evm_assembly/src/executor.rs @@ -1,20 +1,22 @@ -use crate::compiler_context::EvmCompilerContext; -use crate::io_interface::CustomMemoryAccount; -use crate::io_interface::EvmIoInterface; -use crate::types::EvmTypeValue; -use evm::backend::Apply; -use evm::executor::stack::MemoryStackState; -use evm::executor::stack::StackExecutor; -use evm::executor::stack::StackSubstateMetadata; -use evm::Config; +use std::{ + collections::{BTreeMap, HashMap}, + str::FromStr, +}; + +use evm::{ + backend::Apply, + executor::stack::{MemoryStackState, StackExecutor, StackSubstateMetadata}, + Config, +}; use log::info; -use primitive_types::H160; -use primitive_types::U256; -use std::collections::BTreeMap; -use std::collections::HashMap; -use std::str::FromStr; +use primitive_types::{H160, U256}; -use crate::executable::EvmExecutable; +use crate::{ + compiler_context::EvmCompilerContext, + executable::EvmExecutable, + io_interface::{CustomMemoryAccount, EvmIoInterface}, + types::EvmTypeValue, +}; pub struct EvmExecutor<'a> { pub context: &'a EvmCompilerContext, @@ -91,7 +93,7 @@ impl<'a> EvmExecutor<'a> { ); let (state_apply, _logs) = executor.into_state().deconstruct(); - info!("Exit reason: {:#?}", exit_reason); + info!("\n\n\nExit reason: {:#?}", exit_reason); info!("Result: {:#?}", result); let mut ret = ExecutorResult { diff --git a/products/bluebell/evm_assembly/src/function.rs b/products/bluebell/evm_assembly/src/function.rs index 809985bf4..1acdb9158 100644 --- a/products/bluebell/evm_assembly/src/function.rs +++ b/products/bluebell/evm_assembly/src/function.rs @@ -1,8 +1,8 @@ -use crate::block::EvmBlock; -use crate::function_signature::EvmFunctionSignature; +use std::collections::{HashMap, HashSet, VecDeque}; + use evm::Opcode; -use std::collections::HashSet; -use std::collections::{HashMap, VecDeque}; + +use crate::{block::EvmBlock, function_signature::EvmFunctionSignature}; #[derive(Debug, Clone)] pub struct EvmFunction { diff --git a/products/bluebell/evm_assembly/src/function_signature.rs b/products/bluebell/evm_assembly/src/function_signature.rs index 389e6e496..93765aa6a 100644 --- a/products/bluebell/evm_assembly/src/function_signature.rs +++ b/products/bluebell/evm_assembly/src/function_signature.rs @@ -1,4 +1,9 @@ -use crate::types::{EvmType, EvmTypeValue}; +use crate::{ + block::EvmBlock, + types::{EvmType, EvmTypeValue}, +}; + +pub type AssemblyBuilderFn = fn(&mut EvmBlock); #[derive(Debug, Clone)] pub struct EvmFunctionSignature { @@ -6,6 +11,7 @@ pub struct EvmFunctionSignature { pub arguments: Vec, pub return_type: EvmType, + pub inline_assembly_generator: Option, pub external_address: Option, } @@ -15,6 +21,7 @@ impl EvmFunctionSignature { name, arguments, return_type: return_type.clone(), + inline_assembly_generator: None, external_address: None, } } diff --git a/products/bluebell/evm_assembly/src/io_interface.rs b/products/bluebell/evm_assembly/src/io_interface.rs index 8caec9905..7baa472a6 100644 --- a/products/bluebell/evm_assembly/src/io_interface.rs +++ b/products/bluebell/evm_assembly/src/io_interface.rs @@ -1,6 +1,7 @@ +use std::collections::BTreeMap; + use evm::backend::{Backend, Basic}; use primitive_types::{H160, H256, U256}; -use std::collections::BTreeMap; #[derive(Default, Clone, Debug, Eq, PartialEq)] pub struct CustomMemoryAccount { @@ -75,7 +76,7 @@ impl Backend for EvmIoInterface { self.state.contains_key(&address) } - fn basic(&self, address: H160) -> Basic { + fn basic(&self, _address: H160) -> Basic { Basic { balance: 0.into(), nonce: 0.into(), diff --git a/products/bluebell/evm_assembly/src/lib.rs b/products/bluebell/evm_assembly/src/lib.rs index 8b2cfbb31..329cf302c 100644 --- a/products/bluebell/evm_assembly/src/lib.rs +++ b/products/bluebell/evm_assembly/src/lib.rs @@ -13,5 +13,4 @@ pub mod observable_machine; pub mod opcode_spec; pub mod types; -pub use self::evm_bytecode_builder::EvmByteCodeBuilder; -pub use self::evm_decompiler::EvmAssemblyGenerator; +pub use self::{evm_bytecode_builder::EvmByteCodeBuilder, evm_decompiler::EvmAssemblyGenerator}; diff --git a/products/bluebell/evm_assembly/src/observable_machine.rs b/products/bluebell/evm_assembly/src/observable_machine.rs index 854adc98e..7a5983e48 100644 --- a/products/bluebell/evm_assembly/src/observable_machine.rs +++ b/products/bluebell/evm_assembly/src/observable_machine.rs @@ -1,24 +1,19 @@ -use crate::executable::TypeSourceMap; -use crate::io_interface::EvmIoInterface; -use evm::executor::stack::PrecompileFn; -use evm::Capture; -use evm::ExitReason; -use evm::Trap; -use log::warn; -use primitive_types::U256; -use std::collections::BTreeMap; -use std::str::FromStr; - -use evm::Capture::Exit; -use evm::Capture::Trap as CaptureTrap; -use evm::Context; -use evm::Machine; -use evm::Opcode; - -use primitive_types::{H160, H256}; - -use std::collections::{HashMap, HashSet}; -use std::rc::Rc; +use std::{ + collections::{BTreeMap, HashMap, HashSet}, + rc::Rc, + str::FromStr, +}; + +use evm::{ + executor::stack::PrecompileFn, + Capture, + Capture::{Exit, Trap as CaptureTrap}, + Context, ExitReason, Machine, Opcode, Trap, +}; +use log::{error, info}; +use primitive_types::{H160, H256, U256}; + +use crate::{executable::TypeSourceMap, io_interface::EvmIoInterface}; pub type EvmPrecompileSet = BTreeMap; @@ -122,9 +117,9 @@ impl ObservableMachine { Opcode::CALLER => { let stack = self.machine.stack_mut(); - let mut h256_bytes = [0u8; 32]; // Create h256_bytes[12..].copy_from_slice(&self.caller.0); + let h256_bytes = [0u8; 32]; // Create h256_bytes[12..].copy_from_slice(&self.caller.0); - stack.push(H256::from_slice(&h256_bytes)); + let _ = stack.push(H256::from_slice(&h256_bytes)); } Opcode::CALLVALUE => { let stack = self.machine.stack_mut(); @@ -144,8 +139,8 @@ impl ObservableMachine { Opcode::STATICCALL => { // Emulating static call // TODO: Attach runtime! - - let (gas, address, args_offset, args_size, ret_offset, ret_size) = { + info!("Static call"); + let (gas, address, args_offset, args_size, _ret_offset, _ret_size) = { let stack = self.machine.stack_mut(); let gas: u64 = match stack.pop() { Ok(g) => h160_to_usize(g.into()) as u64, @@ -192,8 +187,12 @@ impl ObservableMachine { let ret = { let mem = self.machine.memory().data(); let end = args_offset + args_size; - //let - let input = &mem[args_offset..end]; //args_offset, args_offset+args_size); + + let input = if args_size > 0 { + &mem[args_offset..end] + } else { + &[] + }; // TODO: Integrate these properly let dummy_context = Context { @@ -211,12 +210,15 @@ impl ObservableMachine { // TODO: Write ret to memory H256::zero() } else { + error!("Result error in static call"); H256::zero() } } else { + error!("Contract on address {:?} not found", address); H256::zero() } } else { + error!("Precompile set not found"); H256::zero() }; diff --git a/products/bluebell/evm_assembly/src/opcode_spec.rs b/products/bluebell/evm_assembly/src/opcode_spec.rs index be957f6d9..c48455517 100644 --- a/products/bluebell/evm_assembly/src/opcode_spec.rs +++ b/products/bluebell/evm_assembly/src/opcode_spec.rs @@ -1,6 +1,7 @@ -use evm::Opcode; use std::collections::HashMap; +use evm::Opcode; + #[derive(Debug, Clone)] pub struct OpcodeSpecification { pub opcode: Opcode, diff --git a/products/bluebell/evm_assembly/src/types.rs b/products/bluebell/evm_assembly/src/types.rs index df439f94a..f8f96b617 100644 --- a/products/bluebell/evm_assembly/src/types.rs +++ b/products/bluebell/evm_assembly/src/types.rs @@ -1,4 +1,5 @@ use core::str::FromStr; + use primitive_types::U256; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_json::Value; diff --git a/products/bluebell/evm_assembly/tests/evm_basic_run.rs b/products/bluebell/evm_assembly/tests/evm_basic_run.rs index ca305ca5d..742a504f5 100644 --- a/products/bluebell/evm_assembly/tests/evm_basic_run.rs +++ b/products/bluebell/evm_assembly/tests/evm_basic_run.rs @@ -1,13 +1,12 @@ -use std::str::FromStr; - -use evm::backend::Backend; -use evm::executor::stack::PrecompileFn; -use evm::executor::stack::{PrecompileFailure, PrecompileOutput, PrecompileOutputType}; -use evm::{Context, ExitError, ExitSucceed}; -use evm_assembly::compiler_context::EvmCompilerContext; -use evm_assembly::types::EvmTypeValue; +use std::{collections::BTreeMap, str::FromStr}; + +use evm::{ + backend::Backend, + executor::stack::{PrecompileFailure, PrecompileFn, PrecompileOutput, PrecompileOutputType}, + Context, ExitError, ExitSucceed, +}; +use evm_assembly::{compiler_context::EvmCompilerContext, types::EvmTypeValue}; use primitive_types::H160; -use std::collections::BTreeMap; // See // https://odra.dev/blog/evm-at-risc0/ @@ -58,14 +57,11 @@ pub fn get_precompiles() -> BTreeMap { #[cfg(test)] mod tests { - use evm_assembly::executor::EvmExecutor; - use evm_assembly::types::EvmType; use std::str::FromStr; - use crate::test_precompile; - use crate::{EvmCompilerContext, EvmTypeValue}; + use evm_assembly::{executor::EvmExecutor, types::EvmType, EvmByteCodeBuilder}; - use evm_assembly::EvmByteCodeBuilder; + use crate::{test_precompile, EvmCompilerContext, EvmTypeValue}; #[test] fn blah() { diff --git a/products/bluebell/evm_assembly/tests/evm_decompile.rs b/products/bluebell/evm_assembly/tests/evm_decompile.rs index 3d30fd032..0f4d83b12 100644 --- a/products/bluebell/evm_assembly/tests/evm_decompile.rs +++ b/products/bluebell/evm_assembly/tests/evm_decompile.rs @@ -1,8 +1,6 @@ #[cfg(test)] mod tests { - use evm_assembly::compiler_context::EvmCompilerContext; - use evm_assembly::EvmAssemblyGenerator; - use evm_assembly::EvmByteCodeBuilder; + use evm_assembly::{compiler_context::EvmCompilerContext, EvmByteCodeBuilder}; #[test] fn blah() { @@ -18,7 +16,7 @@ mod tests { specification.declare_unsigned_integer("Uint256", 256); let bytes = hex::decode("608060405234801561001057600080fd5b506004361061002b5760003560e01c80633a19a7c614610030575b600080fd5b61003861004e565b6040516100459190610107565b60405180910390f35b60606000604051806101400160405280610114815260200161012a610114913990508091505090565b600081519050919050565b600082825260208201905092915050565b60005b838110156100b1578082015181840152602081019050610096565b60008484015250505050565b6000601f19601f8301169050919050565b60006100d982610077565b6100e38185610082565b93506100f3818560208601610093565b6100fc816100bd565b840191505092915050565b6000602082019050818103600083015261012181846100ce565b90509291505056fe48656c6c6f20576f726c642048656c6c6f20576f726c642048656c6c6f20576f726c642048656c6c6f20576f726c642048656c6c6f20576f726c642048656c6c6f20576f726c642048656c6c6f20576f726c642048656c6c6f20576f726c642048656c6c6f20576f726c642048656c6c6f20576f726c642048656c6c6f20576f726c642048656c6c6f20576f726c642048656c6c6f20576f726c642048656c6c6f20576f726c642048656c6c6f20576f726c642048656c6c6f20576f726c642048656c6c6f20576f726c642048656c6c6f20576f726c642048656c6c6f20576f726c642048656c6c6f20576f726c642048656c6c6f20576f726c642048656c6c6f20576f726c642048656c6c6f20576f726c6420a26469706673582212209e44d7f3c5ad5ed44f2d09f524e9aea6f2a72997367b5def3f0952f557cf658864736f6c63430008140033").unwrap(); - let builder = EvmByteCodeBuilder::from_bytes(&mut specification, bytes); + let _builder = EvmByteCodeBuilder::from_bytes(&mut specification, bytes); assert!(false); } diff --git a/products/bluebell/examples/hello-world.scilla b/products/bluebell/examples/hello-world.scilla index 12b0f52e6..4f2d9f628 100644 --- a/products/bluebell/examples/hello-world.scilla +++ b/products/bluebell/examples/hello-world.scilla @@ -3,6 +3,6 @@ scilla_version 0 library HelloWorld contract HelloWorld() -transition setHello (msg : Uint64) - x = builtin print__impl msg +transition setHello (msg : String) + print msg end \ No newline at end of file diff --git a/products/bluebell/examples/target3.scilla b/products/bluebell/examples/target3.scilla index db74f8e41..a0e8956ad 100644 --- a/products/bluebell/examples/target3.scilla +++ b/products/bluebell/examples/target3.scilla @@ -6,7 +6,7 @@ contract HelloWorld() field welcome_msg : Uint64 = Uint64 0 transition setHello (msg : Uint64) - x = builtin print__impl msg; + print msg; (* value = "Hello world"; x = builtin print value; *) welcome_msg := msg diff --git a/products/bluebell/examples/target4.scilla b/products/bluebell/examples/target4.scilla index 12b0f52e6..e18b8560d 100644 --- a/products/bluebell/examples/target4.scilla +++ b/products/bluebell/examples/target4.scilla @@ -4,5 +4,5 @@ library HelloWorld contract HelloWorld() transition setHello (msg : Uint64) - x = builtin print__impl msg + print msg end \ No newline at end of file diff --git a/products/bluebell/examples/target5.scilla b/products/bluebell/examples/target5.scilla index 84818605a..f7481949f 100644 --- a/products/bluebell/examples/target5.scilla +++ b/products/bluebell/examples/target5.scilla @@ -8,6 +8,6 @@ field welcome_msg : Uint64 = Uint64 0 transition setHello (msg : Uint64) welcome_msg := msg; msg <- welcome_msg; - x = builtin print__impl msg + print msg end diff --git a/products/bluebell/examples/target6.scilla b/products/bluebell/examples/target6.scilla index d33070bf5..f570dd5ac 100644 --- a/products/bluebell/examples/target6.scilla +++ b/products/bluebell/examples/target6.scilla @@ -16,9 +16,9 @@ transition setHello (msg : Uint64) is_owner = False; match is_owner with | True => - x = builtin print__impl msg + print msg | False => - x = builtin print__impl msg; - y = builtin print__impl msg + print msg; + print msg end end diff --git a/products/bluebell/examples/target7.scilla b/products/bluebell/examples/target7.scilla index a8c34d415..ecbcc4d6e 100644 --- a/products/bluebell/examples/target7.scilla +++ b/products/bluebell/examples/target7.scilla @@ -3,6 +3,6 @@ scilla_version 0 contract HelloWorld() transition setHello (msg : Uint64) - x = builtin print__impl msg; - y = builtin print__impl msg + print msg; + print msg end diff --git a/products/bluebell/examples/target8.scilla b/products/bluebell/examples/target8.scilla index 561dd65ca..b3ab1bbfd 100644 --- a/products/bluebell/examples/target8.scilla +++ b/products/bluebell/examples/target8.scilla @@ -10,7 +10,7 @@ end transition readValue () x <- welcome_msg; - x = builtin print__impl x + print x end diff --git a/products/bluebell/examples/target9.scilla b/products/bluebell/examples/target9.scilla index 39f3d67c6..7403a40ec 100644 --- a/products/bluebell/examples/target9.scilla +++ b/products/bluebell/examples/target9.scilla @@ -9,7 +9,7 @@ end transition readValue () x <- welcome_msg; - x = builtin print__impl x + print x end diff --git a/products/bluebell/playground/src/app.rs b/products/bluebell/playground/src/app.rs index 0726693cb..ca8cc7b3f 100644 --- a/products/bluebell/playground/src/app.rs +++ b/products/bluebell/playground/src/app.rs @@ -1,23 +1,19 @@ -use crate::logger::LoggerView; -use crate::state::ExecutionStatus; +use std::{collections::HashMap, rc::Rc}; -use std::collections::HashMap; -use std::rc::Rc; - -use yew::virtual_dom::VNode; - -use gloo_console as console; +use gloo_timers::callback::Timeout; use web_sys::HtmlInputElement; -use yew::prelude::*; +use yew::{prelude::*, virtual_dom::VNode}; use yewdux::prelude::*; -use crate::bytecode_view::ByteCodeView; -use crate::dropdown::Dropdown; -use crate::examples::EXAMPLES; -use crate::machine_view::MachineView; -use crate::state::{State, StateMessage}; -use crate::vm_remote::VmRemote; -use gloo_timers::callback::Timeout; +use crate::{ + bytecode_view::ByteCodeView, + dropdown::Dropdown, + examples::EXAMPLES, + logger::LoggerView, + machine_view::MachineView, + state::{ExecutionStatus, State, StateMessage}, + vm_remote::VmRemote, +}; #[derive(Clone, PartialEq)] pub struct MenuItem { @@ -36,7 +32,7 @@ pub struct AppLayoutProps { pub struct AppLayout { props: AppLayoutProps, - dispatch: Dispatch, + _dispatch: Dispatch, state: Rc, } @@ -57,7 +53,7 @@ impl Component for AppLayout { Self { props: props.clone(), state: dispatch.get(), - dispatch, + _dispatch: dispatch, } } @@ -290,7 +286,7 @@ pub fn app() -> Html {
"pointer-events-auto flex items-center justify-between gap-x-6 bg-green-600 px-6 py-2.5 sm:rounded-xl sm:py-3 sm:pl-4 sm:pr-3.5", - ExecutionStatus::Paused => "pointer-events-auto flex items-center justify-between gap-x-6 bg-yellow-600 px-6 py-2.5 sm:rounded-xl sm:py-3 sm:pl-4 sm:pr-3.5", + // Unused ExecutionStatus::Paused => "pointer-events-auto flex items-center justify-between gap-x-6 bg-yellow-600 px-6 py-2.5 sm:rounded-xl sm:py-3 sm:pl-4 sm:pr-3.5", ExecutionStatus::Failed => "pointer-events-auto flex items-center justify-between gap-x-6 bg-red-600 px-6 py-2.5 sm:rounded-xl sm:py-3 sm:pl-4 sm:pr-3.5" } }> @@ -300,7 +296,7 @@ pub fn app() -> Html { { match *error_code { ExecutionStatus::Succeeded => "Execution suceeded", - ExecutionStatus::Paused => "Execution paused", + // Unused ExecutionStatus::Paused => "Execution paused", ExecutionStatus::Failed => "Execution failed" } } diff --git a/products/bluebell/playground/src/bytecode_view.rs b/products/bluebell/playground/src/bytecode_view.rs index a7c87db4e..bd0a1c319 100644 --- a/products/bluebell/playground/src/bytecode_view.rs +++ b/products/bluebell/playground/src/bytecode_view.rs @@ -1,15 +1,12 @@ -use crate::state::State; -use crate::state::StateMessage; -use evm_assembly::executable::EvmExecutable; -use evm_assembly::instruction::RustPosition; -use gloo_console as console; +use std::{cell::RefCell, rc::Rc}; + +use evm_assembly::{executable::EvmExecutable, instruction::RustPosition}; use gloo_timers::callback::Timeout; -use std::cell::RefCell; -use std::rc::Rc; use web_sys::HtmlInputElement; +use yew::prelude::*; use yewdux::prelude::Dispatch; -use yew::prelude::*; +use crate::state::{State, StateMessage}; pub struct ByteCodeViewInstruction { pub label: Option, @@ -22,12 +19,12 @@ pub struct ByteCodeViewInstruction { pub struct ByteCodeView { props: ByteCodeViewProps, - selected_tab: usize, + // selected_tab: usize, instructions: Vec, timeout: Option, } pub enum ByteCodeViewMessage { - SelectTab(usize), + // SelectTab(usize), SetInstructions(Vec), ScrollToPosition(usize), } @@ -67,7 +64,7 @@ impl Component for ByteCodeView { let mut ret = Self { props: props.clone(), - selected_tab: 0, + // selected_tab: 0, instructions: Vec::new(), timeout: None, }; @@ -122,10 +119,12 @@ impl Component for ByteCodeView { fn update(&mut self, _ctx: &Context, msg: Self::Message) -> bool { match msg { + /* ByteCodeViewMessage::SelectTab(index) => { self.selected_tab = index; true } + */ ByteCodeViewMessage::SetInstructions(instructions) => { self.instructions = instructions; true diff --git a/products/bluebell/playground/src/examples.rs b/products/bluebell/playground/src/examples.rs index a46429259..53230f172 100644 --- a/products/bluebell/playground/src/examples.rs +++ b/products/bluebell/playground/src/examples.rs @@ -1,4 +1,4 @@ -pub static EXAMPLES: [(&str, &str); 4] = [ +pub static EXAMPLES: [(&str, &str); 5] = [ ( "Hello Builtin", r#"scilla_version 0 @@ -8,7 +8,7 @@ contract HelloWorld() transition setHello () x = Uint64 1; - y = builtin print__impl x + print x end "#, ), @@ -43,14 +43,38 @@ transition setHello () is_owner = False; match is_owner with | True => - x = builtin print__impl msg + print msg | False => - x = builtin print__impl msg; - y = builtin print__impl msg + print msg; + print msg end end "#, ), + ( + "Simple Logic", + r#"scilla_version 0 + + library BasicLogic + + contract BasicLogic() + + transition testValue (msg: Uint64) + reference = Uint64 11; + is_owner = builtin eq msg reference; + logic_reference = False; + is_false = builtin eq logic_reference is_owner; + match is_false with + | True => + msg = "The values were different"; + print msg + | False => + msg = "The values were equal"; + print msg + end + end + "#, + ), ( "Special variables", r#" diff --git a/products/bluebell/playground/src/logger.rs b/products/bluebell/playground/src/logger.rs index d5d3b4b16..0f2e92256 100644 --- a/products/bluebell/playground/src/logger.rs +++ b/products/bluebell/playground/src/logger.rs @@ -1,18 +1,13 @@ // use strum_macros::{Display, EnumIter}; -use yew::prelude::*; +use std::rc::Rc; -use crate::Dispatch; use gloo_console as console; -use yew::Component; -use yew::Context; - use serde::{Deserialize, Serialize}; +use yew::{prelude::*, Component, Context}; +use yewdux::{prelude::Reducer, store::Store}; -use std::rc::Rc; - -use yewdux::prelude::Reducer; -use yewdux::store::Store; +use crate::Dispatch; #[derive(Store, Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] #[store(storage = "local")] @@ -47,7 +42,7 @@ impl Reducer for LoggerMessage { } pub struct LoggerView { - dispatch: Dispatch, // Ignore unused warning. Dispatcher needs to be present for state to update + _dispatch: Dispatch, // Ignore unused warning. Dispatcher needs to be present for state to update state: Rc, } @@ -64,7 +59,7 @@ impl Component for LoggerView { let dispatch = Dispatch::::subscribe(state_callback); Self { state: dispatch.get(), - dispatch, + _dispatch: dispatch, } } diff --git a/products/bluebell/playground/src/machine_view.rs b/products/bluebell/playground/src/machine_view.rs index 885aa2825..96a31b181 100644 --- a/products/bluebell/playground/src/machine_view.rs +++ b/products/bluebell/playground/src/machine_view.rs @@ -1,11 +1,12 @@ -use crate::state::State; - use std::rc::Rc; + use yew::prelude::*; use yewdux::prelude::*; +use crate::state::State; + pub struct MachineView { - dispatch: Dispatch, + _dispatch: Dispatch, state: Rc, } @@ -22,7 +23,7 @@ impl Component for MachineView { let dispatch = Dispatch::::subscribe(state_callback); Self { state: dispatch.get(), - dispatch, + _dispatch: dispatch, } } diff --git a/products/bluebell/playground/src/main.rs b/products/bluebell/playground/src/main.rs index 279c5d150..4975edf5c 100644 --- a/products/bluebell/playground/src/main.rs +++ b/products/bluebell/playground/src/main.rs @@ -10,12 +10,11 @@ mod vm_remote_state; mod logger; -use crate::logger::LoggerMessage; -use crate::logger::LoggerState; -use yewdux::prelude::Dispatch; - use app::App; use log::{Log, Metadata, Record}; +use yewdux::prelude::Dispatch; + +use crate::logger::{LoggerMessage, LoggerState}; struct CaptureLogger {} diff --git a/products/bluebell/playground/src/state.rs b/products/bluebell/playground/src/state.rs index fb6ebe64a..ab9254252 100644 --- a/products/bluebell/playground/src/state.rs +++ b/products/bluebell/playground/src/state.rs @@ -1,30 +1,33 @@ // use strum_macros::{Display, EnumIter}; -use bluebell::support::evm::EvmCompiler; -use bluebell::support::modules::ScillaDebugBuiltins; -use bluebell::support::modules::ScillaDefaultBuiltins; -use bluebell::support::modules::ScillaDefaultTypes; -use evm::Capture::Exit; -use evm::ExitReason; -use evm_assembly::compiler_context::EvmCompilerContext; -use evm_assembly::executable::EvmExecutable; -use evm_assembly::function_signature::EvmFunctionSignature; -use evm_assembly::observable_machine::ObservableMachine; -use evm_assembly::types::EvmTypeValue; +use std::{ + cell::RefCell, + collections::{HashMap, HashSet}, + rc::Rc, +}; + +use bluebell::support::{ + evm::EvmCompiler, + modules::{ScillaDebugBuiltins, ScillaDefaultBuiltins, ScillaDefaultTypes}, +}; +use evm::{Capture::Exit, ExitReason}; +use evm_assembly::{ + compiler_context::EvmCompilerContext, executable::EvmExecutable, + function_signature::EvmFunctionSignature, observable_machine::ObservableMachine, + types::EvmTypeValue, +}; use gloo_console as console; use gloo_timers::callback::Timeout; use serde::{Deserialize, Serialize}; -use std::cell::RefCell; -use std::collections::{HashMap, HashSet}; -use std::rc::Rc; -use yewdux::prelude::Dispatch; -use yewdux::prelude::Reducer; -use yewdux::store::Store; +use yewdux::{ + prelude::{Dispatch, Reducer}, + store::Store, +}; #[derive(Debug, Clone, PartialEq)] pub enum ExecutionStatus { Succeeded, - Paused, + // Unused Paused, Failed, } @@ -91,10 +94,10 @@ transition setHello () is_owner = False; match is_owner with | True => - x = builtin print__impl msg + print msg | False => - x = builtin print__impl msg; - y = builtin print__impl msg + print msg; + print msg end end diff --git a/products/bluebell/playground/src/vm_remote.rs b/products/bluebell/playground/src/vm_remote.rs index 36e8abe61..1fec69bfc 100644 --- a/products/bluebell/playground/src/vm_remote.rs +++ b/products/bluebell/playground/src/vm_remote.rs @@ -1,13 +1,16 @@ -use crate::state::{State, StateMessage}; -use crate::vm_remote_layout::VmRemoteControlLayout; -use crate::vm_remote_state::{VmRemoteMessage, VmRemoteState}; -use gloo_console::info; -use gloo_timers::callback::Timeout; use std::rc::Rc; + +use gloo_timers::callback::Timeout; use web_sys::HtmlInputElement; use yew::prelude::*; use yewdux::prelude::*; +use crate::{ + state::{State, StateMessage}, + vm_remote_layout::VmRemoteControlLayout, + vm_remote_state::{VmRemoteMessage, VmRemoteState}, +}; + #[derive(Properties, Clone, PartialEq)] pub struct VmRemoteProps {} @@ -84,7 +87,7 @@ impl Component for VmRemote { let set_caller = self .dispatch - .apply_callback(|(value)| VmRemoteMessage::SetCaller(value)); + .apply_callback(|value| VmRemoteMessage::SetCaller(value)); let load_function = !self.vm_state.function_loaded; @@ -169,7 +172,7 @@ impl Component for VmRemote { ().value(); - set_caller.emit((value)); + set_caller.emit(value); }}/>
{ if let Some(ref signature) = signature { diff --git a/products/bluebell/playground/src/vm_remote_layout.rs b/products/bluebell/playground/src/vm_remote_layout.rs index b6986b4d8..44b137c32 100644 --- a/products/bluebell/playground/src/vm_remote_layout.rs +++ b/products/bluebell/playground/src/vm_remote_layout.rs @@ -1,5 +1,4 @@ use yew::prelude::*; -use yewdux::prelude::*; pub struct VmRemoteControlLayout { props: VmRemoteControlLayoutProps, diff --git a/products/bluebell/playground/src/vm_remote_state.rs b/products/bluebell/playground/src/vm_remote_state.rs index a41bccfb1..aaf003023 100644 --- a/products/bluebell/playground/src/vm_remote_state.rs +++ b/products/bluebell/playground/src/vm_remote_state.rs @@ -1,8 +1,8 @@ +use std::rc::Rc; + use evm_assembly::function_signature::EvmFunctionSignature; use serde::{Deserialize, Serialize}; -use std::rc::Rc; -use yewdux::prelude::Reducer; -use yewdux::store::Store; +use yewdux::{prelude::Reducer, store::Store}; #[derive(Store, Serialize, Deserialize, Clone)] #[store(storage = "local")] @@ -26,7 +26,7 @@ impl Default for VmRemoteState { } impl PartialEq for VmRemoteState { - fn eq(&self, other: &Self) -> bool { + fn eq(&self, _other: &Self) -> bool { false } } diff --git a/products/bluebell/rustfmt.toml b/products/bluebell/rustfmt.toml new file mode 100644 index 000000000..94e397125 --- /dev/null +++ b/products/bluebell/rustfmt.toml @@ -0,0 +1,3 @@ +edition="2021" +imports_granularity = "crate" +group_imports = "StdExternalCrate" \ No newline at end of file