/src/solidity/libsolidity/interface/CompilerStack.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | This file is part of solidity. |
3 | | |
4 | | solidity is free software: you can redistribute it and/or modify |
5 | | it under the terms of the GNU General Public License as published by |
6 | | the Free Software Foundation, either version 3 of the License, or |
7 | | (at your option) any later version. |
8 | | |
9 | | solidity is distributed in the hope that it will be useful, |
10 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | | GNU General Public License for more details. |
13 | | |
14 | | You should have received a copy of the GNU General Public License |
15 | | along with solidity. If not, see <http://www.gnu.org/licenses/>. |
16 | | */ |
17 | | // SPDX-License-Identifier: GPL-3.0 |
18 | | /** |
19 | | * @author Christian <c@ethdev.com> |
20 | | * @author Gav Wood <g@ethdev.com> |
21 | | * @date 2014 |
22 | | * Full-stack compiler that converts a source code string to bytecode. |
23 | | */ |
24 | | |
25 | | |
26 | | #include <libsolidity/interface/CompilerStack.h> |
27 | | #include <libsolidity/interface/ImportRemapper.h> |
28 | | |
29 | | #include <libsolidity/analysis/ControlFlowAnalyzer.h> |
30 | | #include <libsolidity/analysis/ControlFlowGraph.h> |
31 | | #include <libsolidity/analysis/ControlFlowRevertPruner.h> |
32 | | #include <libsolidity/analysis/ContractLevelChecker.h> |
33 | | #include <libsolidity/analysis/DeclarationTypeChecker.h> |
34 | | #include <libsolidity/analysis/DocStringAnalyser.h> |
35 | | #include <libsolidity/analysis/DocStringTagParser.h> |
36 | | #include <libsolidity/analysis/GlobalContext.h> |
37 | | #include <libsolidity/analysis/NameAndTypeResolver.h> |
38 | | #include <libsolidity/analysis/PostTypeChecker.h> |
39 | | #include <libsolidity/analysis/PostTypeContractLevelChecker.h> |
40 | | #include <libsolidity/analysis/StaticAnalyzer.h> |
41 | | #include <libsolidity/analysis/SyntaxChecker.h> |
42 | | #include <libsolidity/analysis/Scoper.h> |
43 | | #include <libsolidity/analysis/TypeChecker.h> |
44 | | #include <libsolidity/analysis/ViewPureChecker.h> |
45 | | #include <libsolidity/analysis/ImmutableValidator.h> |
46 | | |
47 | | #include <libsolidity/ast/AST.h> |
48 | | #include <libsolidity/ast/TypeProvider.h> |
49 | | #include <libsolidity/ast/ASTJsonImporter.h> |
50 | | #include <libsolidity/codegen/Compiler.h> |
51 | | #include <libsolidity/formal/ModelChecker.h> |
52 | | #include <libsolidity/interface/ABI.h> |
53 | | #include <libsolidity/interface/Natspec.h> |
54 | | #include <libsolidity/interface/GasEstimator.h> |
55 | | #include <libsolidity/interface/StorageLayout.h> |
56 | | #include <libsolidity/interface/Version.h> |
57 | | #include <libsolidity/parsing/Parser.h> |
58 | | |
59 | | #include <libsolidity/codegen/ir/Common.h> |
60 | | #include <libsolidity/codegen/ir/IRGenerator.h> |
61 | | |
62 | | #include <libyul/YulString.h> |
63 | | #include <libyul/AsmPrinter.h> |
64 | | #include <libyul/AsmJsonConverter.h> |
65 | | #include <libyul/YulStack.h> |
66 | | #include <libyul/AST.h> |
67 | | #include <libyul/AsmParser.h> |
68 | | |
69 | | #include <liblangutil/Scanner.h> |
70 | | #include <liblangutil/SemVerHandler.h> |
71 | | |
72 | | #include <libevmasm/Exceptions.h> |
73 | | |
74 | | #include <libsolutil/SwarmHash.h> |
75 | | #include <libsolutil/IpfsHash.h> |
76 | | #include <libsolutil/JSON.h> |
77 | | #include <libsolutil/Algorithms.h> |
78 | | #include <libsolutil/FunctionSelector.h> |
79 | | |
80 | | #include <json/json.h> |
81 | | |
82 | | #include <boost/algorithm/string/replace.hpp> |
83 | | |
84 | | #include <range/v3/view/concat.hpp> |
85 | | |
86 | | #include <utility> |
87 | | #include <map> |
88 | | #include <limits> |
89 | | #include <string> |
90 | | |
91 | | using namespace std; |
92 | | using namespace solidity; |
93 | | using namespace solidity::langutil; |
94 | | using namespace solidity::frontend; |
95 | | |
96 | | using solidity::util::errinfo_comment; |
97 | | using solidity::util::toHex; |
98 | | |
99 | | static int g_compilerStackCounts = 0; |
100 | | |
101 | | CompilerStack::CompilerStack(ReadCallback::Callback _readFile): |
102 | | m_readFile{std::move(_readFile)}, |
103 | | m_errorReporter{m_errorList} |
104 | 4.55k | { |
105 | | // Because TypeProvider is currently a singleton API, we must ensure that |
106 | | // no more than one entity is actually using it at a time. |
107 | 4.55k | solAssert(g_compilerStackCounts == 0, "You shall not have another CompilerStack aside me."); |
108 | 4.55k | ++g_compilerStackCounts; |
109 | 4.55k | } |
110 | | |
111 | | CompilerStack::~CompilerStack() |
112 | 4.55k | { |
113 | 4.55k | --g_compilerStackCounts; |
114 | 4.55k | TypeProvider::reset(); |
115 | 4.55k | } |
116 | | |
117 | | void CompilerStack::createAndAssignCallGraphs() |
118 | 4.55k | { |
119 | 4.55k | for (Source const* source: m_sourceOrder) |
120 | 4.55k | { |
121 | 4.55k | if (!source->ast) |
122 | 0 | continue; |
123 | | |
124 | 4.55k | for (ContractDefinition const* contract: ASTNode::filteredNodes<ContractDefinition>(source->ast->nodes())) |
125 | 4.55k | { |
126 | 4.55k | ContractDefinitionAnnotation& annotation = |
127 | 4.55k | m_contracts.at(contract->fullyQualifiedName()).contract->annotation(); |
128 | | |
129 | 4.55k | annotation.creationCallGraph = make_unique<CallGraph>( |
130 | 4.55k | FunctionCallGraphBuilder::buildCreationGraph(*contract) |
131 | 4.55k | ); |
132 | 4.55k | annotation.deployedCallGraph = make_unique<CallGraph>( |
133 | 4.55k | FunctionCallGraphBuilder::buildDeployedGraph( |
134 | 4.55k | *contract, |
135 | 4.55k | **annotation.creationCallGraph |
136 | 4.55k | ) |
137 | 4.55k | ); |
138 | | |
139 | 4.55k | solAssert(annotation.contractDependencies.empty(), "contractDependencies expected to be empty?!"); |
140 | | |
141 | 4.55k | annotation.contractDependencies = annotation.creationCallGraph->get()->bytecodeDependency; |
142 | | |
143 | 4.55k | for (auto const& [dependencyContract, referencee]: annotation.deployedCallGraph->get()->bytecodeDependency) |
144 | 0 | annotation.contractDependencies.emplace(dependencyContract, referencee); |
145 | 4.55k | } |
146 | 4.55k | } |
147 | 4.55k | } |
148 | | |
149 | | void CompilerStack::findAndReportCyclicContractDependencies() |
150 | 4.55k | { |
151 | | // Cycles we found, used to avoid duplicate reports for the same reference |
152 | 4.55k | set<ASTNode const*, ASTNode::CompareByID> foundCycles; |
153 | | |
154 | 4.55k | for (Source const* source: m_sourceOrder) |
155 | 4.55k | { |
156 | 4.55k | if (!source->ast) |
157 | 0 | continue; |
158 | | |
159 | 4.55k | for (ContractDefinition const* contractDefinition: ASTNode::filteredNodes<ContractDefinition>(source->ast->nodes())) |
160 | 4.55k | { |
161 | 4.55k | util::CycleDetector<ContractDefinition> cycleDetector{[&]( |
162 | 4.55k | ContractDefinition const& _contract, |
163 | 4.55k | util::CycleDetector<ContractDefinition>& _cycleDetector, |
164 | 4.55k | size_t _depth |
165 | 4.55k | ) |
166 | 4.55k | { |
167 | | // No specific reason for exactly that number, just a limit we're unlikely to hit. |
168 | 4.55k | if (_depth >= 256) |
169 | 0 | m_errorReporter.fatalTypeError( |
170 | 0 | 7864_error, |
171 | 0 | _contract.location(), |
172 | 0 | "Contract dependencies exhausting cyclic dependency validator" |
173 | 0 | ); |
174 | | |
175 | 4.55k | for (auto& [dependencyContract, referencee]: _contract.annotation().contractDependencies) |
176 | 0 | if (_cycleDetector.run(*dependencyContract)) |
177 | 0 | return; |
178 | 4.55k | }}; |
179 | | |
180 | 4.55k | ContractDefinition const* cycle = cycleDetector.run(*contractDefinition); |
181 | | |
182 | 4.55k | if (!cycle) |
183 | 4.55k | continue; |
184 | | |
185 | 0 | ASTNode const* referencee = contractDefinition->annotation().contractDependencies.at(cycle); |
186 | |
|
187 | 0 | if (foundCycles.find(referencee) != foundCycles.end()) |
188 | 0 | continue; |
189 | | |
190 | 0 | SecondarySourceLocation secondaryLocation{}; |
191 | 0 | secondaryLocation.append("Referenced contract is here:"s, cycle->location()); |
192 | |
|
193 | 0 | m_errorReporter.typeError( |
194 | 0 | 7813_error, |
195 | 0 | referencee->location(), |
196 | 0 | secondaryLocation, |
197 | 0 | "Circular reference to contract bytecode either via \"new\" or \"type(...).creationCode\" / \"type(...).runtimeCode\"." |
198 | 0 | ); |
199 | |
|
200 | 0 | foundCycles.emplace(referencee); |
201 | 0 | } |
202 | 4.55k | } |
203 | 4.55k | } |
204 | | |
205 | | void CompilerStack::setRemappings(vector<ImportRemapper::Remapping> _remappings) |
206 | 0 | { |
207 | 0 | if (m_stackState >= ParsedAndImported) |
208 | 0 | solThrow(CompilerError, "Must set remappings before parsing."); |
209 | 0 | for (auto const& remapping: _remappings) |
210 | 0 | solAssert(!remapping.prefix.empty(), ""); |
211 | 0 | m_importRemapper.setRemappings(move(_remappings)); |
212 | 0 | } |
213 | | |
214 | | void CompilerStack::setViaIR(bool _viaIR) |
215 | 4.55k | { |
216 | 4.55k | if (m_stackState >= ParsedAndImported) |
217 | 0 | solThrow(CompilerError, "Must set viaIR before parsing."); |
218 | 4.55k | m_viaIR = _viaIR; |
219 | 4.55k | } |
220 | | |
221 | | void CompilerStack::setEVMVersion(langutil::EVMVersion _version) |
222 | 4.55k | { |
223 | 4.55k | if (m_stackState >= ParsedAndImported) |
224 | 0 | solThrow(CompilerError, "Must set EVM version before parsing."); |
225 | 4.55k | m_evmVersion = _version; |
226 | 4.55k | } |
227 | | |
228 | | void CompilerStack::setModelCheckerSettings(ModelCheckerSettings _settings) |
229 | 0 | { |
230 | 0 | if (m_stackState >= ParsedAndImported) |
231 | 0 | solThrow(CompilerError, "Must set model checking settings before parsing."); |
232 | 0 | m_modelCheckerSettings = _settings; |
233 | 0 | } |
234 | | |
235 | | void CompilerStack::setLibraries(std::map<std::string, util::h160> const& _libraries) |
236 | 4.55k | { |
237 | 4.55k | if (m_stackState >= ParsedAndImported) |
238 | 0 | solThrow(CompilerError, "Must set libraries before parsing."); |
239 | 4.55k | m_libraries = _libraries; |
240 | 4.55k | } |
241 | | |
242 | | void CompilerStack::setOptimiserSettings(bool _optimize, size_t _runs) |
243 | 0 | { |
244 | 0 | OptimiserSettings settings = _optimize ? OptimiserSettings::standard() : OptimiserSettings::minimal(); |
245 | 0 | settings.expectedExecutionsPerDeployment = _runs; |
246 | 0 | setOptimiserSettings(std::move(settings)); |
247 | 0 | } |
248 | | |
249 | | void CompilerStack::setOptimiserSettings(OptimiserSettings _settings) |
250 | 4.55k | { |
251 | 4.55k | if (m_stackState >= ParsedAndImported) |
252 | 0 | solThrow(CompilerError, "Must set optimiser settings before parsing."); |
253 | 4.55k | m_optimiserSettings = std::move(_settings); |
254 | 4.55k | } |
255 | | |
256 | | void CompilerStack::setRevertStringBehaviour(RevertStrings _revertStrings) |
257 | 0 | { |
258 | 0 | if (m_stackState >= ParsedAndImported) |
259 | 0 | solThrow(CompilerError, "Must set revert string settings before parsing."); |
260 | 0 | solUnimplementedAssert(_revertStrings != RevertStrings::VerboseDebug); |
261 | 0 | m_revertStrings = _revertStrings; |
262 | 0 | } |
263 | | |
264 | | void CompilerStack::useMetadataLiteralSources(bool _metadataLiteralSources) |
265 | 0 | { |
266 | 0 | if (m_stackState >= ParsedAndImported) |
267 | 0 | solThrow(CompilerError, "Must set use literal sources before parsing."); |
268 | 0 | m_metadataLiteralSources = _metadataLiteralSources; |
269 | 0 | } |
270 | | |
271 | | void CompilerStack::setMetadataHash(MetadataHash _metadataHash) |
272 | 0 | { |
273 | 0 | if (m_stackState >= ParsedAndImported) |
274 | 0 | solThrow(CompilerError, "Must set metadata hash before parsing."); |
275 | 0 | m_metadataHash = _metadataHash; |
276 | 0 | } |
277 | | |
278 | | void CompilerStack::selectDebugInfo(DebugInfoSelection _debugInfoSelection) |
279 | 0 | { |
280 | 0 | if (m_stackState >= CompilationSuccessful) |
281 | 0 | BOOST_THROW_EXCEPTION(CompilerError() << util::errinfo_comment("Must select debug info components before compilation.")); |
282 | 0 | m_debugInfoSelection = _debugInfoSelection; |
283 | 0 | } |
284 | | |
285 | | void CompilerStack::addSMTLib2Response(h256 const& _hash, string const& _response) |
286 | 0 | { |
287 | 0 | if (m_stackState >= ParsedAndImported) |
288 | 0 | solThrow(CompilerError, "Must add SMTLib2 responses before parsing."); |
289 | 0 | m_smtlib2Responses[_hash] = _response; |
290 | 0 | } |
291 | | |
292 | | void CompilerStack::reset(bool _keepSettings) |
293 | 0 | { |
294 | 0 | m_stackState = Empty; |
295 | 0 | m_hasError = false; |
296 | 0 | m_sources.clear(); |
297 | 0 | m_smtlib2Responses.clear(); |
298 | 0 | m_unhandledSMTLib2Queries.clear(); |
299 | 0 | if (!_keepSettings) |
300 | 0 | { |
301 | 0 | m_importRemapper.clear(); |
302 | 0 | m_libraries.clear(); |
303 | 0 | m_viaIR = false; |
304 | 0 | m_evmVersion = langutil::EVMVersion(); |
305 | 0 | m_modelCheckerSettings = ModelCheckerSettings{}; |
306 | 0 | m_generateIR = false; |
307 | 0 | m_generateEwasm = false; |
308 | 0 | m_revertStrings = RevertStrings::Default; |
309 | 0 | m_optimiserSettings = OptimiserSettings::minimal(); |
310 | 0 | m_metadataLiteralSources = false; |
311 | 0 | m_metadataHash = MetadataHash::IPFS; |
312 | 0 | m_stopAfter = State::CompilationSuccessful; |
313 | 0 | } |
314 | 0 | m_globalContext.reset(); |
315 | 0 | m_sourceOrder.clear(); |
316 | 0 | m_contracts.clear(); |
317 | 0 | m_errorReporter.clear(); |
318 | 0 | TypeProvider::reset(); |
319 | 0 | } |
320 | | |
321 | | void CompilerStack::setSources(StringMap _sources) |
322 | 4.55k | { |
323 | 4.55k | if (m_stackState == SourcesSet) |
324 | 0 | solThrow(CompilerError, "Cannot change sources once set."); |
325 | 4.55k | if (m_stackState != Empty) |
326 | 0 | solThrow(CompilerError, "Must set sources before parsing."); |
327 | 4.55k | for (auto source: _sources) |
328 | 4.55k | m_sources[source.first].charStream = make_unique<CharStream>(/*content*/std::move(source.second), /*name*/source.first); |
329 | 4.55k | m_stackState = SourcesSet; |
330 | 4.55k | } |
331 | | |
332 | | bool CompilerStack::parse() |
333 | 4.55k | { |
334 | 4.55k | if (m_stackState != SourcesSet) |
335 | 0 | solThrow(CompilerError, "Must call parse only after the SourcesSet state."); |
336 | 4.55k | m_errorReporter.clear(); |
337 | | |
338 | 4.55k | if (SemVerVersion{string(VersionString)}.isPrerelease()) |
339 | 4.55k | m_errorReporter.warning(3805_error, "This is a pre-release compiler version, please do not use it in production."); |
340 | | |
341 | 4.55k | Parser parser{m_errorReporter, m_evmVersion, m_parserErrorRecovery}; |
342 | | |
343 | 4.55k | vector<string> sourcesToParse; |
344 | 4.55k | for (auto const& s: m_sources) |
345 | 4.55k | sourcesToParse.push_back(s.first); |
346 | | |
347 | 9.10k | for (size_t i = 0; i < sourcesToParse.size(); ++i) |
348 | 4.55k | { |
349 | 4.55k | string const& path = sourcesToParse[i]; |
350 | 4.55k | Source& source = m_sources[path]; |
351 | 4.55k | source.ast = parser.parse(*source.charStream); |
352 | 4.55k | if (!source.ast) |
353 | 4.55k | solAssert(Error::containsErrors(m_errorReporter.errors()), "Parser returned null but did not report error."); |
354 | 4.55k | else |
355 | 4.55k | { |
356 | 4.55k | source.ast->annotation().path = path; |
357 | | |
358 | 4.55k | for (auto const& import: ASTNode::filteredNodes<ImportDirective>(source.ast->nodes())) |
359 | 0 | { |
360 | 0 | solAssert(!import->path().empty(), "Import path cannot be empty."); |
361 | | |
362 | | // The current value of `path` is the absolute path as seen from this source file. |
363 | | // We first have to apply remappings before we can store the actual absolute path |
364 | | // as seen globally. |
365 | 0 | import->annotation().absolutePath = applyRemapping(util::absolutePath( |
366 | 0 | import->path(), |
367 | 0 | path |
368 | 0 | ), path); |
369 | 0 | } |
370 | | |
371 | 4.55k | if (m_stopAfter >= ParsedAndImported) |
372 | 4.55k | for (auto const& newSource: loadMissingSources(*source.ast)) |
373 | 0 | { |
374 | 0 | string const& newPath = newSource.first; |
375 | 0 | string const& newContents = newSource.second; |
376 | 0 | m_sources[newPath].charStream = make_shared<CharStream>(newContents, newPath); |
377 | 0 | sourcesToParse.push_back(newPath); |
378 | 0 | } |
379 | 4.55k | } |
380 | 4.55k | } |
381 | | |
382 | 4.55k | if (m_stopAfter <= Parsed) |
383 | 0 | m_stackState = Parsed; |
384 | 4.55k | else |
385 | 4.55k | m_stackState = ParsedAndImported; |
386 | 4.55k | if (Error::containsErrors(m_errorReporter.errors())) |
387 | 0 | m_hasError = true; |
388 | | |
389 | 4.55k | storeContractDefinitions(); |
390 | | |
391 | 4.55k | return !m_hasError; |
392 | 4.55k | } |
393 | | |
394 | | void CompilerStack::importASTs(map<string, Json::Value> const& _sources) |
395 | 0 | { |
396 | 0 | if (m_stackState != Empty) |
397 | 0 | solThrow(CompilerError, "Must call importASTs only before the SourcesSet state."); |
398 | 0 | m_sourceJsons = _sources; |
399 | 0 | map<string, ASTPointer<SourceUnit>> reconstructedSources = ASTJsonImporter(m_evmVersion).jsonToSourceUnit(m_sourceJsons); |
400 | 0 | for (auto& src: reconstructedSources) |
401 | 0 | { |
402 | 0 | string const& path = src.first; |
403 | 0 | Source source; |
404 | 0 | source.ast = src.second; |
405 | 0 | source.charStream = make_shared<CharStream>( |
406 | 0 | util::jsonCompactPrint(m_sourceJsons[src.first]), |
407 | 0 | src.first, |
408 | 0 | true // imported from AST |
409 | 0 | ); |
410 | 0 | m_sources[path] = move(source); |
411 | 0 | } |
412 | 0 | m_stackState = ParsedAndImported; |
413 | 0 | m_importedSources = true; |
414 | |
|
415 | 0 | storeContractDefinitions(); |
416 | 0 | } |
417 | | |
418 | | bool CompilerStack::analyze() |
419 | 4.55k | { |
420 | 4.55k | if (m_stackState != ParsedAndImported || m_stackState >= AnalysisPerformed) |
421 | 0 | solThrow(CompilerError, "Must call analyze only after parsing was performed."); |
422 | 4.55k | resolveImports(); |
423 | | |
424 | 4.55k | for (Source const* source: m_sourceOrder) |
425 | 4.55k | if (source->ast) |
426 | 4.55k | Scoper::assignScopes(*source->ast); |
427 | | |
428 | 4.55k | bool noErrors = true; |
429 | | |
430 | 4.55k | try |
431 | 4.55k | { |
432 | 4.55k | SyntaxChecker syntaxChecker(m_errorReporter, m_optimiserSettings.runYulOptimiser); |
433 | 4.55k | for (Source const* source: m_sourceOrder) |
434 | 4.55k | if (source->ast && !syntaxChecker.checkSyntax(*source->ast)) |
435 | 0 | noErrors = false; |
436 | | |
437 | 4.55k | m_globalContext = make_shared<GlobalContext>(); |
438 | | // We need to keep the same resolver during the whole process. |
439 | 4.55k | NameAndTypeResolver resolver(*m_globalContext, m_evmVersion, m_errorReporter); |
440 | 4.55k | for (Source const* source: m_sourceOrder) |
441 | 4.55k | if (source->ast && !resolver.registerDeclarations(*source->ast)) |
442 | 0 | return false; |
443 | | |
444 | 4.55k | map<string, SourceUnit const*> sourceUnitsByName; |
445 | 4.55k | for (auto& source: m_sources) |
446 | 4.55k | sourceUnitsByName[source.first] = source.second.ast.get(); |
447 | 4.55k | for (Source const* source: m_sourceOrder) |
448 | 4.55k | if (source->ast && !resolver.performImports(*source->ast, sourceUnitsByName)) |
449 | 0 | return false; |
450 | | |
451 | 4.55k | resolver.warnHomonymDeclarations(); |
452 | | |
453 | 4.55k | DocStringTagParser docStringTagParser(m_errorReporter); |
454 | 4.55k | for (Source const* source: m_sourceOrder) |
455 | 4.55k | if (source->ast && !docStringTagParser.parseDocStrings(*source->ast)) |
456 | 0 | noErrors = false; |
457 | | |
458 | | // Requires DocStringTagParser |
459 | 4.55k | for (Source const* source: m_sourceOrder) |
460 | 4.55k | if (source->ast && !resolver.resolveNamesAndTypes(*source->ast)) |
461 | 0 | return false; |
462 | | |
463 | 4.55k | DeclarationTypeChecker declarationTypeChecker(m_errorReporter, m_evmVersion); |
464 | 4.55k | for (Source const* source: m_sourceOrder) |
465 | 4.55k | if (source->ast && !declarationTypeChecker.check(*source->ast)) |
466 | 0 | return false; |
467 | | |
468 | | // Requires DeclarationTypeChecker to have run |
469 | 4.55k | for (Source const* source: m_sourceOrder) |
470 | 4.55k | if (source->ast && !docStringTagParser.validateDocStringsUsingTypes(*source->ast)) |
471 | 0 | noErrors = false; |
472 | | |
473 | | // Next, we check inheritance, overrides, function collisions and other things at |
474 | | // contract or function level. |
475 | | // This also calculates whether a contract is abstract, which is needed by the |
476 | | // type checker. |
477 | 4.55k | ContractLevelChecker contractLevelChecker(m_errorReporter); |
478 | | |
479 | 4.55k | for (Source const* source: m_sourceOrder) |
480 | 4.55k | if (auto sourceAst = source->ast) |
481 | 4.55k | noErrors = contractLevelChecker.check(*sourceAst); |
482 | | |
483 | | // Now we run full type checks that go down to the expression level. This |
484 | | // cannot be done earlier, because we need cross-contract types and information |
485 | | // about whether a contract is abstract for the `new` expression. |
486 | | // This populates the `type` annotation for all expressions. |
487 | | // |
488 | | // Note: this does not resolve overloaded functions. In order to do that, types of arguments are needed, |
489 | | // which is only done one step later. |
490 | 4.55k | TypeChecker typeChecker(m_evmVersion, m_errorReporter); |
491 | 4.55k | for (Source const* source: m_sourceOrder) |
492 | 4.55k | if (source->ast && !typeChecker.checkTypeRequirements(*source->ast)) |
493 | 0 | noErrors = false; |
494 | | |
495 | 4.55k | if (noErrors) |
496 | 4.55k | { |
497 | | // Requires ContractLevelChecker and TypeChecker |
498 | 4.55k | DocStringAnalyser docStringAnalyser(m_errorReporter); |
499 | 4.55k | for (Source const* source: m_sourceOrder) |
500 | 4.55k | if (source->ast && !docStringAnalyser.analyseDocStrings(*source->ast)) |
501 | 0 | noErrors = false; |
502 | 4.55k | } |
503 | | |
504 | 4.55k | if (noErrors) |
505 | 4.55k | { |
506 | | // Checks that can only be done when all types of all AST nodes are known. |
507 | 4.55k | PostTypeChecker postTypeChecker(m_errorReporter); |
508 | 4.55k | for (Source const* source: m_sourceOrder) |
509 | 4.55k | if (source->ast && !postTypeChecker.check(*source->ast)) |
510 | 0 | noErrors = false; |
511 | 4.55k | if (!postTypeChecker.finalize()) |
512 | 0 | noErrors = false; |
513 | 4.55k | } |
514 | | |
515 | | // Create & assign callgraphs and check for contract dependency cycles |
516 | 4.55k | if (noErrors) |
517 | 4.55k | { |
518 | 4.55k | createAndAssignCallGraphs(); |
519 | 4.55k | findAndReportCyclicContractDependencies(); |
520 | 4.55k | } |
521 | | |
522 | 4.55k | if (noErrors) |
523 | 4.55k | for (Source const* source: m_sourceOrder) |
524 | 4.55k | if (source->ast && !PostTypeContractLevelChecker{m_errorReporter}.check(*source->ast)) |
525 | 0 | noErrors = false; |
526 | | |
527 | | // Check that immutable variables are never read in c'tors and assigned |
528 | | // exactly once |
529 | 4.55k | if (noErrors) |
530 | 4.55k | for (Source const* source: m_sourceOrder) |
531 | 4.55k | if (source->ast) |
532 | 4.55k | for (ASTPointer<ASTNode> const& node: source->ast->nodes()) |
533 | 13.6k | if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get())) |
534 | 4.55k | ImmutableValidator(m_errorReporter, *contract).analyze(); |
535 | | |
536 | 4.55k | if (noErrors) |
537 | 4.55k | { |
538 | | // Control flow graph generator and analyzer. It can check for issues such as |
539 | | // variable is used before it is assigned to. |
540 | 4.55k | CFG cfg(m_errorReporter); |
541 | 4.55k | for (Source const* source: m_sourceOrder) |
542 | 4.55k | if (source->ast && !cfg.constructFlow(*source->ast)) |
543 | 0 | noErrors = false; |
544 | | |
545 | 4.55k | if (noErrors) |
546 | 4.55k | { |
547 | 4.55k | ControlFlowRevertPruner pruner(cfg); |
548 | 4.55k | pruner.run(); |
549 | | |
550 | 4.55k | ControlFlowAnalyzer controlFlowAnalyzer(cfg, m_errorReporter); |
551 | 4.55k | if (!controlFlowAnalyzer.run()) |
552 | 0 | noErrors = false; |
553 | 4.55k | } |
554 | 4.55k | } |
555 | | |
556 | 4.55k | if (noErrors) |
557 | 4.55k | { |
558 | | // Checks for common mistakes. Only generates warnings. |
559 | 4.55k | StaticAnalyzer staticAnalyzer(m_errorReporter); |
560 | 4.55k | for (Source const* source: m_sourceOrder) |
561 | 4.55k | if (source->ast && !staticAnalyzer.analyze(*source->ast)) |
562 | 0 | noErrors = false; |
563 | 4.55k | } |
564 | | |
565 | 4.55k | if (noErrors) |
566 | 4.55k | { |
567 | | // Check for state mutability in every function. |
568 | 4.55k | vector<ASTPointer<ASTNode>> ast; |
569 | 4.55k | for (Source const* source: m_sourceOrder) |
570 | 4.55k | if (source->ast) |
571 | 4.55k | ast.push_back(source->ast); |
572 | | |
573 | 4.55k | if (!ViewPureChecker(ast, m_errorReporter).check()) |
574 | 0 | noErrors = false; |
575 | 4.55k | } |
576 | | |
577 | 4.55k | if (noErrors) |
578 | 4.55k | { |
579 | 4.55k | ModelChecker modelChecker(m_errorReporter, *this, m_smtlib2Responses, m_modelCheckerSettings, m_readFile); |
580 | 4.55k | auto allSources = util::applyMap(m_sourceOrder, [](Source const* _source) { return _source->ast; }); |
581 | 4.55k | modelChecker.enableAllEnginesIfPragmaPresent(allSources); |
582 | 4.55k | modelChecker.checkRequestedSourcesAndContracts(allSources); |
583 | 4.55k | for (Source const* source: m_sourceOrder) |
584 | 4.55k | if (source->ast) |
585 | 4.55k | modelChecker.analyze(*source->ast); |
586 | 4.55k | m_unhandledSMTLib2Queries += modelChecker.unhandledQueries(); |
587 | 4.55k | } |
588 | 4.55k | } |
589 | 4.55k | catch (FatalError const&) |
590 | 4.55k | { |
591 | 0 | if (m_errorReporter.errors().empty()) |
592 | 0 | throw; // Something is weird here, rather throw again. |
593 | 0 | noErrors = false; |
594 | 0 | } |
595 | | |
596 | 4.55k | m_stackState = AnalysisPerformed; |
597 | 4.55k | if (!noErrors) |
598 | 0 | m_hasError = true; |
599 | | |
600 | 4.55k | return !m_hasError; |
601 | 4.55k | } |
602 | | |
603 | | bool CompilerStack::parseAndAnalyze(State _stopAfter) |
604 | 4.55k | { |
605 | 4.55k | m_stopAfter = _stopAfter; |
606 | | |
607 | 4.55k | bool success = parse(); |
608 | 4.55k | if (m_stackState >= m_stopAfter) |
609 | 0 | return success; |
610 | 4.55k | if (success || m_parserErrorRecovery) |
611 | 4.55k | success = analyze(); |
612 | 4.55k | return success; |
613 | 4.55k | } |
614 | | |
615 | | bool CompilerStack::isRequestedSource(string const& _sourceName) const |
616 | 4.55k | { |
617 | 4.55k | return |
618 | 4.55k | m_requestedContractNames.empty() || |
619 | 4.55k | m_requestedContractNames.count("") || |
620 | 4.55k | m_requestedContractNames.count(_sourceName); |
621 | 4.55k | } |
622 | | |
623 | | bool CompilerStack::isRequestedContract(ContractDefinition const& _contract) const |
624 | 4.55k | { |
625 | | /// In case nothing was specified in outputSelection. |
626 | 4.55k | if (m_requestedContractNames.empty()) |
627 | 4.55k | return true; |
628 | | |
629 | 0 | for (auto const& key: vector<string>{"", _contract.sourceUnitName()}) |
630 | 0 | { |
631 | 0 | auto const& it = m_requestedContractNames.find(key); |
632 | 0 | if (it != m_requestedContractNames.end()) |
633 | 0 | if (it->second.count(_contract.name()) || it->second.count("")) |
634 | 0 | return true; |
635 | 0 | } |
636 | | |
637 | 0 | return false; |
638 | 0 | } |
639 | | |
640 | | bool CompilerStack::compile(State _stopAfter) |
641 | 4.55k | { |
642 | 4.55k | m_stopAfter = _stopAfter; |
643 | 4.55k | if (m_stackState < AnalysisPerformed) |
644 | 4.55k | if (!parseAndAnalyze(_stopAfter)) |
645 | 0 | return false; |
646 | | |
647 | 4.55k | if (m_stackState >= m_stopAfter) |
648 | 0 | return true; |
649 | | |
650 | 4.55k | if (m_hasError) |
651 | 0 | solThrow(CompilerError, "Called compile with errors."); |
652 | | |
653 | | // Only compile contracts individually which have been requested. |
654 | 4.55k | map<ContractDefinition const*, shared_ptr<Compiler const>> otherCompilers; |
655 | | |
656 | 4.55k | for (Source const* source: m_sourceOrder) |
657 | 4.55k | for (ASTPointer<ASTNode> const& node: source->ast->nodes()) |
658 | 13.6k | if (auto contract = dynamic_cast<ContractDefinition const*>(node.get())) |
659 | 4.55k | if (isRequestedContract(*contract)) |
660 | 4.55k | { |
661 | 4.55k | try |
662 | 4.55k | { |
663 | 4.55k | if (m_viaIR || m_generateIR || m_generateEwasm) |
664 | 0 | generateIR(*contract); |
665 | 4.55k | if (m_generateEvmBytecode) |
666 | 4.55k | { |
667 | 4.55k | if (m_viaIR) |
668 | 0 | generateEVMFromIR(*contract); |
669 | 4.55k | else |
670 | 4.55k | compileContract(*contract, otherCompilers); |
671 | 4.55k | } |
672 | 4.55k | if (m_generateEwasm) |
673 | 0 | generateEwasm(*contract); |
674 | 4.55k | } |
675 | 4.55k | catch (Error const& _error) |
676 | 4.55k | { |
677 | 0 | if (_error.type() != Error::Type::CodeGenerationError) |
678 | 0 | throw; |
679 | 0 | m_errorReporter.error(_error.errorId(), _error.type(), SourceLocation(), _error.what()); |
680 | 0 | return false; |
681 | 0 | } |
682 | 4.55k | catch (UnimplementedFeatureError const& _unimplementedError) |
683 | 4.55k | { |
684 | 0 | if ( |
685 | 0 | SourceLocation const* sourceLocation = |
686 | 0 | boost::get_error_info<langutil::errinfo_sourceLocation>(_unimplementedError) |
687 | 0 | ) |
688 | 0 | { |
689 | 0 | string const* comment = _unimplementedError.comment(); |
690 | 0 | m_errorReporter.error( |
691 | 0 | 1834_error, |
692 | 0 | Error::Type::CodeGenerationError, |
693 | 0 | *sourceLocation, |
694 | 0 | "Unimplemented feature error" + |
695 | 0 | ((comment && !comment->empty()) ? ": " + *comment : string{}) + |
696 | 0 | " in " + |
697 | 0 | _unimplementedError.lineInfo() |
698 | 0 | ); |
699 | 0 | return false; |
700 | 0 | } |
701 | 0 | else |
702 | 0 | throw; |
703 | 0 | } |
704 | 4.55k | } |
705 | 4.55k | m_stackState = CompilationSuccessful; |
706 | 4.55k | this->link(); |
707 | 4.55k | return true; |
708 | 4.55k | } |
709 | | |
710 | | void CompilerStack::link() |
711 | 4.55k | { |
712 | 4.55k | solAssert(m_stackState >= CompilationSuccessful, ""); |
713 | 4.55k | for (auto& contract: m_contracts) |
714 | 4.55k | { |
715 | 4.55k | contract.second.object.link(m_libraries); |
716 | 4.55k | contract.second.runtimeObject.link(m_libraries); |
717 | 4.55k | } |
718 | 4.55k | } |
719 | | |
720 | | vector<string> CompilerStack::contractNames() const |
721 | 0 | { |
722 | 0 | if (m_stackState < Parsed) |
723 | 0 | solThrow(CompilerError, "Parsing was not successful."); |
724 | 0 | vector<string> contractNames; |
725 | 0 | for (auto const& contract: m_contracts) |
726 | 0 | contractNames.push_back(contract.first); |
727 | 0 | return contractNames; |
728 | 0 | } |
729 | | |
730 | | string const CompilerStack::lastContractName(optional<string> const& _sourceName) const |
731 | 0 | { |
732 | 0 | if (m_stackState < AnalysisPerformed) |
733 | 0 | solThrow(CompilerError, "Parsing was not successful."); |
734 | | // try to find some user-supplied contract |
735 | 0 | string contractName; |
736 | 0 | for (auto const& it: m_sources) |
737 | 0 | if (_sourceName.value_or(it.first) == it.first) |
738 | 0 | for (auto const* contract: ASTNode::filteredNodes<ContractDefinition>(it.second.ast->nodes())) |
739 | 0 | contractName = contract->fullyQualifiedName(); |
740 | 0 | return contractName; |
741 | 0 | } |
742 | | |
743 | | evmasm::AssemblyItems const* CompilerStack::assemblyItems(string const& _contractName) const |
744 | 0 | { |
745 | 0 | if (m_stackState != CompilationSuccessful) |
746 | 0 | solThrow(CompilerError, "Compilation was not successful."); |
747 | | |
748 | 0 | Contract const& currentContract = contract(_contractName); |
749 | 0 | return currentContract.evmAssembly ? ¤tContract.evmAssembly->items() : nullptr; |
750 | 0 | } |
751 | | |
752 | | evmasm::AssemblyItems const* CompilerStack::runtimeAssemblyItems(string const& _contractName) const |
753 | 0 | { |
754 | 0 | if (m_stackState != CompilationSuccessful) |
755 | 0 | solThrow(CompilerError, "Compilation was not successful."); |
756 | | |
757 | 0 | Contract const& currentContract = contract(_contractName); |
758 | 0 | return currentContract.evmRuntimeAssembly ? ¤tContract.evmRuntimeAssembly->items() : nullptr; |
759 | 0 | } |
760 | | |
761 | | Json::Value CompilerStack::generatedSources(string const& _contractName, bool _runtime) const |
762 | 0 | { |
763 | 0 | if (m_stackState != CompilationSuccessful) |
764 | 0 | solThrow(CompilerError, "Compilation was not successful."); |
765 | | |
766 | 0 | Contract const& c = contract(_contractName); |
767 | 0 | util::LazyInit<Json::Value const> const& sources = |
768 | 0 | _runtime ? |
769 | 0 | c.runtimeGeneratedSources : |
770 | 0 | c.generatedSources; |
771 | 0 | return sources.init([&]{ |
772 | 0 | Json::Value sources{Json::arrayValue}; |
773 | | // If there is no compiler, then no bytecode was generated and thus no |
774 | | // sources were generated (or we compiled "via IR"). |
775 | 0 | if (c.compiler) |
776 | 0 | { |
777 | 0 | solAssert(!m_viaIR, ""); |
778 | 0 | string source = |
779 | 0 | _runtime ? |
780 | 0 | c.compiler->runtimeGeneratedYulUtilityCode() : |
781 | 0 | c.compiler->generatedYulUtilityCode(); |
782 | 0 | if (!source.empty()) |
783 | 0 | { |
784 | 0 | string sourceName = CompilerContext::yulUtilityFileName(); |
785 | 0 | unsigned sourceIndex = sourceIndices()[sourceName]; |
786 | 0 | ErrorList errors; |
787 | 0 | ErrorReporter errorReporter(errors); |
788 | 0 | CharStream charStream(source, sourceName); |
789 | 0 | yul::EVMDialect const& dialect = yul::EVMDialect::strictAssemblyForEVM(m_evmVersion); |
790 | 0 | shared_ptr<yul::Block> parserResult = yul::Parser{errorReporter, dialect}.parse(charStream); |
791 | 0 | solAssert(parserResult, ""); |
792 | 0 | sources[0]["ast"] = yul::AsmJsonConverter{sourceIndex}(*parserResult); |
793 | 0 | sources[0]["name"] = sourceName; |
794 | 0 | sources[0]["id"] = sourceIndex; |
795 | 0 | sources[0]["language"] = "Yul"; |
796 | 0 | sources[0]["contents"] = move(source); |
797 | |
|
798 | 0 | } |
799 | 0 | } |
800 | 0 | return sources; |
801 | 0 | }); |
802 | 0 | } |
803 | | |
804 | | string const* CompilerStack::sourceMapping(string const& _contractName) const |
805 | 0 | { |
806 | 0 | if (m_stackState != CompilationSuccessful) |
807 | 0 | solThrow(CompilerError, "Compilation was not successful."); |
808 | | |
809 | 0 | Contract const& c = contract(_contractName); |
810 | 0 | if (!c.sourceMapping) |
811 | 0 | { |
812 | 0 | if (auto items = assemblyItems(_contractName)) |
813 | 0 | c.sourceMapping.emplace(evmasm::AssemblyItem::computeSourceMapping(*items, sourceIndices())); |
814 | 0 | } |
815 | 0 | return c.sourceMapping ? &*c.sourceMapping : nullptr; |
816 | 0 | } |
817 | | |
818 | | string const* CompilerStack::runtimeSourceMapping(string const& _contractName) const |
819 | 0 | { |
820 | 0 | if (m_stackState != CompilationSuccessful) |
821 | 0 | solThrow(CompilerError, "Compilation was not successful."); |
822 | | |
823 | 0 | Contract const& c = contract(_contractName); |
824 | 0 | if (!c.runtimeSourceMapping) |
825 | 0 | { |
826 | 0 | if (auto items = runtimeAssemblyItems(_contractName)) |
827 | 0 | c.runtimeSourceMapping.emplace( |
828 | 0 | evmasm::AssemblyItem::computeSourceMapping(*items, sourceIndices()) |
829 | 0 | ); |
830 | 0 | } |
831 | 0 | return c.runtimeSourceMapping ? &*c.runtimeSourceMapping : nullptr; |
832 | 0 | } |
833 | | |
834 | | std::string const CompilerStack::filesystemFriendlyName(string const& _contractName) const |
835 | 0 | { |
836 | 0 | if (m_stackState < AnalysisPerformed) |
837 | 0 | solThrow(CompilerError, "No compiled contracts found."); |
838 | | |
839 | | // Look up the contract (by its fully-qualified name) |
840 | 0 | Contract const& matchContract = m_contracts.at(_contractName); |
841 | | // Check to see if it could collide on name |
842 | 0 | for (auto const& contract: m_contracts) |
843 | 0 | { |
844 | 0 | if (contract.second.contract->name() == matchContract.contract->name() && |
845 | 0 | contract.second.contract != matchContract.contract) |
846 | 0 | { |
847 | | // If it does, then return its fully-qualified name, made fs-friendly |
848 | 0 | std::string friendlyName = boost::algorithm::replace_all_copy(_contractName, "/", "_"); |
849 | 0 | boost::algorithm::replace_all(friendlyName, ":", "_"); |
850 | 0 | boost::algorithm::replace_all(friendlyName, ".", "_"); |
851 | 0 | return friendlyName; |
852 | 0 | } |
853 | 0 | } |
854 | | // If no collision, return the contract's name |
855 | 0 | return matchContract.contract->name(); |
856 | 0 | } |
857 | | |
858 | | string const& CompilerStack::yulIR(string const& _contractName) const |
859 | 0 | { |
860 | 0 | if (m_stackState != CompilationSuccessful) |
861 | 0 | solThrow(CompilerError, "Compilation was not successful."); |
862 | | |
863 | 0 | return contract(_contractName).yulIR; |
864 | 0 | } |
865 | | |
866 | | string const& CompilerStack::yulIROptimized(string const& _contractName) const |
867 | 0 | { |
868 | 0 | if (m_stackState != CompilationSuccessful) |
869 | 0 | solThrow(CompilerError, "Compilation was not successful."); |
870 | | |
871 | 0 | return contract(_contractName).yulIROptimized; |
872 | 0 | } |
873 | | |
874 | | string const& CompilerStack::ewasm(string const& _contractName) const |
875 | 0 | { |
876 | 0 | if (m_stackState != CompilationSuccessful) |
877 | 0 | solThrow(CompilerError, "Compilation was not successful."); |
878 | | |
879 | 0 | return contract(_contractName).ewasm; |
880 | 0 | } |
881 | | |
882 | | evmasm::LinkerObject const& CompilerStack::ewasmObject(string const& _contractName) const |
883 | 0 | { |
884 | 0 | if (m_stackState != CompilationSuccessful) |
885 | 0 | solThrow(CompilerError, "Compilation was not successful."); |
886 | | |
887 | 0 | return contract(_contractName).ewasmObject; |
888 | 0 | } |
889 | | |
890 | | evmasm::LinkerObject const& CompilerStack::object(string const& _contractName) const |
891 | 4.55k | { |
892 | 4.55k | if (m_stackState != CompilationSuccessful) |
893 | 0 | solThrow(CompilerError, "Compilation was not successful."); |
894 | | |
895 | 4.55k | return contract(_contractName).object; |
896 | 4.55k | } |
897 | | |
898 | | evmasm::LinkerObject const& CompilerStack::runtimeObject(string const& _contractName) const |
899 | 0 | { |
900 | 0 | if (m_stackState != CompilationSuccessful) |
901 | 0 | solThrow(CompilerError, "Compilation was not successful."); |
902 | | |
903 | 0 | return contract(_contractName).runtimeObject; |
904 | 0 | } |
905 | | |
906 | | /// TODO: cache this string |
907 | | string CompilerStack::assemblyString(string const& _contractName, StringMap const& _sourceCodes) const |
908 | 0 | { |
909 | 0 | if (m_stackState != CompilationSuccessful) |
910 | 0 | solThrow(CompilerError, "Compilation was not successful."); |
911 | | |
912 | 0 | Contract const& currentContract = contract(_contractName); |
913 | 0 | if (currentContract.evmAssembly) |
914 | 0 | return currentContract.evmAssembly->assemblyString(m_debugInfoSelection, _sourceCodes); |
915 | 0 | else |
916 | 0 | return string(); |
917 | 0 | } |
918 | | |
919 | | /// TODO: cache the JSON |
920 | | Json::Value CompilerStack::assemblyJSON(string const& _contractName) const |
921 | 0 | { |
922 | 0 | if (m_stackState != CompilationSuccessful) |
923 | 0 | solThrow(CompilerError, "Compilation was not successful."); |
924 | | |
925 | 0 | Contract const& currentContract = contract(_contractName); |
926 | 0 | if (currentContract.evmAssembly) |
927 | 0 | return currentContract.evmAssembly->assemblyJSON(sourceIndices()); |
928 | 0 | else |
929 | 0 | return Json::Value(); |
930 | 0 | } |
931 | | |
932 | | vector<string> CompilerStack::sourceNames() const |
933 | 0 | { |
934 | 0 | vector<string> names; |
935 | 0 | for (auto const& s: m_sources) |
936 | 0 | names.push_back(s.first); |
937 | 0 | return names; |
938 | 0 | } |
939 | | |
940 | | map<string, unsigned> CompilerStack::sourceIndices() const |
941 | 0 | { |
942 | 0 | map<string, unsigned> indices; |
943 | 0 | unsigned index = 0; |
944 | 0 | for (auto const& s: m_sources) |
945 | 0 | indices[s.first] = index++; |
946 | 0 | solAssert(!indices.count(CompilerContext::yulUtilityFileName()), ""); |
947 | 0 | indices[CompilerContext::yulUtilityFileName()] = index++; |
948 | 0 | return indices; |
949 | 0 | } |
950 | | |
951 | | Json::Value const& CompilerStack::contractABI(string const& _contractName) const |
952 | 0 | { |
953 | 0 | if (m_stackState < AnalysisPerformed) |
954 | 0 | solThrow(CompilerError, "Analysis was not successful."); |
955 | | |
956 | 0 | return contractABI(contract(_contractName)); |
957 | 0 | } |
958 | | |
959 | | Json::Value const& CompilerStack::contractABI(Contract const& _contract) const |
960 | 4.55k | { |
961 | 4.55k | if (m_stackState < AnalysisPerformed) |
962 | 0 | solThrow(CompilerError, "Analysis was not successful."); |
963 | | |
964 | 4.55k | solAssert(_contract.contract, ""); |
965 | | |
966 | 4.55k | return _contract.abi.init([&]{ return ABI::generate(*_contract.contract); }); |
967 | 4.55k | } |
968 | | |
969 | | Json::Value const& CompilerStack::storageLayout(string const& _contractName) const |
970 | 0 | { |
971 | 0 | if (m_stackState < AnalysisPerformed) |
972 | 0 | solThrow(CompilerError, "Analysis was not successful."); |
973 | | |
974 | 0 | return storageLayout(contract(_contractName)); |
975 | 0 | } |
976 | | |
977 | | Json::Value const& CompilerStack::storageLayout(Contract const& _contract) const |
978 | 0 | { |
979 | 0 | if (m_stackState < AnalysisPerformed) |
980 | 0 | solThrow(CompilerError, "Analysis was not successful."); |
981 | | |
982 | 0 | solAssert(_contract.contract, ""); |
983 | | |
984 | 0 | return _contract.storageLayout.init([&]{ return StorageLayout().generate(*_contract.contract); }); |
985 | 0 | } |
986 | | |
987 | | Json::Value const& CompilerStack::natspecUser(string const& _contractName) const |
988 | 0 | { |
989 | 0 | if (m_stackState < AnalysisPerformed) |
990 | 0 | solThrow(CompilerError, "Analysis was not successful."); |
991 | | |
992 | 0 | return natspecUser(contract(_contractName)); |
993 | 0 | } |
994 | | |
995 | | Json::Value const& CompilerStack::natspecUser(Contract const& _contract) const |
996 | 4.55k | { |
997 | 4.55k | if (m_stackState < AnalysisPerformed) |
998 | 0 | solThrow(CompilerError, "Analysis was not successful."); |
999 | | |
1000 | 4.55k | solAssert(_contract.contract, ""); |
1001 | | |
1002 | 4.55k | return _contract.userDocumentation.init([&]{ return Natspec::userDocumentation(*_contract.contract); }); |
1003 | 4.55k | } |
1004 | | |
1005 | | Json::Value const& CompilerStack::natspecDev(string const& _contractName) const |
1006 | 0 | { |
1007 | 0 | if (m_stackState < AnalysisPerformed) |
1008 | 0 | solThrow(CompilerError, "Analysis was not successful."); |
1009 | | |
1010 | 0 | return natspecDev(contract(_contractName)); |
1011 | 0 | } |
1012 | | |
1013 | | Json::Value const& CompilerStack::natspecDev(Contract const& _contract) const |
1014 | 4.55k | { |
1015 | 4.55k | if (m_stackState < AnalysisPerformed) |
1016 | 0 | solThrow(CompilerError, "Analysis was not successful."); |
1017 | | |
1018 | 4.55k | solAssert(_contract.contract, ""); |
1019 | | |
1020 | 4.55k | return _contract.devDocumentation.init([&]{ return Natspec::devDocumentation(*_contract.contract); }); |
1021 | 4.55k | } |
1022 | | |
1023 | | Json::Value CompilerStack::interfaceSymbols(string const& _contractName) const |
1024 | 4.55k | { |
1025 | 4.55k | if (m_stackState < AnalysisPerformed) |
1026 | 0 | solThrow(CompilerError, "Analysis was not successful."); |
1027 | | |
1028 | 4.55k | Json::Value interfaceSymbols(Json::objectValue); |
1029 | | // Always have a methods object |
1030 | 4.55k | interfaceSymbols["methods"] = Json::objectValue; |
1031 | | |
1032 | 4.55k | for (auto const& it: contractDefinition(_contractName).interfaceFunctions()) |
1033 | 29.9k | interfaceSymbols["methods"][it.second->externalSignature()] = it.first.hex(); |
1034 | 4.55k | for (ErrorDefinition const* error: contractDefinition(_contractName).interfaceErrors()) |
1035 | 0 | { |
1036 | 0 | string signature = error->functionType(true)->externalSignature(); |
1037 | 0 | interfaceSymbols["errors"][signature] = util::toHex(toCompactBigEndian(util::selectorFromSignature32(signature), 4)); |
1038 | 0 | } |
1039 | | |
1040 | 4.55k | for (EventDefinition const* event: ranges::concat_view( |
1041 | 4.55k | contractDefinition(_contractName).definedInterfaceEvents(), |
1042 | 4.55k | contractDefinition(_contractName).usedInterfaceEvents() |
1043 | 4.55k | )) |
1044 | 0 | if (!event->isAnonymous()) |
1045 | 0 | { |
1046 | 0 | string signature = event->functionType(true)->externalSignature(); |
1047 | 0 | interfaceSymbols["events"][signature] = toHex(u256(h256::Arith(util::keccak256(signature)))); |
1048 | 0 | } |
1049 | | |
1050 | 4.55k | return interfaceSymbols; |
1051 | 4.55k | } |
1052 | | |
1053 | | bytes CompilerStack::cborMetadata(string const& _contractName, bool _forIR) const |
1054 | 0 | { |
1055 | 0 | if (m_stackState < AnalysisPerformed) |
1056 | 0 | solThrow(CompilerError, "Analysis was not successful."); |
1057 | | |
1058 | 0 | return createCBORMetadata(contract(_contractName), _forIR); |
1059 | 0 | } |
1060 | | |
1061 | | string const& CompilerStack::metadata(Contract const& _contract) const |
1062 | 4.55k | { |
1063 | 4.55k | if (m_stackState < AnalysisPerformed) |
1064 | 0 | solThrow(CompilerError, "Analysis was not successful."); |
1065 | | |
1066 | 4.55k | solAssert(_contract.contract, ""); |
1067 | | |
1068 | 4.55k | return _contract.metadata.init([&]{ return createMetadata(_contract, m_viaIR); }); |
1069 | 4.55k | } |
1070 | | |
1071 | | CharStream const& CompilerStack::charStream(string const& _sourceName) const |
1072 | 0 | { |
1073 | 0 | if (m_stackState < SourcesSet) |
1074 | 0 | solThrow(CompilerError, "No sources set."); |
1075 | | |
1076 | 0 | solAssert(source(_sourceName).charStream, ""); |
1077 | | |
1078 | 0 | return *source(_sourceName).charStream; |
1079 | 0 | } |
1080 | | |
1081 | | SourceUnit const& CompilerStack::ast(string const& _sourceName) const |
1082 | 0 | { |
1083 | 0 | if (m_stackState < Parsed) |
1084 | 0 | solThrow(CompilerError, "Parsing not yet performed."); |
1085 | 0 | if (!source(_sourceName).ast && !m_parserErrorRecovery) |
1086 | 0 | solThrow(CompilerError, "Parsing was not successful."); |
1087 | | |
1088 | 0 | return *source(_sourceName).ast; |
1089 | 0 | } |
1090 | | |
1091 | | ContractDefinition const& CompilerStack::contractDefinition(string const& _contractName) const |
1092 | 18.2k | { |
1093 | 18.2k | if (m_stackState < AnalysisPerformed) |
1094 | 0 | solThrow(CompilerError, "Analysis was not successful."); |
1095 | | |
1096 | 18.2k | return *contract(_contractName).contract; |
1097 | 18.2k | } |
1098 | | |
1099 | | size_t CompilerStack::functionEntryPoint( |
1100 | | std::string const& _contractName, |
1101 | | FunctionDefinition const& _function |
1102 | | ) const |
1103 | 0 | { |
1104 | 0 | if (m_stackState != CompilationSuccessful) |
1105 | 0 | solThrow(CompilerError, "Compilation was not successful."); |
1106 | | |
1107 | 0 | for (auto&& [name, data]: contract(_contractName).runtimeObject.functionDebugData) |
1108 | 0 | if (data.sourceID == _function.id()) |
1109 | 0 | if (data.instructionIndex) |
1110 | 0 | return *data.instructionIndex; |
1111 | 0 | return 0; |
1112 | 0 | } |
1113 | | |
1114 | | h256 const& CompilerStack::Source::keccak256() const |
1115 | 4.55k | { |
1116 | 4.55k | if (keccak256HashCached == h256{}) |
1117 | 4.55k | keccak256HashCached = util::keccak256(charStream->source()); |
1118 | 4.55k | return keccak256HashCached; |
1119 | 4.55k | } |
1120 | | |
1121 | | h256 const& CompilerStack::Source::swarmHash() const |
1122 | 4.55k | { |
1123 | 4.55k | if (swarmHashCached == h256{}) |
1124 | 4.55k | swarmHashCached = util::bzzr1Hash(charStream->source()); |
1125 | 4.55k | return swarmHashCached; |
1126 | 4.55k | } |
1127 | | |
1128 | | string const& CompilerStack::Source::ipfsUrl() const |
1129 | 4.55k | { |
1130 | 4.55k | if (ipfsUrlCached.empty()) |
1131 | 4.55k | ipfsUrlCached = "dweb:/ipfs/" + util::ipfsHashBase58(charStream->source()); |
1132 | 4.55k | return ipfsUrlCached; |
1133 | 4.55k | } |
1134 | | |
1135 | | StringMap CompilerStack::loadMissingSources(SourceUnit const& _ast) |
1136 | 4.55k | { |
1137 | 4.55k | solAssert(m_stackState < ParsedAndImported, ""); |
1138 | 4.55k | StringMap newSources; |
1139 | 4.55k | try |
1140 | 4.55k | { |
1141 | 4.55k | for (auto const& node: _ast.nodes()) |
1142 | 13.6k | if (ImportDirective const* import = dynamic_cast<ImportDirective*>(node.get())) |
1143 | 0 | { |
1144 | 0 | string const& importPath = *import->annotation().absolutePath; |
1145 | |
|
1146 | 0 | if (m_sources.count(importPath) || newSources.count(importPath)) |
1147 | 0 | continue; |
1148 | | |
1149 | 0 | ReadCallback::Result result{false, string("File not supplied initially.")}; |
1150 | 0 | if (m_readFile) |
1151 | 0 | result = m_readFile(ReadCallback::kindString(ReadCallback::Kind::ReadFile), importPath); |
1152 | |
|
1153 | 0 | if (result.success) |
1154 | 0 | newSources[importPath] = result.responseOrErrorMessage; |
1155 | 0 | else |
1156 | 0 | { |
1157 | 0 | m_errorReporter.parserError( |
1158 | 0 | 6275_error, |
1159 | 0 | import->location(), |
1160 | 0 | string("Source \"" + importPath + "\" not found: " + result.responseOrErrorMessage) |
1161 | 0 | ); |
1162 | 0 | continue; |
1163 | 0 | } |
1164 | 0 | } |
1165 | 4.55k | } |
1166 | 4.55k | catch (FatalError const&) |
1167 | 4.55k | { |
1168 | 0 | solAssert(m_errorReporter.hasErrors(), ""); |
1169 | 0 | } |
1170 | 4.55k | return newSources; |
1171 | 4.55k | } |
1172 | | |
1173 | | string CompilerStack::applyRemapping(string const& _path, string const& _context) |
1174 | 0 | { |
1175 | 0 | solAssert(m_stackState < ParsedAndImported, ""); |
1176 | 0 | return m_importRemapper.apply(_path, _context); |
1177 | 0 | } |
1178 | | |
1179 | | void CompilerStack::resolveImports() |
1180 | 4.55k | { |
1181 | 4.55k | solAssert(m_stackState == ParsedAndImported, ""); |
1182 | | |
1183 | | // topological sorting (depth first search) of the import graph, cutting potential cycles |
1184 | 4.55k | vector<Source const*> sourceOrder; |
1185 | 4.55k | set<Source const*> sourcesSeen; |
1186 | | |
1187 | 4.55k | function<void(Source const*)> toposort = [&](Source const* _source) |
1188 | 4.55k | { |
1189 | 4.55k | if (sourcesSeen.count(_source)) |
1190 | 0 | return; |
1191 | 4.55k | sourcesSeen.insert(_source); |
1192 | 4.55k | if (_source->ast) |
1193 | 4.55k | for (ASTPointer<ASTNode> const& node: _source->ast->nodes()) |
1194 | 13.6k | if (ImportDirective const* import = dynamic_cast<ImportDirective*>(node.get())) |
1195 | 0 | { |
1196 | 0 | string const& path = *import->annotation().absolutePath; |
1197 | 0 | solAssert(m_sources.count(path), ""); |
1198 | 0 | import->annotation().sourceUnit = m_sources[path].ast.get(); |
1199 | 0 | toposort(&m_sources[path]); |
1200 | 0 | } |
1201 | 4.55k | sourceOrder.push_back(_source); |
1202 | 4.55k | }; |
1203 | | |
1204 | 4.55k | for (auto const& sourcePair: m_sources) |
1205 | 4.55k | if (isRequestedSource(sourcePair.first)) |
1206 | 4.55k | toposort(&sourcePair.second); |
1207 | | |
1208 | 4.55k | swap(m_sourceOrder, sourceOrder); |
1209 | 4.55k | } |
1210 | | |
1211 | | void CompilerStack::storeContractDefinitions() |
1212 | 4.55k | { |
1213 | 4.55k | for (auto const& pair: m_sources) |
1214 | 4.55k | if (pair.second.ast) |
1215 | 4.55k | for ( |
1216 | 4.55k | ContractDefinition const* contract: |
1217 | 4.55k | ASTNode::filteredNodes<ContractDefinition>(pair.second.ast->nodes()) |
1218 | 4.55k | ) |
1219 | 4.55k | { |
1220 | 4.55k | string fullyQualifiedName = *pair.second.ast->annotation().path + ":" + contract->name(); |
1221 | | // Note that we now reference contracts by their fully qualified names, and |
1222 | | // thus contracts can only conflict if declared in the same source file. This |
1223 | | // should already cause a double-declaration error elsewhere. |
1224 | 4.55k | if (!m_contracts.count(fullyQualifiedName)) |
1225 | 4.55k | m_contracts[fullyQualifiedName].contract = contract; |
1226 | 4.55k | } |
1227 | 4.55k | } |
1228 | | |
1229 | | namespace |
1230 | | { |
1231 | | bool onlySafeExperimentalFeaturesActivated(set<ExperimentalFeature> const& features) |
1232 | 4.55k | { |
1233 | 4.55k | for (auto const feature: features) |
1234 | 4.55k | if (!ExperimentalFeatureWithoutWarning.count(feature)) |
1235 | 0 | return false; |
1236 | 4.55k | return true; |
1237 | 4.55k | } |
1238 | | } |
1239 | | |
1240 | | void CompilerStack::assemble( |
1241 | | ContractDefinition const& _contract, |
1242 | | std::shared_ptr<evmasm::Assembly> _assembly, |
1243 | | std::shared_ptr<evmasm::Assembly> _runtimeAssembly |
1244 | | ) |
1245 | 4.55k | { |
1246 | 4.55k | solAssert(m_stackState >= AnalysisPerformed, ""); |
1247 | 4.55k | solAssert(!m_hasError, ""); |
1248 | | |
1249 | 4.55k | Contract& compiledContract = m_contracts.at(_contract.fullyQualifiedName()); |
1250 | | |
1251 | 4.55k | compiledContract.evmAssembly = _assembly; |
1252 | 4.55k | solAssert(compiledContract.evmAssembly, ""); |
1253 | 4.55k | try |
1254 | 4.55k | { |
1255 | | // Assemble deployment (incl. runtime) object. |
1256 | 4.55k | compiledContract.object = compiledContract.evmAssembly->assemble(); |
1257 | 4.55k | } |
1258 | 4.55k | catch (evmasm::AssemblyException const&) |
1259 | 4.55k | { |
1260 | 0 | solAssert(false, "Assembly exception for bytecode"); |
1261 | 0 | } |
1262 | 4.55k | solAssert(compiledContract.object.immutableReferences.empty(), "Leftover immutables."); |
1263 | | |
1264 | 4.55k | compiledContract.evmRuntimeAssembly = _runtimeAssembly; |
1265 | 4.55k | solAssert(compiledContract.evmRuntimeAssembly, ""); |
1266 | 4.55k | try |
1267 | 4.55k | { |
1268 | | // Assemble runtime object. |
1269 | 4.55k | compiledContract.runtimeObject = compiledContract.evmRuntimeAssembly->assemble(); |
1270 | 4.55k | } |
1271 | 4.55k | catch (evmasm::AssemblyException const&) |
1272 | 4.55k | { |
1273 | 0 | solAssert(false, "Assembly exception for deployed bytecode"); |
1274 | 0 | } |
1275 | | |
1276 | | // Throw a warning if EIP-170 limits are exceeded: |
1277 | | // If contract creation returns data with length greater than 0x6000 (214 + 213) bytes, |
1278 | | // contract creation fails with an out of gas error. |
1279 | 4.55k | if ( |
1280 | 4.55k | m_evmVersion >= langutil::EVMVersion::spuriousDragon() && |
1281 | 4.55k | compiledContract.runtimeObject.bytecode.size() > 0x6000 |
1282 | 4.55k | ) |
1283 | 1.67k | m_errorReporter.warning( |
1284 | 1.67k | 5574_error, |
1285 | 1.67k | _contract.location(), |
1286 | 1.67k | "Contract code size is "s + |
1287 | 1.67k | to_string(compiledContract.runtimeObject.bytecode.size()) + |
1288 | 1.67k | " bytes and exceeds 24576 bytes (a limit introduced in Spurious Dragon). " |
1289 | 1.67k | "This contract may not be deployable on Mainnet. " |
1290 | 1.67k | "Consider enabling the optimizer (with a low \"runs\" value!), " |
1291 | 1.67k | "turning off revert strings, or using libraries." |
1292 | 1.67k | ); |
1293 | 4.55k | } |
1294 | | |
1295 | | void CompilerStack::compileContract( |
1296 | | ContractDefinition const& _contract, |
1297 | | map<ContractDefinition const*, shared_ptr<Compiler const>>& _otherCompilers |
1298 | | ) |
1299 | 4.55k | { |
1300 | 4.55k | solAssert(!m_viaIR, ""); |
1301 | 4.55k | solAssert(m_stackState >= AnalysisPerformed, ""); |
1302 | 4.55k | if (m_hasError) |
1303 | 0 | solThrow(CompilerError, "Called compile with errors."); |
1304 | | |
1305 | 4.55k | if (_otherCompilers.count(&_contract)) |
1306 | 0 | return; |
1307 | | |
1308 | 4.55k | for (auto const& [dependency, referencee]: _contract.annotation().contractDependencies) |
1309 | 0 | compileContract(*dependency, _otherCompilers); |
1310 | | |
1311 | 4.55k | if (!_contract.canBeDeployed()) |
1312 | 0 | return; |
1313 | | |
1314 | 4.55k | Contract& compiledContract = m_contracts.at(_contract.fullyQualifiedName()); |
1315 | | |
1316 | 4.55k | shared_ptr<Compiler> compiler = make_shared<Compiler>(m_evmVersion, m_revertStrings, m_optimiserSettings); |
1317 | 4.55k | compiledContract.compiler = compiler; |
1318 | | |
1319 | 4.55k | solAssert(!m_viaIR, ""); |
1320 | 4.55k | bytes cborEncodedMetadata = createCBORMetadata(compiledContract, /* _forIR */ false); |
1321 | | |
1322 | 4.55k | try |
1323 | 4.55k | { |
1324 | | // Run optimiser and compile the contract. |
1325 | 4.55k | compiler->compileContract(_contract, _otherCompilers, cborEncodedMetadata); |
1326 | 4.55k | } |
1327 | 4.55k | catch(evmasm::OptimizerException const&) |
1328 | 4.55k | { |
1329 | 0 | solAssert(false, "Optimizer exception during compilation"); |
1330 | 0 | } |
1331 | | |
1332 | 4.55k | _otherCompilers[compiledContract.contract] = compiler; |
1333 | | |
1334 | 4.55k | assemble(_contract, compiler->assemblyPtr(), compiler->runtimeAssemblyPtr()); |
1335 | 4.55k | } |
1336 | | |
1337 | | void CompilerStack::generateIR(ContractDefinition const& _contract) |
1338 | 0 | { |
1339 | 0 | solAssert(m_stackState >= AnalysisPerformed, ""); |
1340 | 0 | if (m_hasError) |
1341 | 0 | solThrow(CompilerError, "Called generateIR with errors."); |
1342 | | |
1343 | 0 | Contract& compiledContract = m_contracts.at(_contract.fullyQualifiedName()); |
1344 | 0 | if (!compiledContract.yulIR.empty()) |
1345 | 0 | return; |
1346 | | |
1347 | 0 | if (!*_contract.sourceUnit().annotation().useABICoderV2) |
1348 | 0 | m_errorReporter.warning( |
1349 | 0 | 2066_error, |
1350 | 0 | _contract.location(), |
1351 | 0 | "Contract requests the ABI coder v1, which is incompatible with the IR. " |
1352 | 0 | "Using ABI coder v2 instead." |
1353 | 0 | ); |
1354 | |
|
1355 | 0 | string dependenciesSource; |
1356 | 0 | for (auto const& [dependency, referencee]: _contract.annotation().contractDependencies) |
1357 | 0 | generateIR(*dependency); |
1358 | |
|
1359 | 0 | if (!_contract.canBeDeployed()) |
1360 | 0 | return; |
1361 | | |
1362 | 0 | map<ContractDefinition const*, string_view const> otherYulSources; |
1363 | 0 | for (auto const& pair: m_contracts) |
1364 | 0 | otherYulSources.emplace(pair.second.contract, pair.second.yulIR); |
1365 | |
|
1366 | 0 | IRGenerator generator(m_evmVersion, m_revertStrings, m_optimiserSettings, sourceIndices(), m_debugInfoSelection, this); |
1367 | 0 | tie(compiledContract.yulIR, compiledContract.yulIROptimized) = generator.run( |
1368 | 0 | _contract, |
1369 | 0 | createCBORMetadata(compiledContract, /* _forIR */ true), |
1370 | 0 | otherYulSources |
1371 | 0 | ); |
1372 | 0 | } |
1373 | | |
1374 | | void CompilerStack::generateEVMFromIR(ContractDefinition const& _contract) |
1375 | 0 | { |
1376 | 0 | solAssert(m_stackState >= AnalysisPerformed, ""); |
1377 | 0 | if (m_hasError) |
1378 | 0 | solThrow(CompilerError, "Called generateEVMFromIR with errors."); |
1379 | | |
1380 | 0 | if (!_contract.canBeDeployed()) |
1381 | 0 | return; |
1382 | | |
1383 | 0 | Contract& compiledContract = m_contracts.at(_contract.fullyQualifiedName()); |
1384 | 0 | solAssert(!compiledContract.yulIROptimized.empty(), ""); |
1385 | 0 | if (!compiledContract.object.bytecode.empty()) |
1386 | 0 | return; |
1387 | | |
1388 | | // Re-parse the Yul IR in EVM dialect |
1389 | 0 | yul::YulStack stack( |
1390 | 0 | m_evmVersion, |
1391 | 0 | yul::YulStack::Language::StrictAssembly, |
1392 | 0 | m_optimiserSettings, |
1393 | 0 | m_debugInfoSelection |
1394 | 0 | ); |
1395 | 0 | stack.parseAndAnalyze("", compiledContract.yulIROptimized); |
1396 | 0 | stack.optimize(); |
1397 | | |
1398 | | //cout << yul::AsmPrinter{}(*stack.parserResult()->code) << endl; |
1399 | |
|
1400 | 0 | string deployedName = IRNames::deployedObject(_contract); |
1401 | 0 | solAssert(!deployedName.empty(), ""); |
1402 | 0 | tie(compiledContract.evmAssembly, compiledContract.evmRuntimeAssembly) = stack.assembleEVMWithDeployed(deployedName); |
1403 | 0 | assemble(_contract, compiledContract.evmAssembly, compiledContract.evmRuntimeAssembly); |
1404 | 0 | } |
1405 | | |
1406 | | void CompilerStack::generateEwasm(ContractDefinition const& _contract) |
1407 | 0 | { |
1408 | 0 | solAssert(m_stackState >= AnalysisPerformed, ""); |
1409 | 0 | if (m_hasError) |
1410 | 0 | solThrow(CompilerError, "Called generateEwasm with errors."); |
1411 | | |
1412 | 0 | if (!_contract.canBeDeployed()) |
1413 | 0 | return; |
1414 | | |
1415 | 0 | Contract& compiledContract = m_contracts.at(_contract.fullyQualifiedName()); |
1416 | 0 | solAssert(!compiledContract.yulIROptimized.empty(), ""); |
1417 | 0 | if (!compiledContract.ewasm.empty()) |
1418 | 0 | return; |
1419 | | |
1420 | | // Re-parse the Yul IR in EVM dialect |
1421 | 0 | yul::YulStack stack( |
1422 | 0 | m_evmVersion, |
1423 | 0 | yul::YulStack::Language::StrictAssembly, |
1424 | 0 | m_optimiserSettings, |
1425 | 0 | m_debugInfoSelection |
1426 | 0 | ); |
1427 | 0 | stack.parseAndAnalyze("", compiledContract.yulIROptimized); |
1428 | |
|
1429 | 0 | stack.optimize(); |
1430 | 0 | stack.translate(yul::YulStack::Language::Ewasm); |
1431 | 0 | stack.optimize(); |
1432 | | |
1433 | | //cout << yul::AsmPrinter{}(*stack.parserResult()->code) << endl; |
1434 | | |
1435 | | // Turn into Ewasm text representation. |
1436 | 0 | auto result = stack.assemble(yul::YulStack::Machine::Ewasm); |
1437 | 0 | compiledContract.ewasm = std::move(result.assembly); |
1438 | 0 | compiledContract.ewasmObject = std::move(*result.bytecode); |
1439 | 0 | } |
1440 | | |
1441 | | CompilerStack::Contract const& CompilerStack::contract(string const& _contractName) const |
1442 | 22.7k | { |
1443 | 22.7k | solAssert(m_stackState >= AnalysisPerformed, ""); |
1444 | | |
1445 | 22.7k | auto it = m_contracts.find(_contractName); |
1446 | 22.7k | if (it != m_contracts.end()) |
1447 | 0 | return it->second; |
1448 | | |
1449 | | // To provide a measure of backward-compatibility, if a contract is not located by its |
1450 | | // fully-qualified name, a lookup will be attempted purely on the contract's name to see |
1451 | | // if anything will satisfy. |
1452 | 22.7k | if (_contractName.find(':') == string::npos) |
1453 | 22.7k | { |
1454 | 22.7k | for (auto const& contractEntry: m_contracts) |
1455 | 22.7k | { |
1456 | 22.7k | stringstream ss; |
1457 | 22.7k | ss.str(contractEntry.first); |
1458 | | // All entries are <source>:<contract> |
1459 | 22.7k | string source; |
1460 | 22.7k | string foundName; |
1461 | 22.7k | getline(ss, source, ':'); |
1462 | 22.7k | getline(ss, foundName, ':'); |
1463 | 22.7k | if (foundName == _contractName) |
1464 | 22.7k | return contractEntry.second; |
1465 | 22.7k | } |
1466 | 22.7k | } |
1467 | | |
1468 | | // If we get here, both lookup methods failed. |
1469 | 22.7k | solThrow(CompilerError, "Contract \"" + _contractName + "\" not found."); |
1470 | 22.7k | } |
1471 | | |
1472 | | CompilerStack::Source const& CompilerStack::source(string const& _sourceName) const |
1473 | 0 | { |
1474 | 0 | auto it = m_sources.find(_sourceName); |
1475 | 0 | if (it == m_sources.end()) |
1476 | 0 | solThrow(CompilerError, "Given source file not found: " + _sourceName); |
1477 | | |
1478 | 0 | return it->second; |
1479 | 0 | } |
1480 | | |
1481 | | string CompilerStack::createMetadata(Contract const& _contract, bool _forIR) const |
1482 | 4.55k | { |
1483 | 4.55k | Json::Value meta{Json::objectValue}; |
1484 | 4.55k | meta["version"] = 1; |
1485 | 4.55k | meta["language"] = m_importedSources ? "SolidityAST" : "Solidity"; |
1486 | 4.55k | meta["compiler"]["version"] = VersionStringStrict; |
1487 | | |
1488 | | /// All the source files (including self), which should be included in the metadata. |
1489 | 4.55k | set<string> referencedSources; |
1490 | 4.55k | referencedSources.insert(*_contract.contract->sourceUnit().annotation().path); |
1491 | 4.55k | for (auto const sourceUnit: _contract.contract->sourceUnit().referencedSourceUnits(true)) |
1492 | 0 | referencedSources.insert(*sourceUnit->annotation().path); |
1493 | | |
1494 | 4.55k | meta["sources"] = Json::objectValue; |
1495 | 4.55k | for (auto const& s: m_sources) |
1496 | 4.55k | { |
1497 | 4.55k | if (!referencedSources.count(s.first)) |
1498 | 0 | continue; |
1499 | | |
1500 | 4.55k | solAssert(s.second.charStream, "Character stream not available"); |
1501 | 4.55k | meta["sources"][s.first]["keccak256"] = "0x" + util::toHex(s.second.keccak256().asBytes()); |
1502 | 4.55k | if (optional<string> licenseString = s.second.ast->licenseString()) |
1503 | 0 | meta["sources"][s.first]["license"] = *licenseString; |
1504 | 4.55k | if (m_metadataLiteralSources) |
1505 | 0 | meta["sources"][s.first]["content"] = s.second.charStream->source(); |
1506 | 4.55k | else |
1507 | 4.55k | { |
1508 | 4.55k | meta["sources"][s.first]["urls"] = Json::arrayValue; |
1509 | 4.55k | meta["sources"][s.first]["urls"].append("bzz-raw://" + util::toHex(s.second.swarmHash().asBytes())); |
1510 | 4.55k | meta["sources"][s.first]["urls"].append(s.second.ipfsUrl()); |
1511 | 4.55k | } |
1512 | 4.55k | } |
1513 | | |
1514 | 4.55k | static_assert(sizeof(m_optimiserSettings.expectedExecutionsPerDeployment) <= sizeof(Json::LargestUInt), "Invalid word size."); |
1515 | 4.55k | solAssert(static_cast<Json::LargestUInt>(m_optimiserSettings.expectedExecutionsPerDeployment) < std::numeric_limits<Json::LargestUInt>::max(), ""); |
1516 | 4.55k | meta["settings"]["optimizer"]["runs"] = Json::Value(Json::LargestUInt(m_optimiserSettings.expectedExecutionsPerDeployment)); |
1517 | | |
1518 | | /// Backwards compatibility: If set to one of the default settings, do not provide details. |
1519 | 4.55k | OptimiserSettings settingsWithoutRuns = m_optimiserSettings; |
1520 | | // reset to default |
1521 | 4.55k | settingsWithoutRuns.expectedExecutionsPerDeployment = OptimiserSettings::minimal().expectedExecutionsPerDeployment; |
1522 | 4.55k | if (settingsWithoutRuns == OptimiserSettings::minimal()) |
1523 | 4.55k | meta["settings"]["optimizer"]["enabled"] = false; |
1524 | 0 | else if (settingsWithoutRuns == OptimiserSettings::standard()) |
1525 | 0 | meta["settings"]["optimizer"]["enabled"] = true; |
1526 | 0 | else |
1527 | 0 | { |
1528 | 0 | Json::Value details{Json::objectValue}; |
1529 | |
|
1530 | 0 | details["orderLiterals"] = m_optimiserSettings.runOrderLiterals; |
1531 | 0 | details["inliner"] = m_optimiserSettings.runInliner; |
1532 | 0 | details["jumpdestRemover"] = m_optimiserSettings.runJumpdestRemover; |
1533 | 0 | details["peephole"] = m_optimiserSettings.runPeephole; |
1534 | 0 | details["deduplicate"] = m_optimiserSettings.runDeduplicate; |
1535 | 0 | details["cse"] = m_optimiserSettings.runCSE; |
1536 | 0 | details["constantOptimizer"] = m_optimiserSettings.runConstantOptimiser; |
1537 | 0 | details["yul"] = m_optimiserSettings.runYulOptimiser; |
1538 | 0 | if (m_optimiserSettings.runYulOptimiser) |
1539 | 0 | { |
1540 | 0 | details["yulDetails"] = Json::objectValue; |
1541 | 0 | details["yulDetails"]["stackAllocation"] = m_optimiserSettings.optimizeStackAllocation; |
1542 | 0 | details["yulDetails"]["optimizerSteps"] = m_optimiserSettings.yulOptimiserSteps; |
1543 | 0 | } |
1544 | |
|
1545 | 0 | meta["settings"]["optimizer"]["details"] = std::move(details); |
1546 | 0 | } |
1547 | | |
1548 | 4.55k | if (m_revertStrings != RevertStrings::Default) |
1549 | 0 | meta["settings"]["debug"]["revertStrings"] = revertStringsToString(m_revertStrings); |
1550 | | |
1551 | 4.55k | if (m_metadataLiteralSources) |
1552 | 0 | meta["settings"]["metadata"]["useLiteralContent"] = true; |
1553 | | |
1554 | 4.55k | static vector<string> hashes{"ipfs", "bzzr1", "none"}; |
1555 | 4.55k | meta["settings"]["metadata"]["bytecodeHash"] = hashes.at(unsigned(m_metadataHash)); |
1556 | | |
1557 | 4.55k | if (_forIR) |
1558 | 0 | meta["settings"]["viaIR"] = _forIR; |
1559 | 4.55k | meta["settings"]["evmVersion"] = m_evmVersion.name(); |
1560 | 4.55k | meta["settings"]["compilationTarget"][_contract.contract->sourceUnitName()] = |
1561 | 4.55k | *_contract.contract->annotation().canonicalName; |
1562 | | |
1563 | 4.55k | meta["settings"]["remappings"] = Json::arrayValue; |
1564 | 4.55k | set<string> remappings; |
1565 | 4.55k | for (auto const& r: m_importRemapper.remappings()) |
1566 | 0 | remappings.insert(r.context + ":" + r.prefix + "=" + r.target); |
1567 | 4.55k | for (auto const& r: remappings) |
1568 | 0 | meta["settings"]["remappings"].append(r); |
1569 | | |
1570 | 4.55k | meta["settings"]["libraries"] = Json::objectValue; |
1571 | 4.55k | for (auto const& library: m_libraries) |
1572 | 0 | meta["settings"]["libraries"][library.first] = "0x" + util::toHex(library.second.asBytes()); |
1573 | | |
1574 | 4.55k | meta["output"]["abi"] = contractABI(_contract); |
1575 | 4.55k | meta["output"]["userdoc"] = natspecUser(_contract); |
1576 | 4.55k | meta["output"]["devdoc"] = natspecDev(_contract); |
1577 | | |
1578 | 4.55k | return util::jsonCompactPrint(meta); |
1579 | 4.55k | } |
1580 | | |
1581 | | class MetadataCBOREncoder |
1582 | | { |
1583 | | public: |
1584 | | void pushBytes(string const& key, bytes const& value) |
1585 | 4.55k | { |
1586 | 4.55k | m_entryCount++; |
1587 | 4.55k | pushTextString(key); |
1588 | 4.55k | pushByteString(value); |
1589 | 4.55k | } |
1590 | | |
1591 | | void pushString(string const& key, string const& value) |
1592 | 4.55k | { |
1593 | 4.55k | m_entryCount++; |
1594 | 4.55k | pushTextString(key); |
1595 | 4.55k | pushTextString(value); |
1596 | 4.55k | } |
1597 | | |
1598 | | void pushBool(string const& key, bool value) |
1599 | 0 | { |
1600 | 0 | m_entryCount++; |
1601 | 0 | pushTextString(key); |
1602 | 0 | pushBool(value); |
1603 | 0 | } |
1604 | | |
1605 | | bytes serialise() const |
1606 | 4.55k | { |
1607 | 4.55k | size_t size = m_data.size() + 1; |
1608 | 4.55k | solAssert(size <= 0xffff, "Metadata too large."); |
1609 | 4.55k | solAssert(m_entryCount <= 0x1f, "Too many map entries."); |
1610 | | |
1611 | | // CBOR fixed-length map |
1612 | 4.55k | bytes ret{static_cast<unsigned char>(0xa0 + m_entryCount)}; |
1613 | | // The already encoded key-value pairs |
1614 | 4.55k | ret += m_data; |
1615 | | // 16-bit big endian length |
1616 | 4.55k | ret += toCompactBigEndian(size, 2); |
1617 | 4.55k | return ret; |
1618 | 4.55k | } |
1619 | | |
1620 | | private: |
1621 | | void pushTextString(string const& key) |
1622 | 13.6k | { |
1623 | 13.6k | size_t length = key.size(); |
1624 | 13.6k | if (length < 24) |
1625 | 9.10k | { |
1626 | 9.10k | m_data += bytes{static_cast<unsigned char>(0x60 + length)}; |
1627 | 9.10k | m_data += key; |
1628 | 9.10k | } |
1629 | 4.55k | else if (length <= 256) |
1630 | 4.55k | { |
1631 | 4.55k | m_data += bytes{0x78, static_cast<unsigned char>(length)}; |
1632 | 4.55k | m_data += key; |
1633 | 4.55k | } |
1634 | 0 | else |
1635 | 4.55k | solAssert(false, "Text string too large."); |
1636 | 13.6k | } |
1637 | | void pushByteString(bytes const& key) |
1638 | 4.55k | { |
1639 | 4.55k | size_t length = key.size(); |
1640 | 4.55k | if (length < 24) |
1641 | 0 | { |
1642 | 0 | m_data += bytes{static_cast<unsigned char>(0x40 + length)}; |
1643 | 0 | m_data += key; |
1644 | 0 | } |
1645 | 4.55k | else if (length <= 256) |
1646 | 4.55k | { |
1647 | 4.55k | m_data += bytes{0x58, static_cast<unsigned char>(length)}; |
1648 | 4.55k | m_data += key; |
1649 | 4.55k | } |
1650 | 0 | else |
1651 | 4.55k | solAssert(false, "Byte string too large."); |
1652 | 4.55k | } |
1653 | | void pushBool(bool value) |
1654 | 0 | { |
1655 | 0 | if (value) |
1656 | 0 | m_data += bytes{0xf5}; |
1657 | 0 | else |
1658 | 0 | m_data += bytes{0xf4}; |
1659 | 0 | } |
1660 | | unsigned m_entryCount = 0; |
1661 | | bytes m_data; |
1662 | | }; |
1663 | | |
1664 | | bytes CompilerStack::createCBORMetadata(Contract const& _contract, bool _forIR) const |
1665 | 4.55k | { |
1666 | 4.55k | if (m_metadataFormat == MetadataFormat::NoMetadata) |
1667 | 0 | return bytes{}; |
1668 | | |
1669 | 4.55k | bool const experimentalMode = !onlySafeExperimentalFeaturesActivated( |
1670 | 4.55k | _contract.contract->sourceUnit().annotation().experimentalFeatures |
1671 | 4.55k | ); |
1672 | | |
1673 | 4.55k | string meta = (_forIR == m_viaIR ? metadata(_contract) : createMetadata(_contract, _forIR)); |
1674 | | |
1675 | 4.55k | MetadataCBOREncoder encoder; |
1676 | | |
1677 | 4.55k | if (m_metadataHash == MetadataHash::IPFS) |
1678 | 4.55k | encoder.pushBytes("ipfs", util::ipfsHash(meta)); |
1679 | 0 | else if (m_metadataHash == MetadataHash::Bzzr1) |
1680 | 0 | encoder.pushBytes("bzzr1", util::bzzr1Hash(meta).asBytes()); |
1681 | 0 | else |
1682 | 0 | solAssert(m_metadataHash == MetadataHash::None, "Invalid metadata hash"); |
1683 | | |
1684 | 4.55k | if (experimentalMode) |
1685 | 0 | encoder.pushBool("experimental", true); |
1686 | 4.55k | if (m_metadataFormat == MetadataFormat::WithReleaseVersionTag) |
1687 | 0 | encoder.pushBytes("solc", VersionCompactBytes); |
1688 | 4.55k | else |
1689 | 4.55k | { |
1690 | 4.55k | solAssert( |
1691 | 4.55k | m_metadataFormat == MetadataFormat::WithPrereleaseVersionTag, |
1692 | 4.55k | "Invalid metadata format." |
1693 | 4.55k | ); |
1694 | 4.55k | encoder.pushString("solc", VersionStringStrict); |
1695 | 4.55k | } |
1696 | 4.55k | return encoder.serialise(); |
1697 | 4.55k | } |
1698 | | |
1699 | | namespace |
1700 | | { |
1701 | | |
1702 | | Json::Value gasToJson(GasEstimator::GasConsumption const& _gas) |
1703 | 0 | { |
1704 | 0 | if (_gas.isInfinite) |
1705 | 0 | return Json::Value("infinite"); |
1706 | 0 | else |
1707 | 0 | return Json::Value(util::toString(_gas.value)); |
1708 | 0 | } |
1709 | | |
1710 | | } |
1711 | | |
1712 | | Json::Value CompilerStack::gasEstimates(string const& _contractName) const |
1713 | 0 | { |
1714 | 0 | if (m_stackState != CompilationSuccessful) |
1715 | 0 | solThrow(CompilerError, "Compilation was not successful."); |
1716 | | |
1717 | 0 | if (!assemblyItems(_contractName) && !runtimeAssemblyItems(_contractName)) |
1718 | 0 | return Json::Value(); |
1719 | | |
1720 | 0 | using Gas = GasEstimator::GasConsumption; |
1721 | 0 | GasEstimator gasEstimator(m_evmVersion); |
1722 | 0 | Json::Value output(Json::objectValue); |
1723 | |
|
1724 | 0 | if (evmasm::AssemblyItems const* items = assemblyItems(_contractName)) |
1725 | 0 | { |
1726 | 0 | Gas executionGas = gasEstimator.functionalEstimation(*items); |
1727 | 0 | Gas codeDepositGas{evmasm::GasMeter::dataGas(runtimeObject(_contractName).bytecode, false, m_evmVersion)}; |
1728 | |
|
1729 | 0 | Json::Value creation(Json::objectValue); |
1730 | 0 | creation["codeDepositCost"] = gasToJson(codeDepositGas); |
1731 | 0 | creation["executionCost"] = gasToJson(executionGas); |
1732 | | /// TODO: implement + overload to avoid the need of += |
1733 | 0 | executionGas += codeDepositGas; |
1734 | 0 | creation["totalCost"] = gasToJson(executionGas); |
1735 | 0 | output["creation"] = creation; |
1736 | 0 | } |
1737 | |
|
1738 | 0 | if (evmasm::AssemblyItems const* items = runtimeAssemblyItems(_contractName)) |
1739 | 0 | { |
1740 | | /// External functions |
1741 | 0 | ContractDefinition const& contract = contractDefinition(_contractName); |
1742 | 0 | Json::Value externalFunctions(Json::objectValue); |
1743 | 0 | for (auto it: contract.interfaceFunctions()) |
1744 | 0 | { |
1745 | 0 | string sig = it.second->externalSignature(); |
1746 | 0 | externalFunctions[sig] = gasToJson(gasEstimator.functionalEstimation(*items, sig)); |
1747 | 0 | } |
1748 | |
|
1749 | 0 | if (contract.fallbackFunction()) |
1750 | | /// This needs to be set to an invalid signature in order to trigger the fallback, |
1751 | | /// without the shortcut (of CALLDATSIZE == 0), and therefore to receive the upper bound. |
1752 | | /// An empty string ("") would work to trigger the shortcut only. |
1753 | 0 | externalFunctions[""] = gasToJson(gasEstimator.functionalEstimation(*items, "INVALID")); |
1754 | |
|
1755 | 0 | if (!externalFunctions.empty()) |
1756 | 0 | output["external"] = externalFunctions; |
1757 | | |
1758 | | /// Internal functions |
1759 | 0 | Json::Value internalFunctions(Json::objectValue); |
1760 | 0 | for (auto const& it: contract.definedFunctions()) |
1761 | 0 | { |
1762 | | /// Exclude externally visible functions, constructor, fallback and receive ether function |
1763 | 0 | if (it->isPartOfExternalInterface() || !it->isOrdinary()) |
1764 | 0 | continue; |
1765 | | |
1766 | 0 | size_t entry = functionEntryPoint(_contractName, *it); |
1767 | 0 | GasEstimator::GasConsumption gas = GasEstimator::GasConsumption::infinite(); |
1768 | 0 | if (entry > 0) |
1769 | 0 | gas = gasEstimator.functionalEstimation(*items, entry, *it); |
1770 | | |
1771 | | /// TODO: This could move into a method shared with externalSignature() |
1772 | 0 | FunctionType type(*it); |
1773 | 0 | string sig = it->name() + "("; |
1774 | 0 | auto paramTypes = type.parameterTypes(); |
1775 | 0 | for (auto it = paramTypes.begin(); it != paramTypes.end(); ++it) |
1776 | 0 | sig += (*it)->toString() + (it + 1 == paramTypes.end() ? "" : ","); |
1777 | 0 | sig += ")"; |
1778 | |
|
1779 | 0 | internalFunctions[sig] = gasToJson(gas); |
1780 | 0 | } |
1781 | |
|
1782 | 0 | if (!internalFunctions.empty()) |
1783 | 0 | output["internal"] = internalFunctions; |
1784 | 0 | } |
1785 | |
|
1786 | 0 | return output; |
1787 | 0 | } |