Browse Source

Replace function selector jump table by more resilient linear time check.

cl-refactor
Christian 10 years ago
parent
commit
f5730edddb
  1. 39
      libsolidity/Compiler.cpp
  2. 14
      test/solidityCompiler.cpp
  3. 16
      test/solidityEndToEndTest.cpp

39
libsolidity/Compiler.cpp

@ -81,35 +81,34 @@ void Compiler::appendFunctionSelector(vector<ASTPointer<FunctionDefinition>> con
if (publicFunctions.size() > 255) if (publicFunctions.size() > 255)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("More than 255 public functions for contract.")); BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("More than 255 public functions for contract."));
//@todo check for calldatasize? // retrieve the first byte of the call data, which determines the called function
// retrieve the first byte of the call data // @todo This code had a jump table in a previous version which was more efficient but also
m_context << u256(0) << eth::Instruction::CALLDATALOAD << u256(0) << eth::Instruction::BYTE; // error prone (due to the optimizer and variable length tag addresses)
// check that it is not too large m_context << u256(1) << u256(0) // some constants
m_context << eth::Instruction::DUP1 << u256(publicFunctions.size() - 1) << eth::Instruction::LT; << eth::dupInstruction(1) << eth::Instruction::CALLDATALOAD
eth::AssemblyItem returnTag = m_context.appendConditionalJump(); << eth::dupInstruction(2) << eth::Instruction::BYTE
<< eth::dupInstruction(2);
// otherwise, jump inside jump table (each entry of the table has size 4)
m_context << u256(4) << eth::Instruction::MUL; // stack here: 1 0 <funid> 0, stack top will be counted up until it matches funid
eth::AssemblyItem jumpTableStart = m_context.pushNewTag();
m_context << eth::Instruction::ADD << eth::Instruction::JUMP;
// jump table @todo it could be that the optimizer destroys this
m_context << jumpTableStart;
for (pair<string, pair<FunctionDefinition const*, eth::AssemblyItem>> const& f: publicFunctions) for (pair<string, pair<FunctionDefinition const*, eth::AssemblyItem>> const& f: publicFunctions)
m_context.appendJumpTo(f.second.second) << eth::Instruction::JUMPDEST; {
eth::AssemblyItem const& callDataUnpackerEntry = f.second.second;
m_context << returnTag << eth::Instruction::STOP; m_context << eth::dupInstruction(2) << eth::dupInstruction(2) << eth::Instruction::EQ;
m_context.appendConditionalJumpTo(callDataUnpackerEntry);
m_context << eth::dupInstruction(4) << eth::Instruction::ADD;
//@todo avoid the last ADD (or remove it in the optimizer)
}
m_context << eth::Instruction::STOP; // function not found
for (pair<string, pair<FunctionDefinition const*, eth::AssemblyItem>> const& f: publicFunctions) for (pair<string, pair<FunctionDefinition const*, eth::AssemblyItem>> const& f: publicFunctions)
{ {
FunctionDefinition const& function = *f.second.first; FunctionDefinition const& function = *f.second.first;
m_context << f.second.second; eth::AssemblyItem const& callDataUnpackerEntry = f.second.second;
m_context << callDataUnpackerEntry;
eth::AssemblyItem returnTag = m_context.pushNewTag(); eth::AssemblyItem returnTag = m_context.pushNewTag();
appendCalldataUnpacker(function); appendCalldataUnpacker(function);
m_context.appendJumpTo(m_context.getFunctionEntryLabel(function)); m_context.appendJumpTo(m_context.getFunctionEntryLabel(function));
m_context << returnTag; m_context << returnTag;
appendReturnValuePacker(function); appendReturnValuePacker(function);
} }
} }

14
test/solidityCompiler.cpp

