#![recursion_limit = "256"]
#[macro_use]
pub mod error;
#[macro_use]
pub mod engine_threading;
pub mod abi_generation;
pub mod asm_generation;
mod asm_lang;
mod build_config;
pub mod compiler_generated;
mod concurrent_slab;
mod control_flow_analysis;
mod debug_generation;
pub mod decl_engine;
pub mod ir_generation;
pub mod language;
mod metadata;
pub mod query_engine;
pub mod semantic_analysis;
pub mod source_map;
pub mod transform;
pub mod type_system;
use crate::ir_generation::check_function_purity;
use crate::query_engine::ModuleCacheEntry;
use crate::source_map::SourceMap;
pub use asm_generation::from_ir::compile_ir_context_to_finalized_asm;
use asm_generation::FinalizedAsm;
pub use asm_generation::{CompiledBytecode, FinalizedEntry};
pub use build_config::{BuildConfig, BuildTarget, LspConfig, OptLevel, PrintAsm, PrintIr};
use control_flow_analysis::ControlFlowGraph;
pub use debug_generation::write_dwarf;
use indexmap::IndexMap;
use metadata::MetadataManager;
use query_engine::{ModuleCacheKey, ModuleCommonInfo, ParsedModuleInfo, ProgramsCacheEntry};
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use sway_ast::AttributeDecl;
use sway_error::handler::{ErrorEmitted, Handler};
use sway_features::ExperimentalFeatures;
use sway_ir::{
create_o1_pass_group, register_known_passes, Context, Kind, Module, PassGroup, PassManager,
PrintPassesOpts, ARG_DEMOTION_NAME, CONST_DEMOTION_NAME, DCE_NAME, FN_DCE_NAME,
FN_DEDUP_DEBUG_PROFILE_NAME, FN_INLINE_NAME, MEM2REG_NAME, MEMCPYOPT_NAME, MISC_DEMOTION_NAME,
RET_DEMOTION_NAME, SIMPLIFY_CFG_NAME, SROA_NAME,
};
use sway_types::constants::DOC_COMMENT_ATTRIBUTE_NAME;
use sway_types::SourceEngine;
use sway_utils::{time_expr, PerformanceData, PerformanceMetric};
use transform::{Attribute, AttributeArg, AttributeKind, AttributesMap};
use types::{CollectTypesMetadata, CollectTypesMetadataContext, TypeMetadata};
pub use semantic_analysis::namespace::{self, Namespace};
pub mod types;
use sway_error::error::CompileError;
use sway_types::{ident::Ident, span, Spanned};
pub use type_system::*;
pub use language::Programs;
use language::{lexed, parsed, ty, Visibility};
use transform::to_parsed_lang::{self, convert_module_kind};
pub mod fuel_prelude {
pub use fuel_vm::{self, fuel_asm, fuel_crypto, fuel_tx, fuel_types};
}
pub use engine_threading::Engines;
pub fn parse(
input: Arc<str>,
handler: &Handler,
engines: &Engines,
config: Option<&BuildConfig>,
experimental: ExperimentalFeatures,
) -> Result<(lexed::LexedProgram, parsed::ParseProgram), ErrorEmitted> {
match config {
None => parse_in_memory(handler, engines, input, experimental),
Some(config) => parse_module_tree(
handler,
engines,
input,
config.canonical_root_module(),
None,
config.build_target,
config.include_tests,
experimental,
config.lsp_mode.as_ref(),
)
.map(
|ParsedModuleTree {
tree_type: kind,
lexed_module,
parse_module,
}| {
let lexed = lexed::LexedProgram {
kind,
root: lexed_module,
};
let parsed = parsed::ParseProgram {
kind,
root: parse_module,
};
(lexed, parsed)
},
),
}
}
pub fn parse_tree_type(
handler: &Handler,
input: Arc<str>,
) -> Result<parsed::TreeType, ErrorEmitted> {
sway_parse::parse_module_kind(handler, input, None).map(|kind| convert_module_kind(&kind))
}
fn module_attrs_to_map(
handler: &Handler,
attribute_list: &[AttributeDecl],
) -> Result<AttributesMap, ErrorEmitted> {
let mut attrs_map: IndexMap<_, Vec<Attribute>> = IndexMap::new();
for attr_decl in attribute_list {
let attrs = attr_decl.attribute.get().into_iter();
for attr in attrs {
let name = attr.name.as_str();
if name != DOC_COMMENT_ATTRIBUTE_NAME {
handler.emit_err(CompileError::ExpectedModuleDocComment {
span: attr.name.span(),
});
}
let args = attr
.args
.as_ref()
.map(|parens| {
parens
.get()
.into_iter()
.cloned()
.map(|arg| AttributeArg {
name: arg.name.clone(),
value: arg.value.clone(),
span: arg.span(),
})
.collect()
})
.unwrap_or_else(Vec::new);
let attribute = Attribute {
name: attr.name.clone(),
args,
span: attr_decl.span(),
};
if let Some(attr_kind) = match name {
DOC_COMMENT_ATTRIBUTE_NAME => Some(AttributeKind::DocComment),
_ => None,
} {
attrs_map.entry(attr_kind).or_default().push(attribute);
}
}
}
Ok(AttributesMap::new(Arc::new(attrs_map)))
}
fn parse_in_memory(
handler: &Handler,
engines: &Engines,
src: Arc<str>,
experimental: ExperimentalFeatures,
) -> Result<(lexed::LexedProgram, parsed::ParseProgram), ErrorEmitted> {
let mut hasher = DefaultHasher::new();
src.hash(&mut hasher);
let hash = hasher.finish();
let module = sway_parse::parse_file(handler, src, None)?;
let (kind, tree) = to_parsed_lang::convert_parse_tree(
&mut to_parsed_lang::Context::new(BuildTarget::EVM, experimental),
handler,
engines,
module.value.clone(),
)?;
let module_kind_span = module.value.kind.span();
let submodules = Vec::default();
let attributes = module_attrs_to_map(handler, &module.attribute_list)?;
let root = parsed::ParseModule {
span: span::Span::dummy(),
module_kind_span,
module_eval_order: vec![],
tree,
submodules,
attributes,
hash,
};
let lexed_program = lexed::LexedProgram::new(
kind,
lexed::LexedModule {
tree: module.value,
submodules: Vec::default(),
},
);
Ok((lexed_program, parsed::ParseProgram { kind, root }))
}
pub struct Submodule {
name: Ident,
path: Arc<PathBuf>,
lexed: lexed::LexedSubmodule,
parsed: parsed::ParseSubmodule,
}
pub type Submodules = Vec<Submodule>;
#[allow(clippy::too_many_arguments)]
fn parse_submodules(
handler: &Handler,
engines: &Engines,
module_name: Option<&str>,
module: &sway_ast::Module,
module_dir: &Path,
build_target: BuildTarget,
include_tests: bool,
experimental: ExperimentalFeatures,
lsp_mode: Option<&LspConfig>,
) -> Submodules {
let mut submods = Vec::with_capacity(module.submodules().count());
module.submodules().for_each(|submod| {
let submod_path = Arc::new(module_path(module_dir, module_name, submod));
let submod_str: Arc<str> = match std::fs::read_to_string(&*submod_path) {
Ok(s) => Arc::from(s),
Err(e) => {
handler.emit_err(CompileError::FileCouldNotBeRead {
span: submod.name.span(),
file_path: submod_path.to_string_lossy().to_string(),
stringified_error: e.to_string(),
});
return;
}
};
if let Ok(ParsedModuleTree {
tree_type: kind,
lexed_module,
parse_module,
}) = parse_module_tree(
handler,
engines,
submod_str.clone(),
submod_path.clone(),
Some(submod.name.as_str()),
build_target,
include_tests,
experimental,
lsp_mode,
) {
if !matches!(kind, parsed::TreeType::Library) {
let source_id = engines.se().get_source_id(submod_path.as_ref());
let span = span::Span::new(submod_str, 0, 0, Some(source_id)).unwrap();
handler.emit_err(CompileError::ImportMustBeLibrary { span });
return;
}
let parse_submodule = parsed::ParseSubmodule {
module: parse_module,
visibility: match submod.visibility {
Some(..) => Visibility::Public,
None => Visibility::Private,
},
mod_name_span: submod.name.span(),
};
let lexed_submodule = lexed::LexedSubmodule {
module: lexed_module,
};
let submodule = Submodule {
name: submod.name.clone(),
path: submod_path,
lexed: lexed_submodule,
parsed: parse_submodule,
};
submods.push(submodule);
}
});
submods
}
pub type SourceHash = u64;
#[derive(Clone, Debug)]
pub struct ParsedModuleTree {
pub tree_type: parsed::TreeType,
pub lexed_module: lexed::LexedModule,
pub parse_module: parsed::ParseModule,
}
#[allow(clippy::too_many_arguments)]
fn parse_module_tree(
handler: &Handler,
engines: &Engines,
src: Arc<str>,
path: Arc<PathBuf>,
module_name: Option<&str>,
build_target: BuildTarget,
include_tests: bool,
experimental: ExperimentalFeatures,
lsp_mode: Option<&LspConfig>,
) -> Result<ParsedModuleTree, ErrorEmitted> {
let query_engine = engines.qe();
let module_dir = path.parent().expect("module file has no parent directory");
let source_id = engines.se().get_source_id(&path.clone());
let module = sway_parse::parse_file(handler, src.clone(), Some(source_id))?;
let submodules = parse_submodules(
handler,
engines,
module_name,
&module.value,
module_dir,
build_target,
include_tests,
experimental,
lsp_mode,
);
let (kind, tree) = to_parsed_lang::convert_parse_tree(
&mut to_parsed_lang::Context::new(build_target, experimental),
handler,
engines,
module.value.clone(),
)?;
let module_kind_span = module.value.kind.span();
let attributes = module_attrs_to_map(handler, &module.attribute_list)?;
let lexed_submodules = submodules
.iter()
.map(|s| (s.name.clone(), s.lexed.clone()))
.collect::<Vec<_>>();
let lexed = lexed::LexedModule {
tree: module.value,
submodules: lexed_submodules,
};
let mut hasher = DefaultHasher::new();
src.hash(&mut hasher);
let hash = hasher.finish();
let parsed_submodules = submodules
.iter()
.map(|s| (s.name.clone(), s.parsed.clone()))
.collect::<Vec<_>>();
let parsed = parsed::ParseModule {
span: span::Span::new(src, 0, 0, Some(source_id)).unwrap(),
module_kind_span,
module_eval_order: vec![],
tree,
submodules: parsed_submodules,
attributes,
hash,
};
let modified_time = std::fs::metadata(path.as_path())
.ok()
.and_then(|m| m.modified().ok());
let dependencies = submodules.into_iter().map(|s| s.path).collect::<Vec<_>>();
let version = lsp_mode
.and_then(|lsp| lsp.file_versions.get(path.as_ref()).copied())
.unwrap_or(None);
let common_info = ModuleCommonInfo {
path: path.clone(),
include_tests,
dependencies,
hash,
};
let parsed_info = ParsedModuleInfo {
modified_time,
version,
};
let cache_entry = ModuleCacheEntry::new(common_info, parsed_info);
query_engine.update_or_insert_parsed_module_cache_entry(cache_entry);
Ok(ParsedModuleTree {
tree_type: kind,
lexed_module: lexed,
parse_module: parsed,
})
}
pub(crate) fn is_ty_module_cache_up_to_date(
engines: &Engines,
path: &Arc<PathBuf>,
include_tests: bool,
build_config: Option<&BuildConfig>,
) -> bool {
let cache = engines.qe().module_cache.read();
let key = ModuleCacheKey::new(path.clone(), include_tests);
cache.get(&key).map_or(false, |entry| {
entry.typed.as_ref().map_or(false, |typed| {
let cache_up_to_date = build_config
.and_then(|x| x.lsp_mode.as_ref())
.and_then(|lsp| lsp.file_versions.get(path.as_ref()))
.map_or(true, |version| {
version.map_or(true, |v| typed.version.map_or(false, |tv| v <= tv))
});
cache_up_to_date
&& entry.common.dependencies.iter().all(|dep_path| {
is_ty_module_cache_up_to_date(engines, dep_path, include_tests, build_config)
})
})
})
}
pub(crate) fn is_parse_module_cache_up_to_date(
engines: &Engines,
path: &Arc<PathBuf>,
include_tests: bool,
build_config: Option<&BuildConfig>,
) -> bool {
let cache = engines.qe().module_cache.read();
let key = ModuleCacheKey::new(path.clone(), include_tests);
cache.get(&key).map_or(false, |entry| {
let cache_up_to_date = build_config
.and_then(|x| x.lsp_mode.as_ref())
.and_then(|lsp| lsp.file_versions.get(path.as_ref()))
.map_or_else(
|| {
let modified_time = std::fs::metadata(path.as_path())
.ok()
.and_then(|m| m.modified().ok());
entry.parsed.modified_time == modified_time || {
let src = std::fs::read_to_string(path.as_path()).unwrap();
let mut hasher = DefaultHasher::new();
src.hash(&mut hasher);
hasher.finish() == entry.common.hash
}
},
|version| {
version.map_or(true, |v| entry.parsed.version.map_or(false, |ev| v <= ev))
},
);
cache_up_to_date
&& entry.common.dependencies.iter().all(|dep_path| {
is_parse_module_cache_up_to_date(engines, dep_path, include_tests, build_config)
})
})
}
fn module_path(
parent_module_dir: &Path,
parent_module_name: Option<&str>,
submod: &sway_ast::Submodule,
) -> PathBuf {
if let Some(parent_name) = parent_module_name {
parent_module_dir
.join(parent_name)
.join(submod.name.to_string())
.with_extension(sway_types::constants::DEFAULT_FILE_EXTENSION)
} else {
parent_module_dir
.join(submod.name.to_string())
.with_extension(sway_types::constants::DEFAULT_FILE_EXTENSION)
}
}
pub fn build_module_dep_graph(
handler: &Handler,
parse_module: &mut parsed::ParseModule,
) -> Result<(), ErrorEmitted> {
let module_dep_graph = ty::TyModule::build_dep_graph(handler, parse_module)?;
parse_module.module_eval_order = module_dep_graph.compute_order(handler)?;
for (_, submodule) in &mut parse_module.submodules {
build_module_dep_graph(handler, &mut submodule.module)?;
}
Ok(())
}
pub struct CompiledAsm(pub FinalizedAsm);
#[allow(clippy::too_many_arguments)]
pub fn parsed_to_ast(
handler: &Handler,
engines: &Engines,
parse_program: &mut parsed::ParseProgram,
initial_namespace: &mut namespace::Root,
build_config: Option<&BuildConfig>,
package_name: &str,
retrigger_compilation: Option<Arc<AtomicBool>>,
experimental: ExperimentalFeatures,
) -> Result<ty::TyProgram, ErrorEmitted> {
let lsp_config = build_config.map(|x| x.lsp_mode.clone()).unwrap_or_default();
build_module_dep_graph(handler, &mut parse_program.root)?;
let namespace = Namespace::init_root(initial_namespace);
let mut collection_ctx =
ty::TyProgram::collect(handler, engines, parse_program, namespace.clone())?;
let typed_program_opt = ty::TyProgram::type_check(
handler,
engines,
parse_program,
&mut collection_ctx,
namespace,
package_name,
build_config,
experimental,
);
check_should_abort(handler, retrigger_compilation.clone())?;
if lsp_config.is_none() {
engines.pe().clear();
}
let mut typed_program = match typed_program_opt {
Ok(typed_program) => typed_program,
Err(e) => return Err(e),
};
typed_program.check_deprecated(engines, handler);
match typed_program.check_recursive(engines, handler) {
Ok(()) => {}
Err(e) => {
handler.dedup();
return Err(e);
}
};
let types_metadata = if !lsp_config.as_ref().is_some_and(|lsp| lsp.optimized_build) {
let types_metadata_result = typed_program.collect_types_metadata(
handler,
&mut CollectTypesMetadataContext::new(engines, experimental, package_name.to_string()),
);
let types_metadata = match types_metadata_result {
Ok(types_metadata) => types_metadata,
Err(e) => {
handler.dedup();
return Err(e);
}
};
typed_program
.logged_types
.extend(types_metadata.iter().filter_map(|m| match m {
TypeMetadata::LoggedType(log_id, type_id) => Some((*log_id, *type_id)),
_ => None,
}));
typed_program
.messages_types
.extend(types_metadata.iter().filter_map(|m| match m {
TypeMetadata::MessageType(message_id, type_id) => Some((*message_id, *type_id)),
_ => None,
}));
let (print_graph, print_graph_url_format) = match build_config {
Some(cfg) => (
cfg.print_dca_graph.clone(),
cfg.print_dca_graph_url_format.clone(),
),
None => (None, None),
};
check_should_abort(handler, retrigger_compilation.clone())?;
let _ = perform_control_flow_analysis(
handler,
engines,
&typed_program,
print_graph,
print_graph_url_format,
);
types_metadata
} else {
vec![]
};
let mut ctx = Context::new(engines.se(), experimental);
let mut md_mgr = MetadataManager::default();
let module = Module::new(&mut ctx, Kind::Contract);
if let Err(e) = ir_generation::compile::compile_constants(
engines,
&mut ctx,
&mut md_mgr,
module,
typed_program.root.namespace.module(engines),
) {
handler.emit_err(e);
}
let cei_analysis_warnings =
semantic_analysis::cei_pattern_analysis::analyze_program(engines, &typed_program);
for warn in cei_analysis_warnings {
handler.emit_warn(warn);
}
let typed_wiss_res = typed_program.get_typed_program_with_initialized_storage_slots(
handler,
engines,
&mut ctx,
&mut md_mgr,
module,
);
let typed_program_with_storage_slots = match typed_wiss_res {
Ok(typed_program_with_storage_slots) => typed_program_with_storage_slots,
Err(e) => {
handler.dedup();
return Err(e);
}
};
for err in types_metadata.iter().filter_map(|m| match m {
TypeMetadata::UnresolvedType(name, call_site_span_opt) => {
Some(CompileError::UnableToInferGeneric {
ty: name.as_str().to_string(),
span: call_site_span_opt.clone().unwrap_or_else(|| name.span()),
})
}
_ => None,
}) {
handler.emit_err(err);
}
handler.dedup();
Ok(typed_program_with_storage_slots)
}
#[allow(clippy::too_many_arguments)]
pub fn compile_to_ast(
handler: &Handler,
engines: &Engines,
input: Arc<str>,
initial_namespace: &mut namespace::Root,
build_config: Option<&BuildConfig>,
package_name: &str,
retrigger_compilation: Option<Arc<AtomicBool>>,
experimental: ExperimentalFeatures,
) -> Result<Programs, ErrorEmitted> {
check_should_abort(handler, retrigger_compilation.clone())?;
let query_engine = engines.qe();
let mut metrics = PerformanceData::default();
if let Some(config) = build_config {
let path = config.canonical_root_module();
let include_tests = config.include_tests;
if is_parse_module_cache_up_to_date(engines, &path, include_tests, build_config) {
let mut entry = query_engine.get_programs_cache_entry(&path).unwrap();
entry.programs.metrics.reused_programs += 1;
let (warnings, errors) = entry.handler_data;
let new_handler = Handler::from_parts(warnings, errors);
handler.append(new_handler);
return Ok(entry.programs);
};
}
let parse_program_opt = time_expr!(
package_name,
"parse the program to a concrete syntax tree (CST)",
"parse_cst",
parse(input, handler, engines, build_config, experimental),
build_config,
metrics
);
check_should_abort(handler, retrigger_compilation.clone())?;
let (lexed_program, mut parsed_program) = match parse_program_opt {
Ok(modules) => modules,
Err(e) => {
handler.dedup();
return Err(e);
}
};
if build_config.map_or(true, |config| !config.include_tests) {
parsed_program.exclude_tests(engines);
}
let typed_res = time_expr!(
package_name,
"parse the concrete syntax tree (CST) to a typed AST",
"parse_ast",
parsed_to_ast(
handler,
engines,
&mut parsed_program,
initial_namespace,
build_config,
package_name,
retrigger_compilation.clone(),
experimental
),
build_config,
metrics
);
check_should_abort(handler, retrigger_compilation.clone())?;
handler.dedup();
let programs = Programs::new(lexed_program, parsed_program, typed_res, metrics);
if let Some(config) = build_config {
let path = config.canonical_root_module();
let cache_entry = ProgramsCacheEntry {
path,
programs: programs.clone(),
handler_data: handler.clone().consume(),
};
query_engine.insert_programs_cache_entry(cache_entry);
}
check_should_abort(handler, retrigger_compilation.clone())?;
Ok(programs)
}
pub fn compile_to_asm(
handler: &Handler,
engines: &Engines,
input: Arc<str>,
initial_namespace: &mut namespace::Root,
build_config: &BuildConfig,
package_name: &str,
experimental: ExperimentalFeatures,
) -> Result<CompiledAsm, ErrorEmitted> {
let ast_res = compile_to_ast(
handler,
engines,
input,
initial_namespace,
Some(build_config),
package_name,
None,
experimental,
)?;
ast_to_asm(handler, engines, &ast_res, build_config, experimental)
}
pub fn ast_to_asm(
handler: &Handler,
engines: &Engines,
programs: &Programs,
build_config: &BuildConfig,
experimental: ExperimentalFeatures,
) -> Result<CompiledAsm, ErrorEmitted> {
let typed_program = match &programs.typed {
Ok(typed_program) => typed_program,
Err(err) => return Err(*err),
};
let asm =
match compile_ast_to_ir_to_asm(handler, engines, typed_program, build_config, experimental)
{
Ok(res) => res,
Err(err) => {
handler.dedup();
return Err(err);
}
};
Ok(CompiledAsm(asm))
}
pub(crate) fn compile_ast_to_ir_to_asm(
handler: &Handler,
engines: &Engines,
program: &ty::TyProgram,
build_config: &BuildConfig,
experimental: ExperimentalFeatures,
) -> Result<FinalizedAsm, ErrorEmitted> {
let mut ir = match ir_generation::compile_program(
program,
build_config.include_tests,
engines,
experimental,
) {
Ok(ir) => ir,
Err(errors) => {
let mut last = None;
for e in errors {
last = Some(handler.emit_err(e));
}
return Err(last.unwrap());
}
};
let entry_point_functions: Vec<::sway_ir::Function> = ir
.module_iter()
.flat_map(|module| module.function_iter(&ir))
.filter(|func| func.is_entry(&ir))
.collect();
{
let mut env = ir_generation::PurityEnv::default();
let mut md_mgr = metadata::MetadataManager::default();
for entry_point in &entry_point_functions {
check_function_purity(handler, &mut env, &ir, &mut md_mgr, entry_point);
}
}
let mut pass_mgr = PassManager::default();
register_known_passes(&mut pass_mgr);
let mut pass_group = PassGroup::default();
match build_config.optimization_level {
OptLevel::Opt1 => {
pass_group.append_group(create_o1_pass_group());
}
OptLevel::Opt0 => {
pass_group.append_pass(FN_DEDUP_DEBUG_PROFILE_NAME);
pass_group.append_pass(FN_INLINE_NAME);
pass_group.append_pass(FN_DCE_NAME);
pass_group.append_pass(DCE_NAME);
}
}
if build_config.build_target == BuildTarget::Fuel {
pass_group.append_pass(CONST_DEMOTION_NAME);
pass_group.append_pass(ARG_DEMOTION_NAME);
pass_group.append_pass(RET_DEMOTION_NAME);
pass_group.append_pass(MISC_DEMOTION_NAME);
pass_group.append_pass(MEMCPYOPT_NAME);
pass_group.append_pass(DCE_NAME);
pass_group.append_pass(SIMPLIFY_CFG_NAME);
match build_config.optimization_level {
OptLevel::Opt1 => {
pass_group.append_pass(SROA_NAME);
pass_group.append_pass(MEM2REG_NAME);
pass_group.append_pass(DCE_NAME);
}
OptLevel::Opt0 => {}
}
}
let print_passes_opts: PrintPassesOpts = (&build_config.print_ir).into();
let res =
if let Err(ir_error) = pass_mgr.run_with_print(&mut ir, &pass_group, &print_passes_opts) {
Err(handler.emit_err(CompileError::InternalOwned(
ir_error.to_string(),
span::Span::dummy(),
)))
} else {
Ok(())
};
res?;
compile_ir_context_to_finalized_asm(handler, &ir, Some(build_config))
}
#[allow(clippy::too_many_arguments)]
pub fn compile_to_bytecode(
handler: &Handler,
engines: &Engines,
input: Arc<str>,
initial_namespace: &mut namespace::Root,
build_config: &BuildConfig,
source_map: &mut SourceMap,
package_name: &str,
experimental: ExperimentalFeatures,
) -> Result<CompiledBytecode, ErrorEmitted> {
let mut asm_res = compile_to_asm(
handler,
engines,
input,
initial_namespace,
build_config,
package_name,
experimental,
)?;
asm_to_bytecode(
handler,
&mut asm_res,
source_map,
engines.se(),
build_config,
)
}
pub const PRELUDE_CONFIGURABLES_SIZE_IN_BYTES: usize = 8;
pub const PRELUDE_CONFIGURABLES_OFFSET_IN_BYTES: usize = 16;
pub const PRELUDE_SIZE_IN_BYTES: usize = 32;
pub fn set_bytecode_configurables_offset(
compiled_bytecode: &mut CompiledBytecode,
md: &[u8; PRELUDE_CONFIGURABLES_SIZE_IN_BYTES],
) {
assert!(
compiled_bytecode.bytecode.len()
>= PRELUDE_CONFIGURABLES_OFFSET_IN_BYTES + PRELUDE_CONFIGURABLES_SIZE_IN_BYTES
);
let code = &mut compiled_bytecode.bytecode;
for (index, byte) in md.iter().enumerate() {
code[index + PRELUDE_CONFIGURABLES_OFFSET_IN_BYTES] = *byte;
}
}
pub fn asm_to_bytecode(
handler: &Handler,
asm: &mut CompiledAsm,
source_map: &mut SourceMap,
source_engine: &SourceEngine,
build_config: &BuildConfig,
) -> Result<CompiledBytecode, ErrorEmitted> {
let compiled_bytecode =
asm.0
.to_bytecode_mut(handler, source_map, source_engine, build_config)?;
Ok(compiled_bytecode)
}
fn perform_control_flow_analysis(
handler: &Handler,
engines: &Engines,
program: &ty::TyProgram,
print_graph: Option<String>,
print_graph_url_format: Option<String>,
) -> Result<(), ErrorEmitted> {
let dca_res = dead_code_analysis(handler, engines, program);
let rpa_errors = return_path_analysis(engines, program);
let rpa_res = handler.scope(|handler| {
for err in rpa_errors {
handler.emit_err(err);
}
Ok(())
});
if let Ok(graph) = dca_res.clone() {
graph.visualize(engines, print_graph, print_graph_url_format);
}
dca_res?;
rpa_res
}
fn dead_code_analysis<'a>(
handler: &Handler,
engines: &'a Engines,
program: &ty::TyProgram,
) -> Result<ControlFlowGraph<'a>, ErrorEmitted> {
let decl_engine = engines.de();
let mut dead_code_graph = ControlFlowGraph::new(engines);
let tree_type = program.kind.tree_type();
module_dead_code_analysis(
handler,
engines,
&program.root,
&tree_type,
&mut dead_code_graph,
)?;
let warnings = dead_code_graph.find_dead_code(decl_engine);
for warn in warnings {
handler.emit_warn(warn);
}
Ok(dead_code_graph)
}
fn module_dead_code_analysis<'eng: 'cfg, 'cfg>(
handler: &Handler,
engines: &'eng Engines,
module: &ty::TyModule,
tree_type: &parsed::TreeType,
graph: &mut ControlFlowGraph<'cfg>,
) -> Result<(), ErrorEmitted> {
module
.submodules
.iter()
.try_fold((), |(), (_, submodule)| {
let tree_type = parsed::TreeType::Library;
module_dead_code_analysis(handler, engines, &submodule.module, &tree_type, graph)
})?;
let res = {
ControlFlowGraph::append_module_to_dead_code_graph(
engines,
&module.all_nodes,
tree_type,
graph,
)
.map_err(|err| handler.emit_err(err))
};
graph.connect_pending_entry_edges();
res
}
fn return_path_analysis(engines: &Engines, program: &ty::TyProgram) -> Vec<CompileError> {
let mut errors = vec![];
module_return_path_analysis(engines, &program.root, &mut errors);
errors
}
fn module_return_path_analysis(
engines: &Engines,
module: &ty::TyModule,
errors: &mut Vec<CompileError>,
) {
for (_, submodule) in &module.submodules {
module_return_path_analysis(engines, &submodule.module, errors);
}
let graph = ControlFlowGraph::construct_return_path_graph(engines, &module.all_nodes);
match graph {
Ok(graph) => errors.extend(graph.analyze_return_paths(engines)),
Err(mut error) => errors.append(&mut error),
}
}
fn check_should_abort(
handler: &Handler,
retrigger_compilation: Option<Arc<AtomicBool>>,
) -> Result<(), ErrorEmitted> {
if let Some(ref retrigger_compilation) = retrigger_compilation {
if retrigger_compilation.load(Ordering::SeqCst) {
return Err(handler.cancel());
}
}
Ok(())
}
#[test]
fn test_basic_prog() {
let handler = Handler::default();
let engines = Engines::default();
let prog = parse(
r#"
contract;
enum yo
<T>
where
T: IsAThing
{
x: u32,
y: MyStruct<u32>
}
enum MyOtherSumType
{
x: u32,
y: MyStruct<u32>
}
struct MyStruct<T> {
field_name: u64,
other_field: T,
}
fn generic_function
<T>
(arg1: u64,
arg2: T)
->
T
where T: Display,
T: Debug {
let x: MyStruct =
MyStruct
{
field_name:
5
};
return
match
arg1
{
1
=> true,
_ => { return false; },
};
}
struct MyStruct {
test: string,
}
use stdlib::println;
trait MyTrait {
// interface points
fn myfunc(x: int) -> unit;
} {
// methods
fn calls_interface_fn(x: int) -> unit {
// declare a byte
let x = 0b10101111;
let mut y = 0b11111111;
self.interface_fn(x);
}
}
pub fn prints_number_five() -> u8 {
let x: u8 = 5;
println(x);
x.to_string();
let some_list = [
5,
10 + 3 / 2,
func_app(my_args, (so_many_args))];
return 5;
}
"#
.into(),
&handler,
&engines,
None,
ExperimentalFeatures::default(),
);
prog.unwrap();
}
#[test]
fn test_parenthesized() {
let handler = Handler::default();
let engines = Engines::default();
let prog = parse(
r#"
contract;
pub fn some_abi_func() -> unit {
let x = (5 + 6 / (1 + (2 / 1) + 4));
return;
}
"#
.into(),
&handler,
&engines,
None,
ExperimentalFeatures::default(),
);
prog.unwrap();
}
#[test]
fn test_unary_ordering() {
use crate::language::{self, parsed};
let handler = Handler::default();
let engines = Engines::default();
let prog = parse(
r#"
script;
fn main() -> bool {
let a = true;
let b = true;
!a && b;
}"#
.into(),
&handler,
&engines,
None,
ExperimentalFeatures::default(),
);
let (.., prog) = prog.unwrap();
if let parsed::AstNode {
content:
parsed::AstNodeContent::Declaration(parsed::Declaration::FunctionDeclaration(decl_id)),
..
} = &prog.root.tree.root_nodes[0]
{
let fn_decl = engines.pe().get_function(decl_id);
if let parsed::AstNode {
content:
parsed::AstNodeContent::Expression(parsed::Expression {
kind:
parsed::ExpressionKind::LazyOperator(parsed::LazyOperatorExpression {
op, ..
}),
..
}),
..
} = &fn_decl.body.contents[2]
{
assert_eq!(op, &language::LazyOp::And)
} else {
panic!("Was not lazy operator.")
}
} else {
panic!("Was not ast node")
};
}
#[test]
fn test_parser_recovery() {
let handler = Handler::default();
let engines = Engines::default();
let prog = parse(
r#"
script;
fn main() -> bool {
let
let a = true;
true
}"#
.into(),
&handler,
&engines,
None,
ExperimentalFeatures::default(),
);
let (_, _) = prog.unwrap();
assert!(handler.has_errors());
dbg!(handler);
}