1use super::*;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
4#[serde(deny_unknown_fields)]
5pub struct CompileOptions {
6 pub include_source_info: bool,
9}
10
11impl Default for CompileOptions {
12 fn default() -> Self {
13 Self {
14 include_source_info: true,
15 }
16 }
17}
18
19#[derive(Debug, Clone, PartialEq, Eq)]
20pub struct Compilation {
21 pub(super) contract: Contract,
22 pub(super) source_info: Option<SourceInfo>,
23}
24
25impl Compilation {
26 pub fn contract(&self) -> &Contract {
27 &self.contract
28 }
29
30 pub fn source_info(&self) -> Option<&SourceInfo> {
31 self.source_info.as_ref()
32 }
33
34 pub fn into_parts(self) -> (Contract, Option<SourceInfo>) {
35 (self.contract, self.source_info)
36 }
37
38 pub fn try_from_raw(
39 raw_contract: RawContract,
40 raw_source_info: Option<SerializedSourceInfo>,
41 limits: &crate::Limits,
42 ) -> Result<Self, crate::ContractValidationError> {
43 let context = crate::RuntimeContext::new(limits.clone());
44 Self::try_from_raw_with_context(raw_contract, raw_source_info, &context)
45 }
46
47 pub fn try_from_raw_with_context(
48 raw_contract: RawContract,
49 raw_source_info: Option<SerializedSourceInfo>,
50 context: &crate::RuntimeContext,
51 ) -> Result<Self, crate::ContractValidationError> {
52 let mut budget = context.budget();
53 Self::try_from_raw_with_budget(raw_contract, raw_source_info, &mut budget)
54 }
55
56 pub(crate) fn try_from_raw_with_budget(
57 raw_contract: RawContract,
58 raw_source_info: Option<SerializedSourceInfo>,
59 budget: &mut crate::budget::Budget<'_>,
60 ) -> Result<Self, crate::ContractValidationError> {
61 let (contract, mapping) = Contract::from_raw_with_mapping_and_budget(raw_contract, budget)?;
62 let source_info = raw_source_info
63 .map(SourceInfo::from_raw_unchecked)
64 .map(|mut source_info| {
65 remap_source_info(&mut source_info, &mapping, budget)?;
66 source_info.validate_with_budget(&contract, budget)?;
67 Ok::<SourceInfo, crate::ContractValidationError>(source_info)
68 })
69 .transpose()?;
70 Ok(Self {
71 contract,
72 source_info,
73 })
74 }
75
76 pub fn from_json_with_limits(
82 input: &str,
83 limits: &crate::Limits,
84 ) -> Result<Self, crate::ContractJsonError> {
85 Self::from_json_with_context(input, &crate::RuntimeContext::new(limits.clone()))
86 }
87
88 pub fn from_json_with_context(
90 input: &str,
91 context: &crate::RuntimeContext,
92 ) -> Result<Self, crate::ContractJsonError> {
93 let mut budget = context.budget();
94 let raw: RawCompilation = crate::budget::decode_bounded(&mut budget, input.len(), || {
95 serde_json::from_str(input)
96 })?;
97 Self::try_from_raw_with_budget(raw.contract, raw.source_info, &mut budget)
98 .map_err(crate::ContractJsonError::InvalidContract)
99 }
100
101 pub fn from_slice_with_limits(
103 input: &[u8],
104 limits: &crate::Limits,
105 ) -> Result<Self, crate::ContractJsonError> {
106 Self::from_slice_with_context(input, &crate::RuntimeContext::new(limits.clone()))
107 }
108
109 pub fn from_slice_with_context(
110 input: &[u8],
111 context: &crate::RuntimeContext,
112 ) -> Result<Self, crate::ContractJsonError> {
113 let mut budget = context.budget();
114 let raw: RawCompilation = crate::budget::decode_bounded(&mut budget, input.len(), || {
115 serde_json::from_slice(input)
116 })?;
117 Self::try_from_raw_with_budget(raw.contract, raw.source_info, &mut budget)
118 .map_err(crate::ContractJsonError::InvalidContract)
119 }
120
121 pub fn to_json_pretty_with_limits(
128 &self,
129 limits: &crate::Limits,
130 ) -> Result<String, crate::ContractValidationError> {
131 self.to_json_pretty_with_context(&crate::RuntimeContext::new(limits.clone()))
132 }
133
134 pub fn to_json_pretty_with_context(
135 &self,
136 context: &crate::RuntimeContext,
137 ) -> Result<String, crate::ContractValidationError> {
138 let mut budget = context.budget();
139 crate::validate::validate_contract_with_budget(&self.contract, &mut budget)?;
147 budget
148 .checkpoint()
149 .map_err(crate::budget::BudgetError::into_contract_error)?;
150 let json = serde_json::to_string_pretty(&CompilationRef {
151 contract: &self.contract,
152 source_info: &self.source_info,
153 })
154 .map_err(|error| {
155 crate::ContractValidationError::single(
156 "contract_json_serialization_failed",
157 "$",
158 error.to_string(),
159 )
160 })?;
161 let max_work = budget.limits().max_canonicalization_work;
162 budget
163 .charge("canonicalization_work", max_work, json.len())
164 .map_err(crate::budget::BudgetError::into_contract_error)?;
165 budget
166 .checkpoint()
167 .map_err(crate::budget::BudgetError::into_contract_error)?;
168 Ok(json)
169 }
170}
171
172#[derive(Serialize)]
173#[serde(deny_unknown_fields)]
174struct CompilationRef<'a> {
175 contract: &'a Contract,
176 #[serde(default, skip_serializing_if = "Option::is_none")]
177 source_info: &'a Option<SourceInfo>,
178}
179
180impl Serialize for Compilation {
181 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
182 where
183 S: Serializer,
184 {
185 CompilationRef {
186 contract: &self.contract,
187 source_info: &self.source_info,
188 }
189 .serialize(serializer)
190 }
191}
192
193#[derive(Deserialize)]
204#[serde(deny_unknown_fields)]
205struct RawCompilation {
206 contract: RawContract,
207 #[serde(default)]
208 source_info: Option<SerializedSourceInfo>,
209}
210
211fn remap_source_info(
212 source_info: &mut SourceInfo,
213 mapping: &[TypeRef],
214 budget: &mut crate::budget::Budget<'_>,
215) -> Result<(), crate::ContractValidationError> {
216 crate::source::observe_remapped_collections(source_info, budget)?;
219 let map = |reference: TypeRef| {
220 mapping.get(reference as usize).copied().ok_or_else(|| {
221 crate::ContractValidationError::single(
222 "source_type_ref_out_of_bounds",
223 "$",
224 format!("source sidecar type reference {reference} is outside the input arena"),
225 )
226 })
227 };
228 for declaration in &mut source_info.declarations {
229 budget
230 .checkpoint()
231 .map_err(crate::budget::BudgetError::into_contract_error)?;
232 declaration.ty = map(declaration.ty)?;
233 }
234 for field in &mut source_info.field_labels {
235 budget
236 .checkpoint()
237 .map_err(crate::budget::BudgetError::into_contract_error)?;
238 field.container = map(field.container)?;
239 }
240 for method in &mut source_info.methods {
241 budget
242 .checkpoint()
243 .map_err(crate::budget::BudgetError::into_contract_error)?;
244 method.service = map(method.service)?;
245 }
246 for argument in &mut source_info.function_arguments {
247 budget
248 .checkpoint()
249 .map_err(crate::budget::BudgetError::into_contract_error)?;
250 argument.function = map(argument.function)?;
251 }
252 Ok(())
253}