@ -80,7 +80,7 @@ BOOST_AUTO_TEST_CASE(smoke_test)
"}\n"; "}\n";
bytes code = compileContract(sourceCode); bytes code = compileContract(sourceCode);
unsigned boilerplateSize = 51; unsigned boilerplateSize = 42;
bytes expectation({byte(Instruction::JUMPDEST), bytes expectation({byte(Instruction::JUMPDEST),
byte(Instruction::PUSH1), 0x0, // initialize local variable x byte(Instruction::PUSH1), 0x0, // initialize local variable x
byte(Instruction::PUSH1), 0x2, byte(Instruction::PUSH1), 0x2,
@ -100,8 +100,8 @@ BOOST_AUTO_TEST_CASE(different_argument_numbers)
"}\n"; "}\n";
bytes code = compileContract(sourceCode); bytes code = compileContract(sourceCode);
unsigned shift = 75; unsigned shift = 70;
unsigned boilerplateSize = 88; unsigned boilerplateSize = 83;
bytes expectation({byte(Instruction::JUMPDEST), bytes expectation({byte(Instruction::JUMPDEST),
byte(Instruction::PUSH1), 0x0, // initialize return variable d byte(Instruction::PUSH1), 0x0, // initialize return variable d
byte(Instruction::DUP3), byte(Instruction::DUP3),
@ -153,8 +153,8 @@ BOOST_AUTO_TEST_CASE(ifStatement)
"}\n"; "}\n";
bytes code = compileContract(sourceCode); bytes code = compileContract(sourceCode);
unsigned shift = 38; unsigned shift = 29;
unsigned boilerplateSize = 51; unsigned boilerplateSize = 42;
bytes expectation({byte(Instruction::JUMPDEST), bytes expectation({byte(Instruction::JUMPDEST),
byte(Instruction::PUSH1), 0x0, byte(Instruction::PUSH1), 0x0,
byte(Instruction::DUP1), byte(Instruction::DUP1),
@ -195,8 +195,8 @@ BOOST_AUTO_TEST_CASE(loops)
"}\n"; "}\n";
bytes code = compileContract(sourceCode); bytes code = compileContract(sourceCode);
unsigned shift = 38; unsigned shift = 29;
unsigned boilerplateSize = 51; unsigned boilerplateSize = 42;
bytes expectation({byte(Instruction::JUMPDEST), bytes expectation({byte(Instruction::JUMPDEST),
byte(Instruction::JUMPDEST), byte(Instruction::JUMPDEST),
byte(Instruction::PUSH1), 0x1, byte(Instruction::PUSH1), 0x1,

16
test/solidityEndToEndTest.cpp

@ -148,6 +148,22 @@ BOOST_AUTO_TEST_CASE(recursive_calls)
BOOST_CHECK(testSolidityAgainstCpp(0, recursive_calls_cpp, u256(4))); BOOST_CHECK(testSolidityAgainstCpp(0, recursive_calls_cpp, u256(4)));
} }
BOOST_AUTO_TEST_CASE(multiple_functions)
{
char const* sourceCode = "contract test {\n"
" function a() returns(uint n) { return 0; }\n"
" function b() returns(uint n) { return 1; }\n"
" function c() returns(uint n) { return 2; }\n"
" function f() returns(uint n) { return 3; }\n"
"}\n";
compileAndRun(sourceCode);
BOOST_CHECK(callFunction(0, bytes()) == toBigEndian(u256(0)));
BOOST_CHECK(callFunction(1, bytes()) == toBigEndian(u256(1)));
BOOST_CHECK(callFunction(2, bytes()) == toBigEndian(u256(2)));
BOOST_CHECK(callFunction(3, bytes()) == toBigEndian(u256(3)));
BOOST_CHECK(callFunction(4, bytes()) == bytes());
}
BOOST_AUTO_TEST_CASE(while_loop) BOOST_AUTO_TEST_CASE(while_loop)
{ {
char const* sourceCode = "contract test {\n" char const* sourceCode = "contract test {\n"

Loading…
Cancel
Save