Browse Source

Packing and unpacking of constructor arguments.

cl-refactor
Christian 10 years ago
parent
commit
7801b87ddb
  1. 21
      libevmcore/Assembly.cpp
  2. 5
      libevmcore/Assembly.h
  3. 16
      libsolidity/Compiler.cpp
  4. 2
      libsolidity/CompilerContext.h
  5. 26
      test/solidityEndToEndTest.cpp

21
libevmcore/Assembly.cpp

@ -39,6 +39,7 @@ unsigned AssemblyItem::bytesRequired(unsigned _addressLength) const
case Push:
return 1 + max<unsigned>(1, dev::bytesRequired(m_data));
case PushSubSize:
case PushProgramSize:
return 4; // worst case: a 16MB program
case PushTag:
case PushData:
@ -59,7 +60,7 @@ int AssemblyItem::deposit() const
{
case Operation:
return instructionInfo((Instruction)(byte)m_data).ret - instructionInfo((Instruction)(byte)m_data).args;
case Push: case PushString: case PushTag: case PushData: case PushSub: case PushSubSize:
case Push: case PushString: case PushTag: case PushData: case PushSub: case PushSubSize: case PushProgramSize:
return 1;
case Tag:
return 0;
@ -146,6 +147,9 @@ ostream& dev::eth::operator<<(ostream& _out, AssemblyItemsConstRef _i)
case PushSubSize:
_out << " PUSHss[" << hex << h256(i.data()).abridged() << "]";
break;
case PushProgramSize:
_out << " PUSHSIZE";
break;
case NoOptimizeBegin:
_out << " DoNotOptimze{{";
break;
@ -185,6 +189,9 @@ ostream& Assembly::streamRLP(ostream& _out, string const& _prefix) const
case PushSubSize:
_out << _prefix << " PUSH #[$" << h256(i.m_data).abridged() << "]" << endl;
break;
case PushProgramSize:
_out << _prefix << " PUSHSIZE" << endl;
break;
case Tag:
_out << _prefix << "tag" << i.m_data << ": " << endl << _prefix << " JUMPDEST" << endl;
break;
@ -303,6 +310,7 @@ Assembly& Assembly::optimise(bool _enable)
{ { PushString, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } },
{ { PushSub, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } },
{ { PushSubSize, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } },
{ { PushProgramSize, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } },
{ { Push, PushTag, Instruction::JUMPI }, [](AssemblyItemsConstRef m) -> AssemblyItems { if (m[0].data()) return { m[1], Instruction::JUMP }; else return {}; } },
{ { Instruction::ISZERO, Instruction::ISZERO }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } },
};
@ -468,6 +476,7 @@ bytes Assembly::assemble() const
vector<unsigned> tagPos(m_usedTags);
map<unsigned, unsigned> tagRef;
multimap<h256, unsigned> dataRef;
vector<unsigned> sizeRef; ///< Pointers to code locations where the size of the program is inserted
unsigned bytesPerTag = dev::bytesRequired(totalBytes);
byte tagPush = (byte)Instruction::PUSH1 - 1 + bytesPerTag;
@ -526,6 +535,11 @@ bytes Assembly::assemble() const
toBigEndian(s, byr);
break;
}
case PushProgramSize:
ret.push_back(tagPush);
sizeRef.push_back(ret.size());
ret.resize(ret.size() + bytesPerTag);
break;
case Tag:
tagPos[(unsigned)i.m_data] = ret.size();
ret.push_back((byte)Instruction::JUMPDEST);
@ -561,5 +575,10 @@ bytes Assembly::assemble() const
}
}
}
for (unsigned pos: sizeRef)
{
bytesRef r(ret.data() + pos, bytesPerTag);
toBigEndian(ret.size(), r);
}
return ret;
}

5
libevmcore/Assembly.h

