void Compiler::appendConstructor(FunctionDefinition const& _constructor) { CompilerContext::LocationSetter locationSetter(m_context, _constructor); // copy constructor arguments from code to memory and then to stack, they are supplied after the actual program if (!_constructor.getParameters().empty()) { unsigned argumentSize = 0; for (ASTPointer<VariableDeclaration> const& var: _constructor.getParameters()) if (var->getType()->isDynamicallySized()) { argumentSize = 0; break; } else argumentSize += var->getType()->getCalldataEncodedSize(); CompilerUtils(m_context).fetchFreeMemoryPointer(); if (argumentSize == 0) { // argument size is dynamic, use CODESIZE to determine it m_context.appendProgramSize(); // program itself // CODESIZE is program plus manually added arguments m_context << eth::Instruction::CODESIZE << eth::Instruction::SUB; } else m_context << u256(argumentSize); // stack: <memptr> <argument size> m_context << eth::Instruction::DUP1; m_context.appendProgramSize(); m_context << eth::Instruction::DUP4 << eth::Instruction::CODECOPY; m_context << eth::Instruction::ADD; CompilerUtils(m_context).storeFreeMemoryPointer(); appendCalldataUnpacker( FunctionType(_constructor).getParameterTypes(), true, CompilerUtils::freeMemoryPointer + 0x20 ); } _constructor.accept(*this); }
bool Compiler::visit(FunctionDefinition const& _function) { CompilerContext::LocationSetter locationSetter(m_context, _function); m_context.startFunction(_function); // stack upon entry: [return address] [arg0] [arg1] ... [argn] // reserve additional slots: [retarg0] ... [retargm] [localvar0] ... [localvarp] unsigned parametersSize = CompilerUtils::getSizeOnStack(_function.getParameters()); if (!_function.isConstructor()) // adding 1 for return address. m_context.adjustStackOffset(parametersSize + 1); for (ASTPointer<VariableDeclaration const> const& variable: _function.getParameters()) { m_context.addVariable(*variable, parametersSize); parametersSize -= variable->getType()->getSizeOnStack(); } for (ASTPointer<VariableDeclaration const> const& variable: _function.getReturnParameters()) appendStackVariableInitialisation(*variable); for (VariableDeclaration const* localVariable: _function.getLocalVariables()) appendStackVariableInitialisation(*localVariable); if (_function.isConstructor()) if (auto c = m_context.getNextConstructor(dynamic_cast<ContractDefinition const&>(*_function.getScope()))) appendBaseConstructor(*c); m_returnTag = m_context.newTag(); m_breakTags.clear(); m_continueTags.clear(); m_stackCleanupForReturn = 0; m_currentFunction = &_function; m_modifierDepth = 0; appendModifierOrFunctionCode(); m_context << m_returnTag; // Now we need to re-shuffle the stack. For this we keep a record of the stack layout // that shows the target positions of the elements, where "-1" denotes that this element needs // to be removed from the stack. // Note that the fact that the return arguments are of increasing index is vital for this // algorithm to work. unsigned const c_argumentsSize = CompilerUtils::getSizeOnStack(_function.getParameters()); unsigned const c_returnValuesSize = CompilerUtils::getSizeOnStack(_function.getReturnParameters()); unsigned const c_localVariablesSize = CompilerUtils::getSizeOnStack(_function.getLocalVariables()); vector<int> stackLayout; stackLayout.push_back(c_returnValuesSize); // target of return address stackLayout += vector<int>(c_argumentsSize, -1); // discard all arguments for (unsigned i = 0; i < c_returnValuesSize; ++i) stackLayout.push_back(i); stackLayout += vector<int>(c_localVariablesSize, -1); solAssert(stackLayout.size() <= 17, "Stack too deep, try removing local variables."); while (stackLayout.back() != int(stackLayout.size() - 1)) if (stackLayout.back() < 0) { m_context << eth::Instruction::POP; stackLayout.pop_back(); } else { m_context << eth::swapInstruction(stackLayout.size() - stackLayout.back() - 1); swap(stackLayout[stackLayout.back()], stackLayout.back()); } //@todo assert that everything is in place now for (ASTPointer<VariableDeclaration const> const& variable: _function.getParameters() + _function.getReturnParameters()) m_context.removeVariable(*variable); for (VariableDeclaration const* localVariable: _function.getLocalVariables()) m_context.removeVariable(*localVariable); m_context.adjustStackOffset(-(int)c_returnValuesSize); if (!_function.isConstructor()) m_context.appendJump(eth::AssemblyItem::JumpType::OutOfFunction); return false; }