mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	Merge pull request #10445 from ethereum/modifiers
[Sol->Yul] Implement function modifiers.
This commit is contained in:
		
						commit
						c4ade1753e
					
				| @ -34,6 +34,27 @@ Consequently, if the padding space within a struct is used to store data (e.g. i | |||||||
| 
 | 
 | ||||||
| We have the same behavior for implicit delete, for example when array of structs is shortened. | We have the same behavior for implicit delete, for example when array of structs is shortened. | ||||||
| 
 | 
 | ||||||
|  |  * Function modifiers are implemented in a slightly different way regarding function parameters. | ||||||
|  |    This especially has an effect if the placeholder ``_;`` is evaluated multiple times in a modifier. | ||||||
|  |    In the old code generator, each function parameter has a fixed slot on the stack. If the function | ||||||
|  |    is run multiple times because ``_;`` is used multiple times or used in a loop, then a change to the | ||||||
|  |    function parameter's value is visible in the next execution of the function. | ||||||
|  |    The new code generator implements modifiers using actual functions and passes function parameters on. | ||||||
|  |    This means that multiple executions of a function will get the same values for the parameters. | ||||||
|  | 
 | ||||||
|  | :: | ||||||
|  |     // SPDX-License-Identifier: GPL-3.0 | ||||||
|  |     pragma solidity >=0.7.0; | ||||||
|  |     contract C { | ||||||
|  |         function f(uint a) public pure mod() returns (uint r) { | ||||||
|  |             r = a++; | ||||||
|  |         } | ||||||
|  |         modifier mod() { _; _; } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | If you execute ``f(0)`` in the old code generator, it will return ``2``, while | ||||||
|  | it will return ``1`` when using the new code generator. | ||||||
|  | 
 | ||||||
|  * The order of contract initialization has changed in case of inheritance. |  * The order of contract initialization has changed in case of inheritance. | ||||||
| 
 | 
 | ||||||
| The order used to be: | The order used to be: | ||||||
|  | |||||||
| @ -32,10 +32,9 @@ YulArity YulArity::fromType(FunctionType const& _functionType) | |||||||
| 		TupleType(_functionType.returnParameterTypes()).sizeOnStack() | 		TupleType(_functionType.returnParameterTypes()).sizeOnStack() | ||||||
| 	}; | 	}; | ||||||
| } | } | ||||||
|  | 
 | ||||||