@ -32,7 +32,7 @@ namespace dev
namespace eth
{
enum AssemblyItemType { UndefinedItem, Operation, Push, PushString, PushTag, PushSub, PushSubSize, Tag, PushData, NoOptimizeBegin, NoOptimizeEnd };
enum AssemblyItemType { UndefinedItem, Operation, Push, PushString, PushTag, PushSub, PushSubSize, PushProgramSize, Tag, PushData, NoOptimizeBegin, NoOptimizeEnd };
class Assembly;
@ -86,6 +86,9 @@ public:
AssemblyItem const& append(std::string const& _data) { return append(newPushString(_data)); }
AssemblyItem const& append(bytes const& _data) { return append(newData(_data)); }
AssemblyItem appendSubSize(Assembly const& _a) { auto ret = newSub(_a); append(newPushSubSize(ret.data())); return ret; }
/// Pushes the final size of the current assembly itself. Use this when the code is modified
/// after compilation and CODESIZE is not an option.
void appendProgramSize() { append(AssemblyItem(PushProgramSize)); }
AssemblyItem appendJump() { auto ret = append(newPushTag()); append(Instruction::JUMP); return ret; }
AssemblyItem appendJumpI() { auto ret = append(newPushTag()); append(Instruction::JUMPI); return ret; }

16
libsolidity/Compiler.cpp

@ -77,12 +77,21 @@ void Compiler::packIntoContractCreator(ContractDefinition const& _contract,
{
eth::AssemblyItem returnTag = m_context.pushNewTag();
m_context.addFunction(*constructor); // note that it cannot be called due to syntactic reasons
// copy constructor arguments
//@todo ask assembly for the size of the current program
// copy constructor arguments from code to memory and then to stack, they are supplied after the actual program
unsigned argumentSize = 0;
for (ASTPointer<VariableDeclaration> const& var: constructor->getParameters())
argumentSize += var->getType()->getCalldataEncodedSize();
if (argumentSize > 0)
{
m_context << u256(argumentSize);
m_context.appendProgramSize();
m_context << u256(1); // copy it to byte one as expected for ABI calls
m_context << eth::Instruction::CODECOPY;
appendCalldataUnpacker(*constructor, true);
}
//@todo calling other functions inside the constructor should either trigger a parse error
//or we should copy them here (register them above and call "accept") - detecting which
// functions are referenced / called needs to be done in a recursive way.
appendCalldataUnpacker(*constructor, true);
m_context.appendJumpTo(m_context.getFunctionEntryLabel(*constructor));
constructor->accept(*this);
m_context << returnTag;
@ -135,7 +144,6 @@ unsigned Compiler::appendCalldataUnpacker(FunctionDefinition const& _function, b
{
// We do not check the calldata size, everything is zero-padded.
unsigned dataOffset = 1;
//@todo this can be done more efficiently, saving some CALLDATALOAD calls
for (ASTPointer<VariableDeclaration> const& var: _function.getParameters())
{

2
libsolidity/CompilerContext.h

@ -81,6 +81,8 @@ public:
/// Adds a subroutine to the code (in the data section) and pushes its size (via a tag)
/// on the stack. @returns the assembly item corresponding to the pushed subroutine, i.e. its offset.
eth::AssemblyItem addSubroutine(eth::Assembly const& _assembly) { return m_asm.appendSubSize(_assembly); }
/// Pushes the size of the final program
void appendProgramSize() { return m_asm.appendProgramSize(); }
/// Adds data to the data section, pushes a reference to the stack
eth::AssemblyItem appendData(bytes const& _data) { return m_asm.append(_data); }

26
test/solidityEndToEndTest.cpp

@ -1012,6 +1012,32 @@ BOOST_AUTO_TEST_CASE(strings_in_calls)
BOOST_CHECK(callContractFunction(0, bytes({0, 'a', 1})) == bytes({0, 'a', 0, 0, 0}));
}
BOOST_AUTO_TEST_CASE(constructor_arguments)
{
char const* sourceCode = R"(
contract Helper {
string3 name;
bool flag;
function Helper(string3 x, bool f) {
name = x;
flag = f;
}
function getName() returns (string3 ret) { return name; }
function getFlag() returns (bool ret) { return flag; }
}
contract Main {
Helper h;
function Main() {
h = new Helper("abc", true);
}
function getFlag() returns (bool ret) { return h.getFlag(); }
function getName() returns (string3 ret) { return h.getName(); }
})";
compileAndRun(sourceCode, 0, "Main");
BOOST_REQUIRE(callContractFunction(0) == bytes({0x01}));
BOOST_REQUIRE(callContractFunction(1) == bytes({'a', 'b', 'c'}));
}
BOOST_AUTO_TEST_SUITE_END()
}

Loading…
Cancel
Save