use sway_error::error::CompileError;
use sway_types::Span;
use crate::{
asm_lang::{
allocated_ops::{AllocatedOp, AllocatedOpcode},
VirtualImmediate18,
},
error::*,
};
pub(crate) fn check_script_opcodes(ops: &[AllocatedOp]) -> CompileResult<()> {
use AllocatedOpcode::*;
let mut errors = vec![];
for op in ops {
match op.opcode {
GM(_, VirtualImmediate18 { value: 1..=2 }) => {
errors.push(CompileError::GMFromExternalContext {
span: get_op_span(op),
});
}
MINT(..) => {
errors.push(CompileError::MintFromExternalContext {
span: get_op_span(op),
});
}
BURN(..) => {
errors.push(CompileError::BurnFromExternalContext {
span: get_op_span(op),
});
}
SWW(..) | SRW(..) | SRWQ(..) | SWWQ(..) => {
errors.push(CompileError::ContractStorageFromExternalContext {
span: get_op_span(op),
});
}
_ => (),
}
}
if errors.is_empty() {
ok((), vec![], errors)
} else {
err(vec![], errors)
}
}
pub(crate) fn check_predicate_opcodes(ops: &[AllocatedOp]) -> CompileResult<()> {
use AllocatedOpcode::*;
let mut errors = vec![];
for (op, opcode_addr) in ops.iter().zip(0u32..) {
let invalid_opcode = |name_str: &str, errors: &mut Vec<CompileError>| {
errors.push(CompileError::InvalidOpcodeFromPredicate {
opcode: name_str.to_string(),
span: get_op_span(op),
});
};
let invalid_backward_jump = |name_str: &str, errors: &mut Vec<CompileError>| {
errors.push(CompileError::InvalidBackwardJumpFromPredicate {
opcode: name_str.to_string(),
span: get_op_span(op),
});
};
match op.opcode.clone() {
BAL(..) => invalid_opcode("BAL", &mut errors),
BHEI(..) => invalid_opcode("BHEI", &mut errors),
BHSH(..) => invalid_opcode("BHSH", &mut errors),
BURN(..) => invalid_opcode("BURN", &mut errors),
CALL(..) => invalid_opcode("CALL", &mut errors),
CB(..) => invalid_opcode("CB", &mut errors),
CCP(..) => invalid_opcode("CCP", &mut errors),
CROO(..) => invalid_opcode("CROO", &mut errors),
CSIZ(..) => invalid_opcode("CSIZ", &mut errors),
GM(_, VirtualImmediate18 { value: 1..=2 }) => {
errors.push(CompileError::GMFromExternalContext {
span: get_op_span(op),
});
}
JI(imm) if imm.value <= opcode_addr => invalid_backward_jump("JI", &mut errors),
JMP(..) => invalid_opcode("JMP", &mut errors),
JNE(..) => invalid_opcode("JNE", &mut errors),
JNEI(_, _, imm) if u32::from(imm.value) <= opcode_addr => {
invalid_backward_jump("JNEI", &mut errors)
}
JNZI(_, imm) if imm.value <= opcode_addr => invalid_backward_jump("JNZI", &mut errors),
LDC(..) => invalid_opcode("LDC", &mut errors),
LOG(..) => invalid_opcode("LOG", &mut errors),
LOGD(..) => invalid_opcode("LOGD", &mut errors),
MINT(..) => invalid_opcode("MINT", &mut errors),
RETD(..) => invalid_opcode("RETD", &mut errors),
SMO(..) => invalid_opcode("SMO", &mut errors),
SRW(..) => invalid_opcode("SRW", &mut errors),
SRWQ(..) => invalid_opcode("SRWQ", &mut errors),
SWW(..) => invalid_opcode("SWW", &mut errors),
SWWQ(..) => invalid_opcode("SWWQ", &mut errors),
TIME(..) => invalid_opcode("TIME", &mut errors),
TR(..) => invalid_opcode("TR", &mut errors),
TRO(..) => invalid_opcode("TRO", &mut errors),
_ => (),
};
}
if errors.is_empty() {
ok((), vec![], errors)
} else {
err(vec![], errors)
}
}
fn get_op_span(op: &AllocatedOp) -> Span {
let default_span =
sway_types::span::Span::new("no span found for opcode".into(), 0, 1, None).unwrap();
op.owning_span
.clone()
.unwrap_or_else(|| default_span.clone())
}