diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index c034024b4..1c02b281c 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -410,6 +410,38 @@ bool ExpressionCompiler::visit(TupleExpression const& _tuple) bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation) { CompilerContext::LocationSetter locationSetter(m_context, _unaryOperation); + + FunctionDefinition const* function = *_unaryOperation.annotation().userDefinedFunction; + if (function) + { + solAssert(function->isFree()); + + FunctionType const* functionType = _unaryOperation.userDefinedFunctionType(); + solAssert(functionType); + solAssert(functionType->parameterTypes().size() == 1); + solAssert(functionType->returnParameterTypes().size() == 1); + solAssert(functionType->kind() == FunctionType::Kind::Internal); + + evmasm::AssemblyItem returnLabel = m_context.pushNewTag(); + acceptAndConvert( + _unaryOperation.subExpression(), + *functionType->parameterTypes()[0], + false // _cleanupNeeded + ); + + m_context << m_context.functionEntryLabel(*function).pushTag(); + m_context.appendJump(evmasm::AssemblyItem::JumpType::IntoFunction); + m_context << returnLabel; + + unsigned parameterSize = CompilerUtils::sizeOnStack(functionType->parameterTypes()); + unsigned returnParametersSize = CompilerUtils::sizeOnStack(functionType->returnParameterTypes()); + + // callee adds return parameters, but removes arguments and return label + m_context.adjustStackOffset(static_cast(returnParametersSize - parameterSize) - 1); + + return false; + } + Type const& type = *_unaryOperation.annotation().type; if (type.category() == Type::Category::RationalNumber) { @@ -502,7 +534,42 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation) CompilerContext::LocationSetter locationSetter(m_context, _binaryOperation); Expression const& leftExpression = _binaryOperation.leftExpression(); Expression const& rightExpression = _binaryOperation.rightExpression(); - solAssert(!!_binaryOperation.annotation().commonType, ""); + FunctionDefinition const* function = *_binaryOperation.annotation().userDefinedFunction; + if (function) + { + solAssert(function->isFree()); + + FunctionType const* functionType = _binaryOperation.userDefinedFunctionType(); + solAssert(functionType); + solAssert(functionType->parameterTypes().size() == 2); + solAssert(functionType->returnParameterTypes().size() == 1); + solAssert(functionType->kind() == FunctionType::Kind::Internal); + + evmasm::AssemblyItem returnLabel = m_context.pushNewTag(); + acceptAndConvert( + leftExpression, + *functionType->parameterTypes()[0], + false // _cleanupNeeded + ); + acceptAndConvert( + rightExpression, + *functionType->parameterTypes()[1], + false // _cleanupNeeded + ); + + m_context << m_context.functionEntryLabel(*function).pushTag(); + m_context.appendJump(evmasm::AssemblyItem::JumpType::IntoFunction); + m_context << returnLabel; + + unsigned parameterSize = CompilerUtils::sizeOnStack(functionType->parameterTypes()); + unsigned returnParametersSize = CompilerUtils::sizeOnStack(functionType->returnParameterTypes()); + + // callee adds return parameters, but removes arguments and return label + m_context.adjustStackOffset(static_cast(returnParametersSize - parameterSize) - 1); + return false; + } + + solAssert(!!_binaryOperation.annotation().commonType); Type const* commonType = _binaryOperation.annotation().commonType; Token const c_op = _binaryOperation.getOperator(); diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index fd0199204..a9eb0bf15 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -672,6 +672,30 @@ void IRGeneratorForStatements::endVisit(Return const& _return) bool IRGeneratorForStatements::visit(UnaryOperation const& _unaryOperation) { setLocation(_unaryOperation); + + FunctionDefinition const* function = *_unaryOperation.annotation().userDefinedFunction; + if (function) + { + _unaryOperation.subExpression().accept(*this); + setLocation(_unaryOperation); + + solAssert(function->isImplemented()); + solAssert(function->isFree()); + solAssert(function->parameters().size() == 1); + solAssert(function->returnParameters().size() == 1); + solAssert(*function->returnParameters()[0]->type() == *_unaryOperation.annotation().type); + + string argument = expressionAsType(_unaryOperation.subExpression(), *function->parameters()[0]->type()); + solAssert(!argument.empty()); + + solAssert(_unaryOperation.userDefinedFunctionType()->kind() == FunctionType::Kind::Internal); + define(_unaryOperation) << + m_context.enqueueFunctionForCodeGeneration(*function) << + ("(" + argument + ")\n"); + + return false; + } + Type const& resultType = type(_unaryOperation); Token const op = _unaryOperation.getOperator(); @@ -775,6 +799,31 @@ bool IRGeneratorForStatements::visit(BinaryOperation const& _binOp) { setLocation(_binOp); + FunctionDefinition const* function = *_binOp.annotation().userDefinedFunction; + if (function) + { + _binOp.leftExpression().accept(*this); + _binOp.rightExpression().accept(*this); + setLocation(_binOp); + + solAssert(function->isImplemented()); + solAssert(function->isFree()); + solAssert(function->parameters().size() == 2); + solAssert(function->returnParameters().size() == 1); + solAssert(*function->returnParameters()[0]->type() == *_binOp.annotation().type); + + string left = expressionAsType(_binOp.leftExpression(), *function->parameters()[0]->type()); + string right = expressionAsType(_binOp.rightExpression(), *function->parameters()[1]->type()); + solAssert(!left.empty() && !right.empty()); + + solAssert(_binOp.userDefinedFunctionType()->kind() == FunctionType::Kind::Internal); + define(_binOp) << + m_context.enqueueFunctionForCodeGeneration(*function) << + ("(" + left + ", " + right + ")\n"); + + return false; + } + solAssert(!!_binOp.annotation().commonType); Type const* commonType = _binOp.annotation().commonType; langutil::Token op = _binOp.getOperator();