| string IRNames::function(FunctionDefinition const& _function) | string IRNames::function(FunctionDefinition const& _function) | ||||||
| { | { | ||||||
| 	// @TODO previously, we had to distinguish creation context and runtime context,
 |  | ||||||
| 	// but since we do not work with jump positions anymore, this should not be a problem, right?
 |  | ||||||
| 	return "fun_" + _function.name() + "_" + to_string(_function.id()); | 	return "fun_" + _function.name() + "_" + to_string(_function.id()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -44,6 +43,21 @@ string IRNames::function(VariableDeclaration const& _varDecl) | |||||||
| 	return "getter_fun_" + _varDecl.name() + "_" + to_string(_varDecl.id()); | 	return "getter_fun_" + _varDecl.name() + "_" + to_string(_varDecl.id()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | string IRNames::modifierInvocation(ModifierInvocation const& _modifierInvocation) | ||||||
|  | { | ||||||
|  | 	// This uses the ID of the modifier invocation because it has to be unique
 | ||||||
|  | 	// for each invocation.
 | ||||||
|  | 	solAssert(!_modifierInvocation.name().path().empty(), ""); | ||||||
|  | 	string const& modifierName = _modifierInvocation.name().path().back(); | ||||||
|  | 	solAssert(!modifierName.empty(), ""); | ||||||
|  | 	return "modifier_" + modifierName + "_" + to_string(_modifierInvocation.id()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | string IRNames::functionWithModifierInner(FunctionDefinition const& _function) | ||||||
|  | { | ||||||
|  | 	return "fun_" + _function.name() + "_" + to_string(_function.id()) + "_inner"; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| string IRNames::creationObject(ContractDefinition const& _contract) | string IRNames::creationObject(ContractDefinition const& _contract) | ||||||
| { | { | ||||||
| 	return _contract.name() + "_" + toString(_contract.id()); | 	return _contract.name() + "_" + toString(_contract.id()); | ||||||
|  | |||||||
| @ -49,6 +49,8 @@ struct IRNames | |||||||
| { | { | ||||||
| 	static std::string function(FunctionDefinition const& _function); | 	static std::string function(FunctionDefinition const& _function); | ||||||
| 	static std::string function(VariableDeclaration const& _varDecl); | 	static std::string function(VariableDeclaration const& _varDecl); | ||||||
|  | 	static std::string modifierInvocation(ModifierInvocation const& _modifierInvocation); | ||||||
|  | 	static std::string functionWithModifierInner(FunctionDefinition const& _function); | ||||||
| 	static std::string creationObject(ContractDefinition const& _contract); | 	static std::string creationObject(ContractDefinition const& _contract); | ||||||
| 	static std::string runtimeObject(ContractDefinition const& _contract); | 	static std::string runtimeObject(ContractDefinition const& _contract); | ||||||
| 	static std::string internalDispatch(YulArity const& _arity); | 	static std::string internalDispatch(YulArity const& _arity); | ||||||
|  | |||||||
| @ -80,6 +80,11 @@ IRVariable const& IRGenerationContext::localVariable(VariableDeclaration const& | |||||||
| 	return m_localVariables.at(&_varDecl); | 	return m_localVariables.at(&_varDecl); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void IRGenerationContext::resetLocalVariables() | ||||||
|  | { | ||||||
|  | 	m_localVariables.clear(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void IRGenerationContext::registerImmutableVariable(VariableDeclaration const& _variable) | void IRGenerationContext::registerImmutableVariable(VariableDeclaration const& _variable) | ||||||
| { | { | ||||||
| 	solAssert(_variable.immutable(), "Attempted to register a non-immutable variable as immutable."); | 	solAssert(_variable.immutable(), "Attempted to register a non-immutable variable as immutable."); | ||||||
|  | |||||||
| @ -97,6 +97,7 @@ public: | |||||||
| 	IRVariable const& addLocalVariable(VariableDeclaration const& _varDecl); | 	IRVariable const& addLocalVariable(VariableDeclaration const& _varDecl); | ||||||
| 	bool isLocalVariable(VariableDeclaration const& _varDecl) const { return m_localVariables.count(&_varDecl); } | 	bool isLocalVariable(VariableDeclaration const& _varDecl) const { return m_localVariables.count(&_varDecl); } | ||||||
| 	IRVariable const& localVariable(VariableDeclaration const& _varDecl); | 	IRVariable const& localVariable(VariableDeclaration const& _varDecl); | ||||||
|  | 	void resetLocalVariables(); | ||||||
| 
 | 
 | ||||||
| 	/// Registers an immutable variable of the contract.
 | 	/// Registers an immutable variable of the contract.
 | ||||||
| 	/// Should only be called at construction time.
 | 	/// Should only be called at construction time.
 | ||||||
|  | |||||||
| @ -249,10 +249,10 @@ string IRGenerator::generateFunction(FunctionDefinition const& _function) | |||||||
| { | { | ||||||
| 	string functionName = IRNames::function(_function); | 	string functionName = IRNames::function(_function); | ||||||
| 	return m_context.functionCollector().createFunction(functionName, [&]() { | 	return m_context.functionCollector().createFunction(functionName, [&]() { | ||||||
| 		solUnimplementedAssert(_function.modifiers().empty(), "Modifiers not implemented yet."); | 		m_context.resetLocalVariables(); | ||||||
| 		Whiskers t(R"( | 		Whiskers t(R"( | ||||||
| 			function <functionName>(<params>)<?+retParams> -> <retParams></+retParams> { | 			function <functionName>(<params>)<?+retParams> -> <retParams></+retParams> { | ||||||
| 				<initReturnVariables> | 				<retInit> | ||||||
| 				<body> | 				<body> | ||||||
| 			} | 			} | ||||||
| 		)"); | 		)"); | ||||||
| @ -268,8 +268,137 @@ string IRGenerator::generateFunction(FunctionDefinition const& _function) | |||||||
| 			retParams += m_context.addLocalVariable(*varDecl).stackSlots(); | 			retParams += m_context.addLocalVariable(*varDecl).stackSlots(); | ||||||
| 			retInit += generateInitialAssignment(*varDecl); | 			retInit += generateInitialAssignment(*varDecl); | ||||||
| 		} | 		} | ||||||
|  | 
 | ||||||
| 		t("retParams", joinHumanReadable(retParams)); | 		t("retParams", joinHumanReadable(retParams)); | ||||||
| 		t("initReturnVariables", retInit); | 		t("retInit", retInit); | ||||||
|  | 
 | ||||||
|  | 		if (_function.modifiers().empty()) | ||||||
|  | 			t("body", generate(_function.body())); | ||||||
|  | 		else | ||||||
|  | 		{ | ||||||
|  | 			for (size_t i = 0; i < _function.modifiers().size(); ++i) | ||||||
|  | 			{ | ||||||
|  | 				ModifierInvocation const& modifier = *_function.modifiers().at(i); | ||||||
|  | 				string next = | ||||||
|  | 					i + 1 < _function.modifiers().size() ? | ||||||
|  | 					IRNames::modifierInvocation(*_function.modifiers().at(i + 1)) : | ||||||
|  | 					IRNames::functionWithModifierInner(_function); | ||||||
|  | 				generateModifier(modifier, _function, next); | ||||||
|  | 			} | ||||||
|  | 			t("body", | ||||||
|  | 				(retParams.empty() ? string{} : joinHumanReadable(retParams) + " := ") + | ||||||
|  | 				IRNames::modifierInvocation(*_function.modifiers().at(0)) + | ||||||
|  | 				"(" + | ||||||
|  | 				joinHumanReadable(retParams + params) + | ||||||
|  | 				")" | ||||||
|  | 			); | ||||||
|  | 			// Now generate the actual inner function.
 | ||||||
|  | 			generateFunctionWithModifierInner(_function); | ||||||
|  | 		} | ||||||
|  | 		return t.render(); | ||||||
|  | 	}); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | string IRGenerator::generateModifier( | ||||||
|  | 	ModifierInvocation const& _modifierInvocation, | ||||||
|  | 	FunctionDefinition const& _function, | ||||||
|  | 	string const& _nextFunction | ||||||
|  | ) | ||||||
|  | { | ||||||
|  | 	string functionName = IRNames::modifierInvocation(_modifierInvocation); | ||||||
|  | 	return m_context.functionCollector().createFunction(functionName, [&]() { | ||||||
|  | 		m_context.resetLocalVariables(); | ||||||
|  | 		Whiskers t(R"( | ||||||
|  | 			function <functionName>(<params>)<?+retParams> -> <retParams></+retParams> { | ||||||
|  | 				<assignRetParams> | ||||||
|  | 				<evalArgs> | ||||||
|  | 				<body> | ||||||
|  | 			} | ||||||
|  | 		)"); | ||||||
|  | 		t("functionName", functionName); | ||||||
|  | 		vector<string> retParamsIn; | ||||||
|  | 		for (auto const& varDecl: _function.returnParameters()) | ||||||
|  | 			retParamsIn += IRVariable(*varDecl).stackSlots(); | ||||||
|  | 		vector<string> params = retParamsIn; | ||||||
|  | 		for (auto const& varDecl: _function.parameters()) | ||||||
|  | 			params += m_context.addLocalVariable(*varDecl).stackSlots(); | ||||||
|  | 		t("params", joinHumanReadable(params)); | ||||||
|  | 		vector<string> retParams; | ||||||
|  | 		string assignRetParams; | ||||||
|  | 		for (size_t i = 0; i < retParamsIn.size(); ++i) | ||||||
|  | 		{ | ||||||
|  | 			retParams.emplace_back(m_context.newYulVariable()); | ||||||
|  | 			assignRetParams += retParams.back() + " := " + retParamsIn[i] + "\n"; | ||||||
|  | 		} | ||||||
|  | 		t("retParams", joinHumanReadable(retParams)); | ||||||
|  | 		t("assignRetParams", assignRetParams); | ||||||
|  | 
 | ||||||
|  | 		solAssert(*_modifierInvocation.name().annotation().requiredLookup == VirtualLookup::Virtual, ""); | ||||||
|  | 
 | ||||||
|  | 		ModifierDefinition const& modifier = dynamic_cast<ModifierDefinition const&>( | ||||||
|  | 			*_modifierInvocation.name().annotation().referencedDeclaration | ||||||
|  | 		).resolveVirtual(m_context.mostDerivedContract()); | ||||||
|  | 
 | ||||||
|  | 		solAssert( | ||||||
|  | 			modifier.parameters().empty() == | ||||||
|  | 			(!_modifierInvocation.arguments() || _modifierInvocation.arguments()->empty()), | ||||||
|  | 			"" | ||||||
|  | 		); | ||||||
|  | 		IRGeneratorForStatements expressionEvaluator(m_context, m_utils); | ||||||
|  | 		if (_modifierInvocation.arguments()) | ||||||
|  | 			for (size_t i = 0; i < _modifierInvocation.arguments()->size(); i++) | ||||||
|  | 			{ | ||||||
|  | 				IRVariable argument = expressionEvaluator.evaluateExpression( | ||||||
|  | 					*_modifierInvocation.arguments()->at(i), | ||||||
|  | 					*modifier.parameters()[i]->annotation().type | ||||||
|  | 				); | ||||||
|  | 				expressionEvaluator.define( | ||||||
|  | 					m_context.addLocalVariable(*modifier.parameters()[i]), | ||||||
|  | 					argument | ||||||
|  | 				); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 		t("evalArgs", expressionEvaluator.code()); | ||||||
|  | 		IRGeneratorForStatements generator(m_context, m_utils, [&]() { | ||||||
|  | 			string ret = joinHumanReadable(retParams); | ||||||
|  | 			return | ||||||
|  | 				(ret.empty() ? "" : ret + " := ") + | ||||||
|  | 				_nextFunction + "(" + joinHumanReadable(params) + ")\n"; | ||||||
|  | 		}); | ||||||
|  | 		generator.generate(modifier.body()); | ||||||
|  | 		t("body", generator.code()); | ||||||
|  | 		return t.render(); | ||||||
|  | 	}); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | string IRGenerator::generateFunctionWithModifierInner(FunctionDefinition const& _function) | ||||||
|  | { | ||||||
|  | 	string functionName = IRNames::functionWithModifierInner(_function); | ||||||
|  | 	return m_context.functionCollector().createFunction(functionName, [&]() { | ||||||
|  | 		m_context.resetLocalVariables(); | ||||||
|  | 		Whiskers t(R"( | ||||||
|  | 			function <functionName>(<params>)<?+retParams> -> <retParams></+retParams> { | ||||||
|  | 				<assignRetParams> | ||||||
|  | 				<body> | ||||||
|  | 			} | ||||||
|  | 		)"); | ||||||
|  | 		t("functionName", functionName); | ||||||
|  | 		vector<string> retParams; | ||||||
|  | 		vector<string> retParamsIn; | ||||||
|  | 		for (auto const& varDecl: _function.returnParameters()) | ||||||
|  | 			retParams += m_context.addLocalVariable(*varDecl).stackSlots(); | ||||||
|  | 		string assignRetParams; | ||||||
|  | 		for (size_t i = 0; i < retParams.size(); ++i) | ||||||
|  | 		{ | ||||||
|  | 			retParamsIn.emplace_back(m_context.newYulVariable()); | ||||||
|  | 			assignRetParams += retParams.back() + " := " + retParamsIn[i] + "\n"; | ||||||
|  | 		} | ||||||
|  | 		vector<string> params = retParamsIn; | ||||||
|  | 		for (auto const& varDecl: _function.parameters()) | ||||||
|  | 			params += m_context.addLocalVariable(*varDecl).stackSlots(); | ||||||
|  | 		t("params", joinHumanReadable(params)); | ||||||
|  | 		t("retParams", joinHumanReadable(retParams)); | ||||||
|  | 		t("assignRetParams", assignRetParams); | ||||||
| 		t("body", generate(_function.body())); | 		t("body", generate(_function.body())); | ||||||
| 		return t.render(); | 		return t.render(); | ||||||
| 	}); | 	}); | ||||||
| @ -526,6 +655,7 @@ void IRGenerator::generateImplicitConstructors(ContractDefinition const& _contra | |||||||
| 		ContractDefinition const* contract = _contract.annotation().linearizedBaseContracts[i]; | 		ContractDefinition const* contract = _contract.annotation().linearizedBaseContracts[i]; | ||||||
| 		baseConstructorParams.erase(contract); | 		baseConstructorParams.erase(contract); | ||||||
| 
 | 
 | ||||||
|  | 		m_context.resetLocalVariables(); | ||||||
| 		m_context.functionCollector().createFunction(IRNames::implicitConstructor(*contract), [&]() { | 		m_context.functionCollector().createFunction(IRNames::implicitConstructor(*contract), [&]() { | ||||||
| 			Whiskers t(R"( | 			Whiskers t(R"( | ||||||
| 				function <functionName>(<params><comma><baseParams>) { | 				function <functionName>(<params><comma><baseParams>) { | ||||||
| @ -537,16 +667,8 @@ void IRGenerator::generateImplicitConstructors(ContractDefinition const& _contra | |||||||
| 			)"); | 			)"); | ||||||
| 			vector<string> params; | 			vector<string> params; | ||||||
| 			if (contract->constructor()) | 			if (contract->constructor()) | ||||||
| 			{ |  | ||||||
| 				for (auto const& modifierInvocation: contract->constructor()->modifiers()) |  | ||||||
| 					// This can be ContractDefinition too for super arguments. That is supported.
 |  | ||||||
| 					solUnimplementedAssert( |  | ||||||
| 						!dynamic_cast<ModifierDefinition const*>(modifierInvocation->name().annotation().referencedDeclaration), |  | ||||||
| 						"Modifiers not implemented yet." |  | ||||||
| 					); |  | ||||||
| 				for (ASTPointer<VariableDeclaration> const& varDecl: contract->constructor()->parameters()) | 				for (ASTPointer<VariableDeclaration> const& varDecl: contract->constructor()->parameters()) | ||||||
| 					params += m_context.addLocalVariable(*varDecl).stackSlots(); | 					params += m_context.addLocalVariable(*varDecl).stackSlots(); | ||||||
| 			} |  | ||||||
| 			t("params", joinHumanReadable(params)); | 			t("params", joinHumanReadable(params)); | ||||||
| 			vector<string> baseParams = listAllParams(baseConstructorParams); | 			vector<string> baseParams = listAllParams(baseConstructorParams); | ||||||
| 			t("baseParams", joinHumanReadable(baseParams)); | 			t("baseParams", joinHumanReadable(baseParams)); | ||||||
| @ -565,7 +687,37 @@ void IRGenerator::generateImplicitConstructors(ContractDefinition const& _contra | |||||||
| 			else | 			else | ||||||
| 				t("hasNextConstructor", false); | 				t("hasNextConstructor", false); | ||||||
| 			t("initStateVariables", initStateVariables(*contract)); | 			t("initStateVariables", initStateVariables(*contract)); | ||||||
| 			t("userDefinedConstructorBody", contract->constructor() ? generate(contract->constructor()->body()) : ""); | 			string body; | ||||||
|  | 			if (FunctionDefinition const* constructor = contract->constructor()) | ||||||
|  | 			{ | ||||||
|  | 				vector<ModifierInvocation*> realModifiers; | ||||||
|  | 				for (auto const& modifierInvocation: constructor->modifiers()) | ||||||
|  | 					// Filter out the base constructor calls
 | ||||||
|  | 					if (dynamic_cast<ModifierDefinition const*>(modifierInvocation->name().annotation().referencedDeclaration)) | ||||||
|  | 						realModifiers.emplace_back(modifierInvocation.get()); | ||||||
|  | 				if (realModifiers.empty()) | ||||||
|  | 					body = generate(constructor->body()); | ||||||
|  | 				else | ||||||
|  | 				{ | ||||||
|  | 					for (size_t i = 0; i < realModifiers.size(); ++i) | ||||||
|  | 					{ | ||||||
|  | 						ModifierInvocation const& modifier = *realModifiers.at(i); | ||||||
|  | 						string next = | ||||||
|  | 							i + 1 < realModifiers.size() ? | ||||||
|  | 							IRNames::modifierInvocation(*realModifiers.at(i + 1)) : | ||||||
|  | 							IRNames::functionWithModifierInner(*constructor); | ||||||
|  | 						generateModifier(modifier, *constructor, next); | ||||||
|  | 					} | ||||||
|  | 					body = | ||||||
|  | 						IRNames::modifierInvocation(*constructor->modifiers().at(0)) + | ||||||
|  | 						"(" + | ||||||
|  | 						joinHumanReadable(params) + | ||||||
|  | 						")"; | ||||||
|  | 					// Now generate the actual inner function.
 | ||||||
|  | 					generateFunctionWithModifierInner(*constructor); | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			t("userDefinedConstructorBody", move(body)); | ||||||
| 
 | 
 | ||||||
| 			return t.render(); | 			return t.render(); | ||||||
| 		}); | 		}); | ||||||
|  | |||||||
| @ -73,6 +73,12 @@ private: | |||||||
| 	InternalDispatchMap generateInternalDispatchFunctions(); | 	InternalDispatchMap generateInternalDispatchFunctions(); | ||||||
| 	/// Generates code for and returns the name of the function.
 | 	/// Generates code for and returns the name of the function.
 | ||||||
| 	std::string generateFunction(FunctionDefinition const& _function); | 	std::string generateFunction(FunctionDefinition const& _function); | ||||||
|  | 	std::string generateModifier( | ||||||
|  | 		ModifierInvocation const& _modifierInvocation, | ||||||
|  | 		FunctionDefinition const& _function, | ||||||
|  | 		std::string const& _nextFunction | ||||||
|  | 	); | ||||||
|  | 	std::string generateFunctionWithModifierInner(FunctionDefinition const& _function); | ||||||
| 	/// Generates a getter for the given declaration and returns its name
 | 	/// Generates a getter for the given declaration and returns its name
 | ||||||
| 	std::string generateGetter(VariableDeclaration const& _varDecl); | 	std::string generateGetter(VariableDeclaration const& _varDecl); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -581,6 +581,12 @@ bool IRGeneratorForStatements::visit(IfStatement const& _ifStatement) | |||||||
| 	return false; | 	return false; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void IRGeneratorForStatements::endVisit(PlaceholderStatement const&) | ||||||
|  | { | ||||||
|  | 	solAssert(m_placeholderCallback, ""); | ||||||
|  | 	m_code << m_placeholderCallback(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| bool IRGeneratorForStatements::visit(ForStatement const& _forStatement) | bool IRGeneratorForStatements::visit(ForStatement const& _forStatement) | ||||||
| { | { | ||||||
| 	setLocation(_forStatement); | 	setLocation(_forStatement); | ||||||
|  | |||||||
| @ -40,8 +40,13 @@ class YulUtilFunctions; | |||||||
| class IRGeneratorForStatements: public ASTConstVisitor | class IRGeneratorForStatements: public ASTConstVisitor | ||||||
| { | { | ||||||
| public: | public: | ||||||
| 	IRGeneratorForStatements(IRGenerationContext& _context, YulUtilFunctions& _utils): | 	IRGeneratorForStatements( | ||||||
|  | 		IRGenerationContext& _context, | ||||||
|  | 		YulUtilFunctions& _utils, | ||||||
|  | 		std::function<std::string()> _placeholderCallback = {} | ||||||
|  | 	): | ||||||
| 		m_context(_context), | 		m_context(_context), | ||||||
|  | 		m_placeholderCallback(std::move(_placeholderCallback)), | ||||||
| 		m_utils(_utils) | 		m_utils(_utils) | ||||||
| 	{} | 	{} | ||||||
| 
 | 
 | ||||||
| @ -58,6 +63,9 @@ public: | |||||||
| 	/// Calculates expression's value and returns variable where it was stored
 | 	/// Calculates expression's value and returns variable where it was stored
 | ||||||
| 	IRVariable evaluateExpression(Expression const& _expression, Type const& _to); | 	IRVariable evaluateExpression(Expression const& _expression, Type const& _to); | ||||||
| 
 | 
 | ||||||
|  | 	/// Defines @a _var using the value of @a _value while performing type conversions, if required.
 | ||||||
|  | 	void define(IRVariable const& _var, IRVariable const& _value) { declareAssign(_var, _value, true); } | ||||||
|  | 
 | ||||||
| 	/// @returns the name of a function that computes the value of the given constant
 | 	/// @returns the name of a function that computes the value of the given constant
 | ||||||
| 	/// and also generates the function.
 | 	/// and also generates the function.
 | ||||||
| 	std::string constantValueFunction(VariableDeclaration const& _constant); | 	std::string constantValueFunction(VariableDeclaration const& _constant); | ||||||
| @ -66,6 +74,7 @@ public: | |||||||
| 	bool visit(Conditional const& _conditional) override; | 	bool visit(Conditional const& _conditional) override; | ||||||
| 	bool visit(Assignment const& _assignment) override; | 	bool visit(Assignment const& _assignment) override; | ||||||
| 	bool visit(TupleExpression const& _tuple) override; | 	bool visit(TupleExpression const& _tuple) override; | ||||||
|  | 	void endVisit(PlaceholderStatement const& _placeholder) override; | ||||||
| 	bool visit(Block const& _block) override; | 	bool visit(Block const& _block) override; | ||||||
| 	void endVisit(Block const& _block) override; | 	void endVisit(Block const& _block) override; | ||||||
| 	bool visit(IfStatement const& _ifStatement) override; | 	bool visit(IfStatement const& _ifStatement) override; | ||||||
| @ -135,8 +144,7 @@ private: | |||||||
| 	/// @returns an output stream that can be used to define @a _var using a function call or
 | 	/// @returns an output stream that can be used to define @a _var using a function call or
 | ||||||
| 	/// single stack slot expression.
 | 	/// single stack slot expression.
 | ||||||
| 	std::ostream& define(IRVariable const& _var); | 	std::ostream& define(IRVariable const& _var); | ||||||
| 	/// Defines @a _var using the value of @a _value while performing type conversions, if required.
 | 
 | ||||||
| 	void define(IRVariable const& _var, IRVariable const& _value) { declareAssign(_var, _value, true); } |  | ||||||
| 	/// Assigns @a _var to the value of @a _value while performing type conversions, if required.
 | 	/// Assigns @a _var to the value of @a _value while performing type conversions, if required.
 | ||||||
| 	void assign(IRVariable const& _var, IRVariable const& _value) { declareAssign(_var, _value, false); } | 	void assign(IRVariable const& _var, IRVariable const& _value) { declareAssign(_var, _value, false); } | ||||||
| 	/// Declares variable @a _var.
 | 	/// Declares variable @a _var.
 | ||||||
| @ -189,6 +197,7 @@ private: | |||||||
| 
 | 
 | ||||||
| 	std::ostringstream m_code; | 	std::ostringstream m_code; | ||||||
| 	IRGenerationContext& m_context; | 	IRGenerationContext& m_context; | ||||||
|  | 	std::function<std::string()> m_placeholderCallback; | ||||||
| 	YulUtilFunctions& m_utils; | 	YulUtilFunctions& m_utils; | ||||||
| 	std::optional<IRLValue> m_currentLValue; | 	std::optional<IRLValue> m_currentLValue; | ||||||
| 	langutil::SourceLocation m_currentLocation; | 	langutil::SourceLocation m_currentLocation; | ||||||
|  | |||||||
| @ -34,5 +34,7 @@ contract C is B { | |||||||
|         return (x, y); |         return (x, y); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | // ==== | ||||||
|  | // compileViaYul: also | ||||||
| // ---- | // ---- | ||||||
| // test() -> 5, 10 | // test() -> 5, 10 | ||||||
|  | |||||||
| @ -8,6 +8,8 @@ contract C { | |||||||
|         return b + c; |         return b + c; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | // ==== | ||||||
|  | // compileViaYul: also | ||||||
| // ---- | // ---- | ||||||
| // f(uint16,uint16,uint16): 0xe000, 0xe500, 2 -> 58626 | // f(uint16,uint16,uint16): 0xe000, 0xe500, 2 -> 58626 | ||||||
| // f(uint16,uint16,uint16): 0x1000, 0xe500, 0xe000 -> FAILURE, hex"4e487b71", 0x11 | // f(uint16,uint16,uint16): 0x1000, 0xe500, 0xe000 -> FAILURE, hex"4e487b71", 0x11 | ||||||
|  | |||||||
| @ -28,6 +28,8 @@ contract C { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // ==== | ||||||
|  | // compileViaYul: also | ||||||
| // ---- | // ---- | ||||||
| // f() -> true | // f() -> true | ||||||
| // g() -> FAILURE | // g() -> FAILURE | ||||||
|  | |||||||
| @ -14,6 +14,8 @@ contract C { | |||||||
|         x = t; |         x = t; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | // ==== | ||||||
|  | // compileViaYul: also | ||||||
| // ---- | // ---- | ||||||
| // x() -> 0 | // x() -> 0 | ||||||
| // f() -> | // f() -> | ||||||
|  | |||||||
| @ -14,6 +14,8 @@ contract C { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // ==== | ||||||
|  | // compileViaYul: also | ||||||
| // ---- | // ---- | ||||||
| // x() -> 0 | // x() -> 0 | ||||||
| // f() -> | // f() -> | ||||||
|  | |||||||
| @ -0,0 +1,22 @@ | |||||||
|  | contract A { constructor(uint) {} } | ||||||
|  | contract B { constructor(uint) {} } | ||||||
|  | contract C { constructor(uint) {} } | ||||||
|  | 
 | ||||||
|  | contract D is A, B, C { | ||||||
|  |     uint[] x; | ||||||
|  |     constructor() m2(f(1)) B(f(2)) m1(f(3)) C(f(4)) m3(f(5)) A(f(6)) { | ||||||
|  |         f(7); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     function query() public view returns (uint[] memory) { return x; } | ||||||
|  | 
 | ||||||
|  |     modifier m1(uint) { _; } | ||||||
|  |     modifier m2(uint) { _; } | ||||||
|  |     modifier m3(uint) { _; } | ||||||
|  | 
 | ||||||
|  |     function f(uint y) internal returns (uint) { x.push(y); return 0; } | ||||||
|  | } | ||||||
|  | // ==== | ||||||
|  | // compileViaYul: also | ||||||
|  | // ---- | ||||||
|  | // query() -> 0x20, 7, 4, 2, 6, 1, 3, 5, 7 | ||||||
| @ -8,6 +8,8 @@ contract C { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // ==== | ||||||
|  | // compileViaYul: also | ||||||
| // ---- | // ---- | ||||||
| // getOne() -> 0 | // getOne() -> 0 | ||||||
| // getOne(), 1 wei -> 1 | // getOne(), 1 wei -> 1 | ||||||
|  | |||||||
| @ -45,5 +45,7 @@ contract C is A { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // ==== | ||||||
|  | // compileViaYul: also | ||||||
| // ---- | // ---- | ||||||
| // getData() -> 0x4300 | // getData() -> 0x4300 | ||||||
|  | |||||||
| @ -13,5 +13,7 @@ contract C is A { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // ==== | ||||||
|  | // compileViaYul: also | ||||||
| // ---- | // ---- | ||||||
| // f() -> false | // f() -> false | ||||||
|  | |||||||
| @ -22,6 +22,7 @@ contract C is A { | |||||||
|         _; |         _; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | // ==== | ||||||
|  | // compileViaYul: also | ||||||
| // ---- | // ---- | ||||||
| // getData() -> 6 | // getData() -> 6 | ||||||
|  | |||||||
| @ -24,5 +24,7 @@ contract Test { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // ==== | ||||||
|  | // compileViaYul: also | ||||||
| // ---- | // ---- | ||||||
| // f() -> 0x202 | // f() -> 0x202 | ||||||
|  | |||||||
| @ -30,5 +30,7 @@ contract Test { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // ==== | ||||||
|  | // compileViaYul: also | ||||||
| // ---- | // ---- | ||||||
| // f() -> 0x202 | // f() -> 0x202 | ||||||
|  | |||||||
| @ -14,6 +14,8 @@ contract C { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // ==== | ||||||
|  | // compileViaYul: also | ||||||
| // ---- | // ---- | ||||||
| // f(bool): true -> 0 | // f(bool): true -> 0 | ||||||
| // f(bool): false -> 3 | // f(bool): false -> 3 | ||||||
|  | |||||||
| @ -8,6 +8,7 @@ contract C { | |||||||
|         r += 1; |         r += 1; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | // ==== | ||||||
|  | // compileViaYul: false | ||||||
| // ---- | // ---- | ||||||
| // f() -> 10 | // f() -> 10 | ||||||
|  | |||||||
| @ -0,0 +1,14 @@ | |||||||
|  | contract C { | ||||||
|  |     modifier repeat(uint256 count) { | ||||||
|  |         uint256 i; | ||||||
|  |         for (i = 0; i < count; ++i) _; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     function f() public repeat(10) returns (uint256 r) { | ||||||
|  |         r += 1; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | // ==== | ||||||
|  | // compileViaYul: true | ||||||
|  | // ---- | ||||||
|  | // f() -> 1 | ||||||
| @ -8,7 +8,8 @@ contract C { | |||||||
|         r += 1; |         r += 1; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | // ==== | ||||||
|  | // compileViaYul: false | ||||||
| // ---- | // ---- | ||||||
| // f(bool): false -> 1 | // f(bool): false -> 1 | ||||||
| // f(bool): true -> 2 | // f(bool): true -> 2 | ||||||
|  | |||||||
| @ -0,0 +1,15 @@ | |||||||
|  | contract C { | ||||||
|  |     modifier repeat(bool twice) { | ||||||
|  |         if (twice) _; | ||||||
|  |         _; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     function f(bool twice) public repeat(twice) returns (uint256 r) { | ||||||
|  |         r += 1; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | // ==== | ||||||
|  | // compileViaYul: true | ||||||
|  | // ---- | ||||||
|  | // f(bool): false -> 1 | ||||||
|  | // f(bool): true -> 1 | ||||||
| @ -11,7 +11,8 @@ contract C { | |||||||
|         return r; |         return r; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | // ==== | ||||||
|  | // compileViaYul: false | ||||||
| // ---- | // ---- | ||||||
| // f(bool): false -> 1 | // f(bool): false -> 1 | ||||||
| // f(bool): true -> 2 | // f(bool): true -> 2 | ||||||
|  | |||||||
| @ -10,6 +10,8 @@ contract C { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // ==== | ||||||
|  | // compileViaYul: also | ||||||
| // ---- | // ---- | ||||||
| // f(uint256): 3 -> 10 | // f(uint256): 3 -> 10 | ||||||
| // a() -> 10 | // a() -> 10 | ||||||
|  | |||||||
| @ -13,6 +13,8 @@ contract C { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // ==== | ||||||
|  | // compileViaYul: also | ||||||
| // ---- | // ---- | ||||||
| // f(uint256): 3 -> 10 | // f(uint256): 3 -> 10 | ||||||
| // a() -> 0 | // a() -> 0 | ||||||
|  | |||||||
| @ -15,5 +15,7 @@ contract C is A { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // ==== | ||||||
|  | // compileViaYul: also | ||||||
| // ---- | // ---- | ||||||
| // f() -> false | // f() -> false | ||||||
|  | |||||||
| @ -0,0 +1,15 @@ | |||||||
|  | contract C { | ||||||
|  |     modifier m(bool condition) { | ||||||
|  |         if (condition) _; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     function f(uint x) public m(x >= 10) returns (uint[5] memory r) { | ||||||
|  |         r[2] = 3; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // ==== | ||||||
|  | // compileViaYul: also | ||||||
|  | // ---- | ||||||
|  | // f(uint256): 9 -> 0x00, 0x00, 0x00, 0x00, 0x00 | ||||||
|  | // f(uint256): 10 -> 0x00, 0x00, 3, 0x00, 0x00 | ||||||
| @ -10,6 +10,8 @@ contract C { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // ==== | ||||||
|  | // compileViaYul: also | ||||||
| // ---- | // ---- | ||||||
| // x() -> 0 | // x() -> 0 | ||||||
| // f() -> 2 | // f() -> 2 | ||||||
|  | |||||||
| @ -14,6 +14,8 @@ contract C { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // ==== | ||||||
|  | // compileViaYul: also | ||||||
| // ---- | // ---- | ||||||
| // x() -> 0 | // x() -> 0 | ||||||
| // f() -> | // f() -> | ||||||
|  | |||||||
| @ -15,6 +15,8 @@ contract C { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | // ==== | ||||||
|  | // compileViaYul: also | ||||||
| // ---- | // ---- | ||||||
| // x() -> 0 | // x() -> 0 | ||||||
| // f() -> 42 | // f() -> 42 | ||||||
|  | |||||||
| @ -13,6 +13,8 @@ contract C { | |||||||
|         return address(this).balance; |         return address(this).balance; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | // ==== | ||||||
|  | // compileViaYul: also | ||||||
| // ---- | // ---- | ||||||
| // f(), 27 wei -> FAILURE | // f(), 27 wei -> FAILURE | ||||||
| // balance() -> 0 | // balance() -> 0 | ||||||
|  | |||||||
| @ -16,6 +16,8 @@ contract C { | |||||||
|         x += 3; |         x += 3; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | // ==== | ||||||
|  | // compileViaYul: also | ||||||
| // ---- | // ---- | ||||||
| // f1() -> | // f1() -> | ||||||
| // x() -> 0x08 | // x() -> 0x08 | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user