|
|
@@ -1,13 +1,13 @@
|
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
|
pub(super) mod target;
|
|
|
-use crate::codegen::cfg::ControlFlowGraph;
|
|
|
-use crate::emit::cfg::emit_cfg;
|
|
|
-use crate::{
|
|
|
- codegen::{cfg::ASTFunction, Options},
|
|
|
- emit::Binary,
|
|
|
- sema::ast,
|
|
|
+use crate::codegen::{
|
|
|
+ cfg::{ASTFunction, ControlFlowGraph},
|
|
|
+ Options, STORAGE_INITIALIZER,
|
|
|
};
|
|
|
+
|
|
|
+use crate::emit::cfg::emit_cfg;
|
|
|
+use crate::{emit::Binary, sema::ast};
|
|
|
use inkwell::{
|
|
|
context::Context,
|
|
|
module::{Linkage, Module},
|
|
|
@@ -16,8 +16,12 @@ use soroban_sdk::xdr::{
|
|
|
DepthLimitedWrite, ScEnvMetaEntry, ScSpecEntry, ScSpecFunctionInputV0, ScSpecFunctionV0,
|
|
|
ScSpecTypeDef, StringM, WriteXdr,
|
|
|
};
|
|
|
+use std::ffi::CString;
|
|
|
+use std::sync;
|
|
|
|
|
|
-const SOROBAN_ENV_INTERFACE_VERSION: u64 = 85899345977;
|
|
|
+const SOROBAN_ENV_INTERFACE_VERSION: u64 = 90194313216;
|
|
|
+pub const PUT_CONTRACT_DATA: &str = "l._";
|
|
|
+pub const GET_CONTRACT_DATA: &str = "l.1";
|
|
|
|
|
|
pub struct SorobanTarget;
|
|
|
|
|
|
@@ -41,8 +45,21 @@ impl SorobanTarget {
|
|
|
None,
|
|
|
);
|
|
|
|
|
|
- Self::emit_functions_with_spec(contract, &mut binary, ns, context, contract_no);
|
|
|
- Self::emit_env_meta_entries(context, &mut binary);
|
|
|
+ let mut export_list = Vec::new();
|
|
|
+ Self::declare_externals(&mut binary);
|
|
|
+ Self::emit_functions_with_spec(
|
|
|
+ contract,
|
|
|
+ &mut binary,
|
|
|
+ ns,
|
|
|
+ context,
|
|
|
+ contract_no,
|
|
|
+ &mut export_list,
|
|
|
+ );
|
|
|
+ binary.internalize(export_list.as_slice());
|
|
|
+
|
|
|
+ Self::emit_initializer(&mut binary, ns);
|
|
|
+
|
|
|
+ Self::emit_env_meta_entries(context, &mut binary, opt);
|
|
|
|
|
|
binary
|
|
|
}
|
|
|
@@ -54,7 +71,8 @@ impl SorobanTarget {
|
|
|
binary: &mut Binary<'a>,
|
|
|
ns: &'a ast::Namespace,
|
|
|
context: &'a Context,
|
|
|
- contract_no: usize,
|
|
|
+ _contract_no: usize,
|
|
|
+ export_list: &mut Vec<&'a str>,
|
|
|
) {
|
|
|
let mut defines = Vec::new();
|
|
|
|
|
|
@@ -68,41 +86,30 @@ impl SorobanTarget {
|
|
|
// For each function, determine the name and the linkage
|
|
|
// Soroban has no dispatcher, so all externally addressable functions are exported and should be named the same as the original function name in the source code.
|
|
|
// If there are duplicate function names, then the function name in the source is mangled to include the signature.
|
|
|
- let default_constructor = ns.default_constructor(contract_no);
|
|
|
- let name = {
|
|
|
- if cfg.public {
|
|
|
- let f = match &cfg.function_no {
|
|
|
- ASTFunction::SolidityFunction(no) | ASTFunction::YulFunction(no) => {
|
|
|
- &ns.functions[*no]
|
|
|
- }
|
|
|
- _ => &default_constructor,
|
|
|
- };
|
|
|
-
|
|
|
- if f.mangled_name_contracts.contains(&contract_no) {
|
|
|
- &f.mangled_name
|
|
|
- } else {
|
|
|
- &f.id.name
|
|
|
- }
|
|
|
- } else {
|
|
|
- &cfg.name
|
|
|
- }
|
|
|
- };
|
|
|
|
|
|
- Self::emit_function_spec_entry(context, cfg, name.clone(), binary);
|
|
|
+ // if func is a default constructor, then the function name is the contract name
|
|
|
|
|
|
let linkage = if cfg.public {
|
|
|
+ let name = if cfg.name.contains("::") {
|
|
|
+ // get the third part of the name which is the function name
|
|
|
+ cfg.name.split("::").collect::<Vec<&str>>()[2]
|
|
|
+ } else {
|
|
|
+ &cfg.name
|
|
|
+ };
|
|
|
+ Self::emit_function_spec_entry(context, cfg, name.to_string(), binary);
|
|
|
+ export_list.push(name);
|
|
|
Linkage::External
|
|
|
} else {
|
|
|
Linkage::Internal
|
|
|
};
|
|
|
|
|
|
- let func_decl = if let Some(func) = binary.module.get_function(name) {
|
|
|
+ let func_decl = if let Some(func) = binary.module.get_function(&cfg.name) {
|
|
|
// must not have a body yet
|
|
|
assert_eq!(func.get_first_basic_block(), None);
|
|
|
|
|
|
func
|
|
|
} else {
|
|
|
- binary.module.add_function(name, ftype, Some(linkage))
|
|
|
+ binary.module.add_function(&cfg.name, ftype, Some(linkage))
|
|
|
};
|
|
|
|
|
|
binary.functions.insert(cfg_no, func_decl);
|
|
|
@@ -110,14 +117,21 @@ impl SorobanTarget {
|
|
|
defines.push((func_decl, cfg));
|
|
|
}
|
|
|
|
|
|
+ let init_type = context.i64_type().fn_type(&[], false);
|
|
|
+ binary
|
|
|
+ .module
|
|
|
+ .add_function("storage_initializer", init_type, None);
|
|
|
+
|
|
|
for (func_decl, cfg) in defines {
|
|
|
emit_cfg(&mut SorobanTarget, binary, contract, cfg, func_decl, ns);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- fn emit_env_meta_entries<'a>(context: &'a Context, binary: &mut Binary<'a>) {
|
|
|
+ fn emit_env_meta_entries<'a>(context: &'a Context, binary: &mut Binary<'a>, opt: &'a Options) {
|
|
|
let mut meta = DepthLimitedWrite::new(Vec::new(), 10);
|
|
|
- ScEnvMetaEntry::ScEnvMetaKindInterfaceVersion(SOROBAN_ENV_INTERFACE_VERSION)
|
|
|
+ let soroban_env_interface_version =
|
|
|
+ opt.soroban_version.unwrap_or(SOROBAN_ENV_INTERFACE_VERSION);
|
|
|
+ ScEnvMetaEntry::ScEnvMetaKindInterfaceVersion(soroban_env_interface_version)
|
|
|
.write_xdr(&mut meta)
|
|
|
.expect("writing env meta interface version to xdr");
|
|
|
Self::add_custom_section(context, &binary.module, "contractenvmetav0", meta.inner);
|
|
|
@@ -125,12 +139,12 @@ impl SorobanTarget {
|
|
|
|
|
|
fn emit_function_spec_entry<'a>(
|
|
|
context: &'a Context,
|
|
|
- cfg: &'a ControlFlowGraph,
|
|
|
+ cfg: &ControlFlowGraph,
|
|
|
name: String,
|
|
|
binary: &mut Binary<'a>,
|
|
|
) {
|
|
|
if cfg.public && !cfg.is_placeholder() {
|
|
|
- // TODO: Emit custom type spec entries.
|
|
|
+ // TODO: Emit custom type spec entries
|
|
|
let mut spec = DepthLimitedWrite::new(Vec::new(), 10);
|
|
|
ScSpecEntry::FunctionV0(ScSpecFunctionV0 {
|
|
|
name: name
|
|
|
@@ -148,7 +162,7 @@ impl SorobanTarget {
|
|
|
.unwrap_or_else(|| i.to_string())
|
|
|
.try_into()
|
|
|
.expect("function input name exceeds limit"),
|
|
|
- type_: ScSpecTypeDef::U32, // TODO: Map type.
|
|
|
+ type_: ScSpecTypeDef::U64, // TODO: Map type.
|
|
|
doc: StringM::default(), // TODO: Add doc.
|
|
|
})
|
|
|
.collect::<Vec<_>>()
|
|
|
@@ -157,7 +171,20 @@ impl SorobanTarget {
|
|
|
outputs: cfg
|
|
|
.returns
|
|
|
.iter()
|
|
|
- .map(|_| ScSpecTypeDef::U32) // TODO: Map type.
|
|
|
+ .map(|return_type| {
|
|
|
+ let ty = return_type.ty.clone();
|
|
|
+ match ty {
|
|
|
+ ast::Type::Uint(32) => ScSpecTypeDef::U32,
|
|
|
+ ast::Type::Uint(64) => ScSpecTypeDef::U64,
|
|
|
+ ast::Type::Int(_) => ScSpecTypeDef::I32,
|
|
|
+ ast::Type::Bool => ScSpecTypeDef::Bool,
|
|
|
+ ast::Type::Address(_) => ScSpecTypeDef::Address,
|
|
|
+ ast::Type::Bytes(_) => ScSpecTypeDef::Bytes,
|
|
|
+ ast::Type::String => ScSpecTypeDef::String,
|
|
|
+ ast::Type::Void => ScSpecTypeDef::Void,
|
|
|
+ _ => panic!("unsupported return type {:?}", ty),
|
|
|
+ }
|
|
|
+ }) // TODO: Map type.
|
|
|
.collect::<Vec<_>>()
|
|
|
.try_into()
|
|
|
.expect("function output count exceeds limit"),
|
|
|
@@ -192,4 +219,59 @@ impl SorobanTarget {
|
|
|
)
|
|
|
.expect("adding spec as metadata");
|
|
|
}
|
|
|
+
|
|
|
+ fn declare_externals(binary: &mut Binary) {
|
|
|
+ let ty = binary.context.i64_type();
|
|
|
+ let function_ty_1 = binary
|
|
|
+ .context
|
|
|
+ .i64_type()
|
|
|
+ .fn_type(&[ty.into(), ty.into(), ty.into()], false);
|
|
|
+ let function_ty = binary
|
|
|
+ .context
|
|
|
+ .i64_type()
|
|
|
+ .fn_type(&[ty.into(), ty.into()], false);
|
|
|
+
|
|
|
+ binary
|
|
|
+ .module
|
|
|
+ .add_function(PUT_CONTRACT_DATA, function_ty_1, Some(Linkage::External));
|
|
|
+ binary
|
|
|
+ .module
|
|
|
+ .add_function(GET_CONTRACT_DATA, function_ty, Some(Linkage::External));
|
|
|
+ }
|
|
|
+
|
|
|
+ fn emit_initializer(binary: &mut Binary, _ns: &ast::Namespace) {
|
|
|
+ let mut cfg = ControlFlowGraph::new("init".to_string(), ASTFunction::None);
|
|
|
+
|
|
|
+ cfg.public = true;
|
|
|
+ let void_param = ast::Parameter::new_default(ast::Type::Void);
|
|
|
+ cfg.returns = sync::Arc::new(vec![void_param]);
|
|
|
+
|
|
|
+ Self::emit_function_spec_entry(binary.context, &cfg, "init".to_string(), binary);
|
|
|
+
|
|
|
+ let function_name = CString::new(STORAGE_INITIALIZER).unwrap();
|
|
|
+ let mut storage_initializers = binary
|
|
|
+ .functions
|
|
|
+ .values()
|
|
|
+ .filter(|f: &&inkwell::values::FunctionValue| f.get_name() == function_name.as_c_str());
|
|
|
+ let storage_initializer = *storage_initializers
|
|
|
+ .next()
|
|
|
+ .expect("storage initializer is always present");
|
|
|
+ assert!(storage_initializers.next().is_none());
|
|
|
+
|
|
|
+ let void_type = binary.context.i64_type().fn_type(&[], false);
|
|
|
+ let init = binary
|
|
|
+ .module
|
|
|
+ .add_function("init", void_type, Some(Linkage::External));
|
|
|
+ let entry = binary.context.append_basic_block(init, "entry");
|
|
|
+
|
|
|
+ binary.builder.position_at_end(entry);
|
|
|
+ binary
|
|
|
+ .builder
|
|
|
+ .build_call(storage_initializer, &[], "storage_initializer")
|
|
|
+ .unwrap();
|
|
|
+
|
|
|
+ // return zero
|
|
|
+ let zero_val = binary.context.i64_type().const_int(2, false);
|
|
|
+ binary.builder.build_return(Some(&zero_val)).unwrap();
|
|
|
+ }
|
|
|
}
|