diff options
author | chriseth <chris@ethereum.org> | 2017-07-03 20:52:29 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-07-03 20:52:29 +0800 |
commit | 76d3b7c5a160e1f550c710e6850ee6f116142ca1 (patch) | |
tree | 93c96f7073617b4f56c8c355cdc30701aec4818b /libsolidity/interface | |
parent | 78969364608ba60d1654f4d1738886d13112b6cd (diff) | |
parent | 2222ddecf49b5b901f63b9e7449ee76c9f51c47a (diff) | |
download | dexon-solidity-76d3b7c5a160e1f550c710e6850ee6f116142ca1.tar dexon-solidity-76d3b7c5a160e1f550c710e6850ee6f116142ca1.tar.gz dexon-solidity-76d3b7c5a160e1f550c710e6850ee6f116142ca1.tar.bz2 dexon-solidity-76d3b7c5a160e1f550c710e6850ee6f116142ca1.tar.lz dexon-solidity-76d3b7c5a160e1f550c710e6850ee6f116142ca1.tar.xz dexon-solidity-76d3b7c5a160e1f550c710e6850ee6f116142ca1.tar.zst dexon-solidity-76d3b7c5a160e1f550c710e6850ee6f116142ca1.zip |
Merge pull request #2510 from ethereum/develop
Version 0.4.12
Diffstat (limited to 'libsolidity/interface')
-rw-r--r-- | libsolidity/interface/ABI.cpp | 116 | ||||
-rw-r--r-- | libsolidity/interface/ABI.h | 56 | ||||
-rw-r--r-- | libsolidity/interface/AssemblyStack.cpp | 119 | ||||
-rw-r--r-- | libsolidity/interface/AssemblyStack.h | 96 | ||||
-rw-r--r-- | libsolidity/interface/CompilerStack.cpp | 129 | ||||
-rw-r--r-- | libsolidity/interface/CompilerStack.h | 84 | ||||
-rw-r--r-- | libsolidity/interface/ErrorReporter.cpp | 167 | ||||
-rw-r--r-- | libsolidity/interface/ErrorReporter.h | 102 | ||||
-rw-r--r-- | libsolidity/interface/Exceptions.cpp | 4 | ||||
-rw-r--r-- | libsolidity/interface/Exceptions.h | 12 | ||||
-rw-r--r-- | libsolidity/interface/InterfaceHandler.cpp | 197 | ||||
-rw-r--r-- | libsolidity/interface/Natspec.cpp | 130 | ||||
-rw-r--r-- | libsolidity/interface/Natspec.h (renamed from libsolidity/interface/InterfaceHandler.h) | 9 | ||||
-rw-r--r-- | libsolidity/interface/StandardCompiler.cpp | 32 | ||||
-rw-r--r-- | libsolidity/interface/Utils.h | 45 | ||||
-rw-r--r-- | libsolidity/interface/Version.cpp | 2 |
16 files changed, 913 insertions, 387 deletions
diff --git a/libsolidity/interface/ABI.cpp b/libsolidity/interface/ABI.cpp new file mode 100644 index 00000000..12f958fc --- /dev/null +++ b/libsolidity/interface/ABI.cpp @@ -0,0 +1,116 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Utilities to handle the Contract ABI (https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI) + */ + +#include <libsolidity/interface/ABI.h> +#include <boost/range/irange.hpp> +#include <libsolidity/ast/AST.h> + +using namespace std; +using namespace dev; +using namespace dev::solidity; + +Json::Value ABI::generate(ContractDefinition const& _contractDef) +{ + Json::Value abi(Json::arrayValue); + + for (auto it: _contractDef.interfaceFunctions()) + { + auto externalFunctionType = it.second->interfaceFunctionType(); + Json::Value method; + method["type"] = "function"; + method["name"] = it.second->declaration().name(); + method["constant"] = it.second->isConstant(); + method["payable"] = it.second->isPayable(); + method["inputs"] = formatTypeList( + externalFunctionType->parameterNames(), + externalFunctionType->parameterTypes(), + _contractDef.isLibrary() + ); + method["outputs"] = formatTypeList( + externalFunctionType->returnParameterNames(), + externalFunctionType->returnParameterTypes(), + _contractDef.isLibrary() + ); + abi.append(method); + } + if (_contractDef.constructor()) + { + Json::Value method; + method["type"] = "constructor"; + auto externalFunction = FunctionType(*_contractDef.constructor(), false).interfaceFunctionType(); + solAssert(!!externalFunction, ""); + method["payable"] = externalFunction->isPayable(); + method["inputs"] = formatTypeList( + externalFunction->parameterNames(), + externalFunction->parameterTypes(), + _contractDef.isLibrary() + ); + abi.append(method); + } + if (_contractDef.fallbackFunction()) + { + auto externalFunctionType = FunctionType(*_contractDef.fallbackFunction(), false).interfaceFunctionType(); + solAssert(!!externalFunctionType, ""); + Json::Value method; + method["type"] = "fallback"; + method["payable"] = externalFunctionType->isPayable(); + abi.append(method); + } + for (auto const& it: _contractDef.interfaceEvents()) + { + Json::Value event; + event["type"] = "event"; + event["name"] = it->name(); + event["anonymous"] = it->isAnonymous(); + Json::Value params(Json::arrayValue); + for (auto const& p: it->parameters()) + { + solAssert(!!p->annotation().type->interfaceType(false), ""); + Json::Value input; + input["name"] = p->name(); + input["type"] = p->annotation().type->interfaceType(false)->canonicalName(false); + input["indexed"] = p->isIndexed(); + params.append(input); + } + event["inputs"] = params; + abi.append(event); + } + + return abi; +} + +Json::Value ABI::formatTypeList( + vector<string> const& _names, + vector<TypePointer> const& _types, + bool _forLibrary +) +{ + Json::Value params(Json::arrayValue); + solAssert(_names.size() == _types.size(), "Names and types vector size does not match"); + for (unsigned i = 0; i < _names.size(); ++i) + { + solAssert(_types[i], ""); + Json::Value param; + param["name"] = _names[i]; + param["type"] = _types[i]->canonicalName(_forLibrary); + params.append(param); + } + return params; +} diff --git a/libsolidity/interface/ABI.h b/libsolidity/interface/ABI.h new file mode 100644 index 00000000..95b162a9 --- /dev/null +++ b/libsolidity/interface/ABI.h @@ -0,0 +1,56 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Utilities to handle the Contract ABI (https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI) + */ + +#pragma once + +#include <string> +#include <memory> +#include <json/json.h> + +namespace dev +{ +namespace solidity +{ + +// Forward declarations +class ContractDefinition; +class Type; +using TypePointer = std::shared_ptr<Type const>; + +class ABI +{ +public: + /// Get the ABI Interface of the contract + /// @param _contractDef The contract definition + /// @return A JSONrepresentation of the contract's ABI Interface + static Json::Value generate(ContractDefinition const& _contractDef); +private: + /// @returns a json value suitable for a list of types in function input or output + /// parameters or other places. If @a _forLibrary is true, complex types are referenced + /// by name, otherwise they are anonymously expanded. + static Json::Value formatTypeList( + std::vector<std::string> const& _names, + std::vector<TypePointer> const& _types, + bool _forLibrary + ); +}; + +} +} diff --git a/libsolidity/interface/AssemblyStack.cpp b/libsolidity/interface/AssemblyStack.cpp new file mode 100644 index 00000000..23524bb3 --- /dev/null +++ b/libsolidity/interface/AssemblyStack.cpp @@ -0,0 +1,119 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Full assembly stack that can support EVM-assembly and JULIA as input and EVM, EVM1.5 and + * eWasm as output. + */ + + +#include <libsolidity/interface/AssemblyStack.h> + +#include <libsolidity/parsing/Scanner.h> +#include <libsolidity/inlineasm/AsmPrinter.h> +#include <libsolidity/inlineasm/AsmParser.h> +#include <libsolidity/inlineasm/AsmAnalysis.h> +#include <libsolidity/inlineasm/AsmAnalysisInfo.h> +#include <libsolidity/inlineasm/AsmCodeGen.h> + +#include <libevmasm/Assembly.h> + +#include <libjulia/backends/evm/EVMCodeTransform.h> +#include <libjulia/backends/evm/EVMAssembly.h> + +using namespace std; +using namespace dev; +using namespace dev::solidity; + + +Scanner const& AssemblyStack::scanner() const +{ + solAssert(m_scanner, ""); + return *m_scanner; +} + +bool AssemblyStack::parseAndAnalyze(std::string const& _sourceName, std::string const& _source) +{ + m_errors.clear(); + m_analysisSuccessful = false; + m_scanner = make_shared<Scanner>(CharStream(_source), _sourceName); + m_parserResult = assembly::Parser(m_errorReporter, m_language == Language::JULIA).parse(m_scanner); + if (!m_errorReporter.errors().empty()) + return false; + solAssert(m_parserResult, ""); + + return analyzeParsed(); +} + +bool AssemblyStack::analyze(assembly::Block const& _block, Scanner const* _scanner) +{ + m_errors.clear(); + m_analysisSuccessful = false; + if (_scanner) + m_scanner = make_shared<Scanner>(*_scanner); + m_parserResult = make_shared<assembly::Block>(_block); + + return analyzeParsed(); +} + +bool AssemblyStack::analyzeParsed() +{ + m_analysisInfo = make_shared<assembly::AsmAnalysisInfo>(); + assembly::AsmAnalyzer analyzer(*m_analysisInfo, m_errorReporter, m_language == Language::JULIA); + m_analysisSuccessful = analyzer.analyze(*m_parserResult); + return m_analysisSuccessful; +} + +MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const +{ + solAssert(m_analysisSuccessful, ""); + solAssert(m_parserResult, ""); + solAssert(m_analysisInfo, ""); + + switch (_machine) + { + case Machine::EVM: + { + MachineAssemblyObject object; + eth::Assembly assembly; + assembly::CodeGenerator::assemble(*m_parserResult, *m_analysisInfo, assembly); + object.bytecode = make_shared<eth::LinkerObject>(assembly.assemble()); + ostringstream tmp; + assembly.stream(tmp); + object.assembly = tmp.str(); + return object; + } + case Machine::EVM15: + { + MachineAssemblyObject object; + julia::EVMAssembly assembly(true); + julia::CodeTransform(assembly, *m_analysisInfo, m_language == Language::JULIA, true)(*m_parserResult); + object.bytecode = make_shared<eth::LinkerObject>(assembly.finalize()); + /// TOOD: fill out text representation + return object; + } + case Machine::eWasm: + solUnimplemented("eWasm backend is not yet implemented."); + } + // unreachable + return MachineAssemblyObject(); +} + +string AssemblyStack::print() const +{ + solAssert(m_parserResult, ""); + return assembly::AsmPrinter(m_language == Language::JULIA)(*m_parserResult); +} diff --git a/libsolidity/interface/AssemblyStack.h b/libsolidity/interface/AssemblyStack.h new file mode 100644 index 00000000..2ae596ed --- /dev/null +++ b/libsolidity/interface/AssemblyStack.h @@ -0,0 +1,96 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Full assembly stack that can support EVM-assembly and JULIA as input and EVM, EVM1.5 and + * eWasm as output. + */ + +#pragma once + +#include <libsolidity/interface/ErrorReporter.h> +#include <libevmasm/LinkerObject.h> + +#include <string> +#include <memory> + +namespace dev +{ +namespace solidity +{ +class Scanner; +namespace assembly +{ +struct AsmAnalysisInfo; +struct Block; +} + +struct MachineAssemblyObject +{ + std::shared_ptr<eth::LinkerObject> bytecode; + std::string assembly; +}; + +/* + * Full assembly stack that can support EVM-assembly and JULIA as input and EVM, EVM1.5 and + * eWasm as output. + */ +class AssemblyStack +{ +public: + enum class Language { JULIA, Assembly }; + enum class Machine { EVM, EVM15, eWasm }; + + explicit AssemblyStack(Language _language = Language::Assembly): + m_language(_language), m_errorReporter(m_errors) + {} + + /// @returns the scanner used during parsing + Scanner const& scanner() const; + + /// Runs parsing and analysis steps, returns false if input cannot be assembled. + /// Multiple calls overwrite the previous state. + bool parseAndAnalyze(std::string const& _sourceName, std::string const& _source); + + /// Runs analysis step on the supplied block, returns false if input cannot be assembled. + /// Multiple calls overwrite the previous state. + bool analyze(assembly::Block const& _block, Scanner const* _scanner = nullptr); + + /// Run the assembly step (should only be called after parseAndAnalyze). + MachineAssemblyObject assemble(Machine _machine) const; + + /// @returns the errors generated during parsing, analysis (and potentially assembly). + ErrorList const& errors() const { return m_errors; } + + /// Pretty-print the input after having parsed it. + std::string print() const; + +private: + bool analyzeParsed(); + + Language m_language = Language::Assembly; + + std::shared_ptr<Scanner> m_scanner; + + bool m_analysisSuccessful = false; + std::shared_ptr<assembly::Block> m_parserResult; + std::shared_ptr<assembly::AsmAnalysisInfo> m_analysisInfo; + ErrorList m_errors; + ErrorReporter m_errorReporter; +}; + +} +} diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 9c9c9614..e2507821 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -37,9 +37,9 @@ #include <libsolidity/analysis/PostTypeChecker.h> #include <libsolidity/analysis/SyntaxChecker.h> #include <libsolidity/codegen/Compiler.h> -#include <libsolidity/interface/InterfaceHandler.h> +#include <libsolidity/interface/ABI.h> +#include <libsolidity/interface/Natspec.h> #include <libsolidity/interface/GasEstimator.h> -#include <libsolidity/formal/Why3Translator.h> #include <libevmasm/Exceptions.h> @@ -56,9 +56,6 @@ using namespace std; using namespace dev; using namespace dev::solidity; -CompilerStack::CompilerStack(ReadFile::Callback const& _readFile): - m_readFile(_readFile) {} - void CompilerStack::setRemappings(vector<string> const& _remappings) { vector<Remapping> remappings; @@ -87,6 +84,7 @@ void CompilerStack::reset(bool _keepSources) } else { + m_stackState = Empty; m_sources.clear(); } m_optimize = false; @@ -95,8 +93,7 @@ void CompilerStack::reset(bool _keepSources) m_scopes.clear(); m_sourceOrder.clear(); m_contracts.clear(); - m_errors.clear(); - m_stackState = Empty; + m_errorReporter.clear(); } bool CompilerStack::addSource(string const& _name, string const& _content, bool _isLibrary) @@ -120,15 +117,11 @@ bool CompilerStack::parse() //reset if(m_stackState != SourcesSet) return false; - m_errors.clear(); + m_errorReporter.clear(); ASTNode::resetID(); if (SemVerVersion{string(VersionString)}.isPrerelease()) - { - auto err = make_shared<Error>(Error::Type::Warning); - *err << errinfo_comment("This is a pre-release compiler version, please do not use it in production."); - m_errors.push_back(err); - } + m_errorReporter.warning("This is a pre-release compiler version, please do not use it in production."); vector<string> sourcesToParse; for (auto const& s: m_sources) @@ -138,9 +131,9 @@ bool CompilerStack::parse() string const& path = sourcesToParse[i]; Source& source = m_sources[path]; source.scanner->reset(); - source.ast = Parser(m_errors).parse(source.scanner); + source.ast = Parser(m_errorReporter).parse(source.scanner); if (!source.ast) - solAssert(!Error::containsOnlyWarnings(m_errors), "Parser returned null but did not report error."); + solAssert(!Error::containsOnlyWarnings(m_errorReporter.errors()), "Parser returned null but did not report error."); else { source.ast->annotation().path = path; @@ -153,7 +146,7 @@ bool CompilerStack::parse() } } } - if (Error::containsOnlyWarnings(m_errors)) + if (Error::containsOnlyWarnings(m_errorReporter.errors())) { m_stackState = ParsingSuccessful; return true; @@ -169,18 +162,18 @@ bool CompilerStack::analyze() resolveImports(); bool noErrors = true; - SyntaxChecker syntaxChecker(m_errors); + SyntaxChecker syntaxChecker(m_errorReporter); for (Source const* source: m_sourceOrder) if (!syntaxChecker.checkSyntax(*source->ast)) noErrors = false; - DocStringAnalyser docStringAnalyser(m_errors); + DocStringAnalyser docStringAnalyser(m_errorReporter); for (Source const* source: m_sourceOrder) if (!docStringAnalyser.analyseDocStrings(*source->ast)) noErrors = false; m_globalContext = make_shared<GlobalContext>(); - NameAndTypeResolver resolver(m_globalContext->declarations(), m_scopes, m_errors); + NameAndTypeResolver resolver(m_globalContext->declarations(), m_scopes, m_errorReporter); for (Source const* source: m_sourceOrder) if (!resolver.registerDeclarations(*source->ast)) return false; @@ -216,11 +209,11 @@ bool CompilerStack::analyze() { m_globalContext->setCurrentContract(*contract); resolver.updateDeclaration(*m_globalContext->currentThis()); - TypeChecker typeChecker(m_errors); + TypeChecker typeChecker(m_errorReporter); if (typeChecker.checkTypeRequirements(*contract)) { - contract->setDevDocumentation(InterfaceHandler::devDocumentation(*contract)); - contract->setUserDocumentation(InterfaceHandler::userDocumentation(*contract)); + contract->setDevDocumentation(Natspec::devDocumentation(*contract)); + contract->setUserDocumentation(Natspec::userDocumentation(*contract)); } else noErrors = false; @@ -236,7 +229,7 @@ bool CompilerStack::analyze() if (noErrors) { - PostTypeChecker postTypeChecker(m_errors); + PostTypeChecker postTypeChecker(m_errorReporter); for (Source const* source: m_sourceOrder) if (!postTypeChecker.check(*source->ast)) noErrors = false; @@ -244,7 +237,7 @@ bool CompilerStack::analyze() if (noErrors) { - StaticAnalyzer staticAnalyzer(m_errors); + StaticAnalyzer staticAnalyzer(m_errorReporter); for (Source const* source: m_sourceOrder) if (!staticAnalyzer.analyze(*source->ast)) noErrors = false; @@ -322,20 +315,6 @@ void CompilerStack::link() } } -bool CompilerStack::prepareFormalAnalysis(ErrorList* _errors) -{ - if (!_errors) - _errors = &m_errors; - Why3Translator translator(*_errors); - for (Source const* source: m_sourceOrder) - if (!translator.process(*source->ast)) - return false; - - m_formalTranslation = translator.translation(); - - return true; -} - eth::AssemblyItems const* CompilerStack::assemblyItems(string const& _contractName) const { Contract const& currentContract = contract(_contractName); @@ -406,15 +385,6 @@ eth::LinkerObject const& CompilerStack::cloneObject(string const& _contractName) return contract(_contractName).cloneObject; } -dev::h256 CompilerStack::contractCodeHash(string const& _contractName) const -{ - auto const& obj = runtimeObject(_contractName); - if (obj.bytecode.empty() || !obj.linkReferences.empty()) - return dev::h256(); - else - return dev::keccak256(obj.bytecode); -} - Json::Value CompilerStack::streamAssembly(ostream& _outStream, string const& _contractName, StringMap _sourceCodes, bool _inJsonFormat) const { Contract const& currentContract = contract(_contractName); @@ -444,20 +414,31 @@ map<string, unsigned> CompilerStack::sourceIndices() const return indices; } -Json::Value const& CompilerStack::interface(string const& _contractName) const +Json::Value const& CompilerStack::contractABI(string const& _contractName) const { - return metadata(_contractName, DocumentationType::ABIInterface); + return contractABI(contract(_contractName)); } -Json::Value const& CompilerStack::metadata(string const& _contractName, DocumentationType _type) const +Json::Value const& CompilerStack::contractABI(Contract const& _contract) const { if (m_stackState < AnalysisSuccessful) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful.")); - return metadata(contract(_contractName), _type); + solAssert(_contract.contract, ""); + + // caches the result + if (!_contract.abi) + _contract.abi.reset(new Json::Value(ABI::generate(*_contract.contract))); + + return *_contract.abi; } -Json::Value const& CompilerStack::metadata(Contract const& _contract, DocumentationType _type) const +Json::Value const& CompilerStack::natspec(string const& _contractName, DocumentationType _type) const +{ + return natspec(contract(_contractName), _type); +} + +Json::Value const& CompilerStack::natspec(Contract const& _contract, DocumentationType _type) const { if (m_stackState < AnalysisSuccessful) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful.")); @@ -474,40 +455,54 @@ Json::Value const& CompilerStack::metadata(Contract const& _contract, Documentat case DocumentationType::NatspecDev: doc = &_contract.devDocumentation; break; - case DocumentationType::ABIInterface: - doc = &_contract.interface; - break; default: BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Illegal documentation type.")); } // caches the result if (!*doc) - doc->reset(new Json::Value(InterfaceHandler::documentation(*_contract.contract, _type))); + doc->reset(new Json::Value(Natspec::documentation(*_contract.contract, _type))); return *(*doc); } +Json::Value CompilerStack::methodIdentifiers(string const& _contractName) const +{ + Json::Value methodIdentifiers(Json::objectValue); + for (auto const& it: contractDefinition(_contractName).interfaceFunctions()) + methodIdentifiers[it.second->externalSignature()] = toHex(it.first.ref()); + return methodIdentifiers; +} + string const& CompilerStack::onChainMetadata(string const& _contractName) const { if (m_stackState != CompilationSuccessful) - BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful.")); + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful.")); return contract(_contractName).onChainMetadata; } Scanner const& CompilerStack::scanner(string const& _sourceName) const { + if (m_stackState < SourcesSet) + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("No sources set.")); + return *source(_sourceName).scanner; } SourceUnit const& CompilerStack::ast(string const& _sourceName) const { + if (m_stackState < ParsingSuccessful) + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful.")); + return *source(_sourceName).ast; } ContractDefinition const& CompilerStack::contractDefinition(string const& _contractName) const { + if (m_stackState != CompilationSuccessful) + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful.")); + return *contract(_contractName).contract; } @@ -564,11 +559,10 @@ StringMap CompilerStack::loadMissingSources(SourceUnit const& _ast, std::string newSources[importPath] = result.contentsOrErrorMessage; else { - auto err = make_shared<Error>(Error::Type::ParserError); - *err << - errinfo_sourceLocation(import->location()) << - errinfo_comment("Source \"" + importPath + "\" not found: " + result.contentsOrErrorMessage); - m_errors.push_back(std::move(err)); + m_errorReporter.parserError( + import->location(), + string("Source \"" + importPath + "\" not found: " + result.contentsOrErrorMessage) + ); continue; } } @@ -734,11 +728,6 @@ void CompilerStack::compileContract( } } -std::string CompilerStack::defaultContractName() const -{ - return contract("").contract->name(); -} - CompilerStack::Contract const& CompilerStack::contract(string const& _contractName) const { if (m_contracts.empty()) @@ -821,9 +810,9 @@ string CompilerStack::createOnChainMetadata(Contract const& _contract) const for (auto const& library: m_libraries) meta["settings"]["libraries"][library.first] = "0x" + toHex(library.second.asBytes()); - meta["output"]["abi"] = metadata(_contract, DocumentationType::ABIInterface); - meta["output"]["userdoc"] = metadata(_contract, DocumentationType::NatspecUser); - meta["output"]["devdoc"] = metadata(_contract, DocumentationType::NatspecDev); + meta["output"]["abi"] = contractABI(_contract); + meta["output"]["userdoc"] = natspec(_contract, DocumentationType::NatspecUser); + meta["output"]["devdoc"] = natspec(_contract, DocumentationType::NatspecDev); return jsonCompactPrint(meta); } diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index c1d344ca..03a1b806 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -35,7 +35,7 @@ #include <libdevcore/FixedHash.h> #include <libevmasm/SourceLocation.h> #include <libevmasm/LinkerObject.h> -#include <libsolidity/interface/Exceptions.h> +#include <libsolidity/interface/ErrorReporter.h> #include <libsolidity/interface/ReadFile.h> namespace dev @@ -59,15 +59,14 @@ class FunctionDefinition; class SourceUnit; class Compiler; class GlobalContext; -class InterfaceHandler; +class Natspec; class Error; class DeclarationContainer; enum class DocumentationType: uint8_t { NatspecUser = 1, - NatspecDev, - ABIInterface + NatspecDev }; /** @@ -78,10 +77,21 @@ enum class DocumentationType: uint8_t class CompilerStack: boost::noncopyable { public: + enum State { + Empty, + SourcesSet, + ParsingSuccessful, + AnalysisSuccessful, + CompilationSuccessful + }; + /// Creates a new compiler stack. /// @param _readFile callback to used to read files for import statements. Must return /// and must not emit exceptions. - explicit CompilerStack(ReadFile::Callback const& _readFile = ReadFile::Callback()); + explicit CompilerStack(ReadFile::Callback const& _readFile = ReadFile::Callback()): + m_readFile(_readFile), + m_errorList(), + m_errorReporter(m_errorList) {} /// Sets path remappings in the format "context:prefix=target" void setRemappings(std::vector<std::string> const& _remappings); @@ -115,7 +125,6 @@ public: bool parseAndAnalyze(std::string const& _sourceCode); /// @returns a list of the contract names in the sources. std::vector<std::string> contractNames() const; - std::string defaultContractName() const; /// Compiles the source units that were previously added and parsed. /// @returns false on error. @@ -128,12 +137,6 @@ public: /// @returns false on error. bool compile(std::string const& _sourceCode, bool _optimize = false, unsigned _runs = 200); - /// Tries to translate all source files into a language suitable for formal analysis. - /// @param _errors list to store errors - defaults to the internal error list. - /// @returns false on error. - bool prepareFormalAnalysis(ErrorList* _errors = nullptr); - std::string const& formalTranslation() const { return m_formalTranslation; } - /// @returns the assembled object for a contract. eth::LinkerObject const& object(std::string const& _contractName = "") const; /// @returns the runtime object for the contract. @@ -157,11 +160,6 @@ public: /// @returns either the contract's name or a mixture of its name and source file, sanitized for filesystem use std::string const filesystemFriendlyName(std::string const& _contractName) const; - /// @returns hash of the runtime bytecode for the contract, i.e. the code that is - /// returned by the constructor or the zero-h256 if the contract still needs to be linked or - /// does not have runtime code. - dev::h256 contractCodeHash(std::string const& _contractName = "") const; - /// Streams a verbose version of the assembly to @a _outStream. /// @arg _sourceCodes is the map of input files to source code strings /// @arg _inJsonFromat shows whether the out should be in Json format @@ -173,14 +171,18 @@ public: /// @returns a mapping assigning each source name its index inside the vector returned /// by sourceNames(). std::map<std::string, unsigned> sourceIndices() const; - /// @returns a JSON representing the contract interface. + /// @returns a JSON representing the contract ABI. /// Prerequisite: Successful call to parse or compile. - Json::Value const& interface(std::string const& _contractName = "") const; + Json::Value const& contractABI(std::string const& _contractName = "") const; /// @returns a JSON representing the contract's documentation. /// Prerequisite: Successful call to parse or compile. /// @param type The type of the documentation to get. /// Can be one of 4 types defined at @c DocumentationType - Json::Value const& metadata(std::string const& _contractName, DocumentationType _type) const; + Json::Value const& natspec(std::string const& _contractName, DocumentationType _type) const; + + /// @returns a JSON representing a map of method identifiers (hashes) to function names. + Json::Value methodIdentifiers(std::string const& _contractName) const; + std::string const& onChainMetadata(std::string const& _contractName) const; void useMetadataLiteralSources(bool _metadataLiteralSources) { m_metadataLiteralSources = _metadataLiteralSources; } @@ -191,16 +193,6 @@ public: Scanner const& scanner(std::string const& _sourceName = "") const; /// @returns the parsed source unit with the supplied name. SourceUnit const& ast(std::string const& _sourceName = "") const; - /// @returns the parsed contract with the supplied name. Throws an exception if the contract - /// does not exist. - ContractDefinition const& contractDefinition(std::string const& _contractName) const; - - /// @returns the offset of the entry point of the given function into the list of assembly items - /// or zero if it is not found or does not exist. - size_t functionEntryPoint( - std::string const& _contractName, - FunctionDefinition const& _function - ) const; /// Helper function for logs printing. Do only use in error cases, it's quite expensive. /// line and columns are numbered starting from 1 with following order: @@ -208,7 +200,9 @@ public: std::tuple<int, int, int, int> positionFromSourceLocation(SourceLocation const& _sourceLocation) const; /// @returns the list of errors that occured during parsing and type checking. - ErrorList const& errors() const { return m_errors; } + ErrorList const& errors() { return m_errorReporter.errors(); } + + State state() const { return m_stackState; } private: /** @@ -230,20 +224,12 @@ private: eth::LinkerObject runtimeObject; eth::LinkerObject cloneObject; std::string onChainMetadata; ///< The metadata json that will be hashed into the chain. - mutable std::unique_ptr<Json::Value const> interface; + mutable std::unique_ptr<Json::Value const> abi; mutable std::unique_ptr<Json::Value const> userDocumentation; mutable std::unique_ptr<Json::Value const> devDocumentation; mutable std::unique_ptr<std::string const> sourceMapping; mutable std::unique_ptr<std::string const> runtimeSourceMapping; }; - enum State { - Empty, - SourcesSet, - ParsingSuccessful, - AnalysisSuccessful, - CompilationSuccessful - }; - /// Loads the missing sources from @a _ast (named @a _path) using the callback /// @a m_readFile and stores the absolute paths of all imports in the AST annotations. /// @returns the newly loaded sources. @@ -265,9 +251,21 @@ private: Contract const& contract(std::string const& _contractName = "") const; Source const& source(std::string const& _sourceName = "") const; + /// @returns the parsed contract with the supplied name. Throws an exception if the contract + /// does not exist. + ContractDefinition const& contractDefinition(std::string const& _contractName) const; + std::string createOnChainMetadata(Contract const& _contract) const; std::string computeSourceMapping(eth::AssemblyItems const& _items) const; - Json::Value const& metadata(Contract const&, DocumentationType _type) const; + Json::Value const& contractABI(Contract const&) const; + Json::Value const& natspec(Contract const&, DocumentationType _type) const; + + /// @returns the offset of the entry point of the given function into the list of assembly items + /// or zero if it is not found or does not exist. + size_t functionEntryPoint( + std::string const& _contractName, + FunctionDefinition const& _function + ) const; struct Remapping { @@ -288,8 +286,8 @@ private: std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>> m_scopes; std::vector<Source const*> m_sourceOrder; std::map<std::string const, Contract> m_contracts; - std::string m_formalTranslation; - ErrorList m_errors; + ErrorList m_errorList; + ErrorReporter m_errorReporter; bool m_metadataLiteralSources = false; State m_stackState = Empty; }; diff --git a/libsolidity/interface/ErrorReporter.cpp b/libsolidity/interface/ErrorReporter.cpp new file mode 100644 index 00000000..6e2667a5 --- /dev/null +++ b/libsolidity/interface/ErrorReporter.cpp @@ -0,0 +1,167 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * @author Rhett <roadriverrail@gmail.com> + * @date 2017 + * Error helper class. + */ + +#include <libsolidity/interface/ErrorReporter.h> +#include <libsolidity/ast/AST.h> +#include <memory> + +using namespace std; +using namespace dev; +using namespace dev::solidity; + +ErrorReporter& ErrorReporter::operator=(ErrorReporter const& _errorReporter) +{ + if (&_errorReporter == this) + return *this; + m_errorList = _errorReporter.m_errorList; + return *this; +} + + +void ErrorReporter::warning(string const& _description) +{ + error(Error::Type::Warning, SourceLocation(), _description); +} + +void ErrorReporter::warning(SourceLocation const& _location, string const& _description) +{ + error(Error::Type::Warning, _location, _description); +} + +void ErrorReporter::error(Error::Type _type, SourceLocation const& _location, string const& _description) +{ + auto err = make_shared<Error>(_type); + *err << + errinfo_sourceLocation(_location) << + errinfo_comment(_description); + + m_errorList.push_back(err); +} + +void ErrorReporter::error(Error::Type _type, SourceLocation const& _location, SecondarySourceLocation const& _secondaryLocation, string const& _description) +{ + auto err = make_shared<Error>(_type); + *err << + errinfo_sourceLocation(_location) << + errinfo_secondarySourceLocation(_secondaryLocation) << + errinfo_comment(_description); + + m_errorList.push_back(err); +} + + +void ErrorReporter::fatalError(Error::Type _type, SourceLocation const& _location, string const& _description) +{ + error(_type, _location, _description); + BOOST_THROW_EXCEPTION(FatalError()); +} + +ErrorList const& ErrorReporter::errors() const +{ + return m_errorList; +} + +void ErrorReporter::clear() +{ + m_errorList.clear(); +} + +void ErrorReporter::declarationError(SourceLocation const& _location, SecondarySourceLocation const&_secondaryLocation, string const& _description) +{ + error( + Error::Type::DeclarationError, + _location, + _secondaryLocation, + _description + ); +} + +void ErrorReporter::declarationError(SourceLocation const& _location, string const& _description) +{ + error( + Error::Type::DeclarationError, + _location, + _description + ); +} + +void ErrorReporter::fatalDeclarationError(SourceLocation const& _location, std::string const& _description) +{ + fatalError( + Error::Type::DeclarationError, + _location, + _description); +} + +void ErrorReporter::parserError(SourceLocation const& _location, string const& _description) +{ + error( + Error::Type::ParserError, + _location, + _description + ); +} + +void ErrorReporter::fatalParserError(SourceLocation const& _location, string const& _description) +{ + fatalError( + Error::Type::ParserError, + _location, + _description + ); +} + +void ErrorReporter::syntaxError(SourceLocation const& _location, string const& _description) +{ + error( + Error::Type::SyntaxError, + _location, + _description + ); +} + +void ErrorReporter::typeError(SourceLocation const& _location, string const& _description) +{ + error( + Error::Type::TypeError, + _location, + _description + ); +} + + +void ErrorReporter::fatalTypeError(SourceLocation const& _location, string const& _description) +{ + fatalError(Error::Type::TypeError, + _location, + _description + ); +} + +void ErrorReporter::docstringParsingError(string const& _description) +{ + error( + Error::Type::DocstringParsingError, + SourceLocation(), + _description + ); +} diff --git a/libsolidity/interface/ErrorReporter.h b/libsolidity/interface/ErrorReporter.h new file mode 100644 index 00000000..e5605d24 --- /dev/null +++ b/libsolidity/interface/ErrorReporter.h @@ -0,0 +1,102 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * @author Rhett <roadriverrail@gmail.com> + * @date 2017 + * Error reporting helper class. + */ + +#pragma once + +#include <libsolidity/interface/Exceptions.h> +#include <libevmasm/SourceLocation.h> + +namespace dev +{ +namespace solidity +{ + +class ASTNode; + +class ErrorReporter +{ +public: + + ErrorReporter(ErrorList& _errors): + m_errorList(_errors) { } + + ErrorReporter& operator=(ErrorReporter const& _errorReporter); + + void warning(std::string const& _description = std::string()); + + void warning( + SourceLocation const& _location = SourceLocation(), + std::string const& _description = std::string() + ); + + void error( + Error::Type _type, + SourceLocation const& _location = SourceLocation(), + std::string const& _description = std::string() + ); + + void declarationError( + SourceLocation const& _location, + SecondarySourceLocation const& _secondaryLocation = SecondarySourceLocation(), + std::string const& _description = std::string() + ); + + void declarationError( + SourceLocation const& _location, + std::string const& _description = std::string() + ); + + void fatalDeclarationError(SourceLocation const& _location, std::string const& _description); + + void parserError(SourceLocation const& _location, std::string const& _description); + + void fatalParserError(SourceLocation const& _location, std::string const& _description); + + void syntaxError(SourceLocation const& _location, std::string const& _description); + + void typeError(SourceLocation const& _location, std::string const& _description); + + void fatalTypeError(SourceLocation const& _location, std::string const& _description); + + void docstringParsingError(std::string const& _location); + + ErrorList const& errors() const; + + void clear(); + +private: + void error(Error::Type _type, + SourceLocation const& _location, + SecondarySourceLocation const& _secondaryLocation, + std::string const& _description = std::string()); + + void fatalError(Error::Type _type, + SourceLocation const& _location = SourceLocation(), + std::string const& _description = std::string()); + + ErrorList& m_errorList; +}; + + +} +} + diff --git a/libsolidity/interface/Exceptions.cpp b/libsolidity/interface/Exceptions.cpp index c09180de..9f2a2d06 100644 --- a/libsolidity/interface/Exceptions.cpp +++ b/libsolidity/interface/Exceptions.cpp @@ -21,7 +21,6 @@ */ #include <libsolidity/interface/Exceptions.h> -#include <libsolidity/interface/Utils.h> using namespace std; using namespace dev; @@ -47,9 +46,6 @@ Error::Error(Type _type, SourceLocation const& _location, string const& _descrip case Type::TypeError: m_typeName = "TypeError"; break; - case Type::Why3TranslatorError: - m_typeName = "Why3TranslatorError"; - break; case Type::Warning: m_typeName = "Warning"; break; diff --git a/libsolidity/interface/Exceptions.h b/libsolidity/interface/Exceptions.h index 0803d8cc..09301b10 100644 --- a/libsolidity/interface/Exceptions.h +++ b/libsolidity/interface/Exceptions.h @@ -25,6 +25,7 @@ #include <string> #include <utility> #include <libdevcore/Exceptions.h> +#include <libdevcore/Assertions.h> #include <libevmasm/SourceLocation.h> namespace dev @@ -39,6 +40,16 @@ struct InternalCompilerError: virtual Exception {}; struct FatalError: virtual Exception {}; struct UnimplementedFeatureError: virtual Exception{}; +/// Assertion that throws an InternalCompilerError containing the given description if it is not met. +#define solAssert(CONDITION, DESCRIPTION) \ + assertThrow(CONDITION, ::dev::solidity::InternalCompilerError, DESCRIPTION) + +#define solUnimplementedAssert(CONDITION, DESCRIPTION) \ + assertThrow(CONDITION, ::dev::solidity::UnimplementedFeatureError, DESCRIPTION) + +#define solUnimplemented(DESCRIPTION) \ + solUnimplementedAssert(false, DESCRIPTION) + class Error: virtual public Exception { public: @@ -49,7 +60,6 @@ public: ParserError, TypeError, SyntaxError, - Why3TranslatorError, Warning }; diff --git a/libsolidity/interface/InterfaceHandler.cpp b/libsolidity/interface/InterfaceHandler.cpp deleted file mode 100644 index 6c1bb0c4..00000000 --- a/libsolidity/interface/InterfaceHandler.cpp +++ /dev/null @@ -1,197 +0,0 @@ - -#include <libsolidity/interface/InterfaceHandler.h> -#include <boost/range/irange.hpp> -#include <libsolidity/ast/AST.h> -#include <libsolidity/interface/CompilerStack.h> - -using namespace std; -using namespace dev; -using namespace dev::solidity; - -Json::Value InterfaceHandler::documentation( - ContractDefinition const& _contractDef, - DocumentationType _type -) -{ - switch(_type) - { - case DocumentationType::NatspecUser: - return userDocumentation(_contractDef); - case DocumentationType::NatspecDev: - return devDocumentation(_contractDef); - case DocumentationType::ABIInterface: - return abiInterface(_contractDef); - } - - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown documentation type")); -} - -Json::Value InterfaceHandler::abiInterface(ContractDefinition const& _contractDef) -{ - Json::Value abi(Json::arrayValue); - - for (auto it: _contractDef.interfaceFunctions()) - { - auto externalFunctionType = it.second->interfaceFunctionType(); - Json::Value method; - method["type"] = "function"; - method["name"] = it.second->declaration().name(); - method["constant"] = it.second->isConstant(); - method["payable"] = it.second->isPayable(); - method["inputs"] = formatTypeList( - externalFunctionType->parameterNames(), - externalFunctionType->parameterTypes(), - _contractDef.isLibrary() - ); - method["outputs"] = formatTypeList( - externalFunctionType->returnParameterNames(), - externalFunctionType->returnParameterTypes(), - _contractDef.isLibrary() - ); - abi.append(method); - } - if (_contractDef.constructor()) - { - Json::Value method; - method["type"] = "constructor"; - auto externalFunction = FunctionType(*_contractDef.constructor(), false).interfaceFunctionType(); - solAssert(!!externalFunction, ""); - method["payable"] = externalFunction->isPayable(); - method["inputs"] = formatTypeList( - externalFunction->parameterNames(), - externalFunction->parameterTypes(), - _contractDef.isLibrary() - ); - abi.append(method); - } - if (_contractDef.fallbackFunction()) - { - auto externalFunctionType = FunctionType(*_contractDef.fallbackFunction(), false).interfaceFunctionType(); - solAssert(!!externalFunctionType, ""); - Json::Value method; - method["type"] = "fallback"; - method["payable"] = externalFunctionType->isPayable(); - abi.append(method); - } - for (auto const& it: _contractDef.interfaceEvents()) - { - Json::Value event; - event["type"] = "event"; - event["name"] = it->name(); - event["anonymous"] = it->isAnonymous(); - Json::Value params(Json::arrayValue); - for (auto const& p: it->parameters()) - { - solAssert(!!p->annotation().type->interfaceType(false), ""); - Json::Value input; - input["name"] = p->name(); - input["type"] = p->annotation().type->interfaceType(false)->canonicalName(false); - input["indexed"] = p->isIndexed(); - params.append(input); - } - event["inputs"] = params; - abi.append(event); - } - - return abi; -} - -Json::Value InterfaceHandler::userDocumentation(ContractDefinition const& _contractDef) -{ - Json::Value doc; - Json::Value methods(Json::objectValue); - - for (auto const& it: _contractDef.interfaceFunctions()) - if (it.second->hasDeclaration()) - if (auto const* f = dynamic_cast<FunctionDefinition const*>(&it.second->declaration())) - { - string value = extractDoc(f->annotation().docTags, "notice"); - if (!value.empty()) - { - Json::Value user; - // since @notice is the only user tag if missing function should not appear - user["notice"] = Json::Value(value); - methods[it.second->externalSignature()] = user; - } - } - doc["methods"] = methods; - - return doc; -} - -Json::Value InterfaceHandler::devDocumentation(ContractDefinition const& _contractDef) -{ - Json::Value doc; - Json::Value methods(Json::objectValue); - - auto author = extractDoc(_contractDef.annotation().docTags, "author"); - if (!author.empty()) - doc["author"] = author; - auto title = extractDoc(_contractDef.annotation().docTags, "title"); - if (!title.empty()) - doc["title"] = title; - - for (auto const& it: _contractDef.interfaceFunctions()) - { - if (!it.second->hasDeclaration()) - continue; - Json::Value method; - if (auto fun = dynamic_cast<FunctionDefinition const*>(&it.second->declaration())) - { - auto dev = extractDoc(fun->annotation().docTags, "dev"); - if (!dev.empty()) - method["details"] = Json::Value(dev); - - auto author = extractDoc(fun->annotation().docTags, "author"); - if (!author.empty()) - method["author"] = author; - - auto ret = extractDoc(fun->annotation().docTags, "return"); - if (!ret.empty()) - method["return"] = ret; - - Json::Value params(Json::objectValue); - auto paramRange = fun->annotation().docTags.equal_range("param"); - for (auto i = paramRange.first; i != paramRange.second; ++i) - params[i->second.paramName] = Json::Value(i->second.content); - - if (!params.empty()) - method["params"] = params; - - if (!method.empty()) - // add the function, only if we have any documentation to add - methods[it.second->externalSignature()] = method; - } - } - doc["methods"] = methods; - - return doc; -} - -Json::Value InterfaceHandler::formatTypeList( - vector<string> const& _names, - vector<TypePointer> const& _types, - bool _forLibrary -) -{ - Json::Value params(Json::arrayValue); - solAssert(_names.size() == _types.size(), "Names and types vector size does not match"); - for (unsigned i = 0; i < _names.size(); ++i) - { - solAssert(_types[i], ""); - Json::Value param; - param["name"] = _names[i]; - param["type"] = _types[i]->canonicalName(_forLibrary); - params.append(param); - } - return params; -} - -string InterfaceHandler::extractDoc(multimap<string, DocTag> const& _tags, string const& _name) -{ - string value; - auto range = _tags.equal_range(_name); - for (auto i = range.first; i != range.second; i++) - value += i->second.content; - return value; -} diff --git a/libsolidity/interface/Natspec.cpp b/libsolidity/interface/Natspec.cpp new file mode 100644 index 00000000..70486e23 --- /dev/null +++ b/libsolidity/interface/Natspec.cpp @@ -0,0 +1,130 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * @author Lefteris <lefteris@ethdev.com> + * @date 2014 + * Takes the parsed AST and produces the Natspec documentation: + * https://github.com/ethereum/wiki/wiki/Ethereum-Natural-Specification-Format + * + * Can generally deal with JSON files + */ + +#include <libsolidity/interface/Natspec.h> +#include <boost/range/irange.hpp> +#include <libsolidity/ast/AST.h> +#include <libsolidity/interface/CompilerStack.h> + +using namespace std; +using namespace dev; +using namespace dev::solidity; + +Json::Value Natspec::documentation( + ContractDefinition const& _contractDef, + DocumentationType _type +) +{ + switch(_type) + { + case DocumentationType::NatspecUser: + return userDocumentation(_contractDef); + case DocumentationType::NatspecDev: + return devDocumentation(_contractDef); + } + + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown documentation type")); +} + +Json::Value Natspec::userDocumentation(ContractDefinition const& _contractDef) +{ + Json::Value doc; + Json::Value methods(Json::objectValue); + + for (auto const& it: _contractDef.interfaceFunctions()) + if (it.second->hasDeclaration()) + if (auto const* f = dynamic_cast<FunctionDefinition const*>(&it.second->declaration())) + { + string value = extractDoc(f->annotation().docTags, "notice"); + if (!value.empty()) + { + Json::Value user; + // since @notice is the only user tag if missing function should not appear + user["notice"] = Json::Value(value); + methods[it.second->externalSignature()] = user; + } + } + doc["methods"] = methods; + + return doc; +} + +Json::Value Natspec::devDocumentation(ContractDefinition const& _contractDef) +{ + Json::Value doc; + Json::Value methods(Json::objectValue); + + auto author = extractDoc(_contractDef.annotation().docTags, "author"); + if (!author.empty()) + doc["author"] = author; + auto title = extractDoc(_contractDef.annotation().docTags, "title"); + if (!title.empty()) + doc["title"] = title; + + for (auto const& it: _contractDef.interfaceFunctions()) + { + if (!it.second->hasDeclaration()) + continue; + Json::Value method; + if (auto fun = dynamic_cast<FunctionDefinition const*>(&it.second->declaration())) + { + auto dev = extractDoc(fun->annotation().docTags, "dev"); + if (!dev.empty()) + method["details"] = Json::Value(dev); + + auto author = extractDoc(fun->annotation().docTags, "author"); + if (!author.empty()) + method["author"] = author; + + auto ret = extractDoc(fun->annotation().docTags, "return"); + if (!ret.empty()) + method["return"] = ret; + + Json::Value params(Json::objectValue); + auto paramRange = fun->annotation().docTags.equal_range("param"); + for (auto i = paramRange.first; i != paramRange.second; ++i) + params[i->second.paramName] = Json::Value(i->second.content); + + if (!params.empty()) + method["params"] = params; + + if (!method.empty()) + // add the function, only if we have any documentation to add + methods[it.second->externalSignature()] = method; + } + } + doc["methods"] = methods; + + return doc; +} + +string Natspec::extractDoc(multimap<string, DocTag> const& _tags, string const& _name) +{ + string value; + auto range = _tags.equal_range(_name); + for (auto i = range.first; i != range.second; i++) + value += i->second.content; + return value; +} diff --git a/libsolidity/interface/InterfaceHandler.h b/libsolidity/interface/Natspec.h index 56927d44..bec9acd2 100644 --- a/libsolidity/interface/InterfaceHandler.h +++ b/libsolidity/interface/Natspec.h @@ -17,8 +17,7 @@ /** * @author Lefteris <lefteris@ethdev.com> * @date 2014 - * Takes the parsed AST and produces the Natspec - * documentation and the ABI interface + * Takes the parsed AST and produces the Natspec documentation: * https://github.com/ethereum/wiki/wiki/Ethereum-Natural-Specification-Format * * Can generally deal with JSON files @@ -59,7 +58,7 @@ enum class CommentOwner Function }; -class InterfaceHandler +class Natspec { public: /// Get the given type of documentation @@ -71,10 +70,6 @@ public: ContractDefinition const& _contractDef, DocumentationType _type ); - /// Get the ABI Interface of the contract - /// @param _contractDef The contract definition - /// @return A JSONrepresentation of the contract's ABI Interface - static Json::Value abiInterface(ContractDefinition const& _contractDef); /// Get the User documentation of the contract /// @param _contractDef The contract definition /// @return A JSON representation of the contract's user documentation diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index 223cc15d..e677afc8 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -115,14 +115,6 @@ StringMap createSourceList(Json::Value const& _input) return sources; } -Json::Value methodIdentifiers(ContractDefinition const& _contract) -{ - Json::Value methodIdentifiers(Json::objectValue); - for (auto const& it: _contract.interfaceFunctions()) - methodIdentifiers[it.second->externalSignature()] = toHex(it.first.ref()); - return methodIdentifiers; -} - Json::Value formatLinkReferences(std::map<size_t, std::string> const& linkReferences) { Json::Value ret(Json::objectValue); @@ -273,11 +265,9 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) auto scannerFromSourceName = [&](string const& _sourceName) -> solidity::Scanner const& { return m_compilerStack.scanner(_sourceName); }; - bool success = false; - try { - success = m_compilerStack.compile(optimize, optimizeRuns, libraries); + m_compilerStack.compile(optimize, optimizeRuns, libraries); for (auto const& error: m_compilerStack.errors()) { @@ -367,22 +357,26 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) if (errors.size() > 0) output["errors"] = errors; + bool parsingSuccess = m_compilerStack.state() >= CompilerStack::State::ParsingSuccessful; + bool compilationSuccess = m_compilerStack.state() == CompilerStack::State::CompilationSuccessful; + /// Inconsistent state - stop here to receive error reports from users - if (!success && (errors.size() == 0)) + if (!compilationSuccess && (errors.size() == 0)) return formatFatalError("InternalCompilerError", "No error reported, but compilation failed."); output["sources"] = Json::objectValue; unsigned sourceIndex = 0; - for (auto const& source: m_compilerStack.sourceNames()) + for (auto const& source: parsingSuccess ? m_compilerStack.sourceNames() : vector<string>()) { Json::Value sourceResult = Json::objectValue; sourceResult["id"] = sourceIndex++; - sourceResult["legacyAST"] = ASTJsonConverter(m_compilerStack.ast(source), m_compilerStack.sourceIndices()).json(); + sourceResult["ast"] = ASTJsonConverter(false, m_compilerStack.sourceIndices()).toJson(m_compilerStack.ast(source)); + sourceResult["legacyAST"] = ASTJsonConverter(true, m_compilerStack.sourceIndices()).toJson(m_compilerStack.ast(source)); output["sources"][source] = sourceResult; } Json::Value contractsOutput = Json::objectValue; - for (string const& contractName: success ? m_compilerStack.contractNames() : vector<string>()) + for (string const& contractName: compilationSuccess ? m_compilerStack.contractNames() : vector<string>()) { size_t colon = contractName.find(':'); solAssert(colon != string::npos, ""); @@ -391,10 +385,10 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) // ABI, documentation and metadata Json::Value contractData(Json::objectValue); - contractData["abi"] = m_compilerStack.metadata(contractName, DocumentationType::ABIInterface); + contractData["abi"] = m_compilerStack.contractABI(contractName); contractData["metadata"] = m_compilerStack.onChainMetadata(contractName); - contractData["userdoc"] = m_compilerStack.metadata(contractName, DocumentationType::NatspecUser); - contractData["devdoc"] = m_compilerStack.metadata(contractName, DocumentationType::NatspecDev); + contractData["userdoc"] = m_compilerStack.natspec(contractName, DocumentationType::NatspecUser); + contractData["devdoc"] = m_compilerStack.natspec(contractName, DocumentationType::NatspecDev); // EVM Json::Value evmData(Json::objectValue); @@ -403,7 +397,7 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) m_compilerStack.streamAssembly(tmp, contractName, createSourceList(_input), false); evmData["assembly"] = tmp.str(); evmData["legacyAssembly"] = m_compilerStack.streamAssembly(tmp, contractName, createSourceList(_input), true); - evmData["methodIdentifiers"] = methodIdentifiers(m_compilerStack.contractDefinition(contractName)); + evmData["methodIdentifiers"] = m_compilerStack.methodIdentifiers(contractName); evmData["gasEstimates"] = m_compilerStack.gasEstimates(contractName); evmData["bytecode"] = collectEVMObject( diff --git a/libsolidity/interface/Utils.h b/libsolidity/interface/Utils.h deleted file mode 100644 index 0027759c..00000000 --- a/libsolidity/interface/Utils.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * @author Christian <c@ethdev.com> - * @date 2014 - * Solidity Utilities. - */ - -#pragma once - -#include <libdevcore/Assertions.h> -#include <libsolidity/interface/Exceptions.h> - -namespace dev -{ -namespace solidity -{ -struct InternalCompilerError; -struct UnimplementedFeatureError; -} -} - -/// Assertion that throws an InternalCompilerError containing the given description if it is not met. -#define solAssert(CONDITION, DESCRIPTION) \ - assertThrow(CONDITION, ::dev::solidity::InternalCompilerError, DESCRIPTION) - -#define solUnimplementedAssert(CONDITION, DESCRIPTION) \ - assertThrow(CONDITION, ::dev::solidity::UnimplementedFeatureError, DESCRIPTION) - -#define solUnimplemented(DESCRIPTION) \ - solUnimplementedAssert(false, DESCRIPTION) diff --git a/libsolidity/interface/Version.cpp b/libsolidity/interface/Version.cpp index 0d23f9c3..a35bfd29 100644 --- a/libsolidity/interface/Version.cpp +++ b/libsolidity/interface/Version.cpp @@ -24,7 +24,7 @@ #include <string> #include <libdevcore/CommonData.h> #include <libdevcore/Common.h> -#include <libsolidity/interface/Utils.h> +#include <libsolidity/interface/Exceptions.h> #include <solidity/BuildInfo.h> using namespace dev; |