Browse Source

Merge remote-tracking branch 'upstream/develop' into evmjit

cl-refactor
Paweł Bylica 10 years ago
parent
commit
56c3a7594f
  1. 2
      libdevcore/Exceptions.h
  2. 11
      libethcore/Exceptions.cpp
  3. 2
      libjsqrc/bignumber.min.js
  4. 8
      libjsqrc/ethereumjs/README.md
  5. 7
      libjsqrc/ethereumjs/bower.json
  6. 900
      libjsqrc/ethereumjs/dist/ethereum.js
  7. 22
      libjsqrc/ethereumjs/dist/ethereum.js.map
  8. 2
      libjsqrc/ethereumjs/dist/ethereum.min.js
  9. 1
      libjsqrc/ethereumjs/example/balance.html
  10. 3
      libjsqrc/ethereumjs/example/contract.html
  11. 4
      libjsqrc/ethereumjs/gulpfile.js
  12. 5
      libjsqrc/ethereumjs/index.js
  13. 370
      libjsqrc/ethereumjs/lib/abi.js
  14. 18
      libjsqrc/ethereumjs/lib/autoprovider.js
  15. 29
      libjsqrc/ethereumjs/lib/contract.js
  16. 86
      libjsqrc/ethereumjs/lib/filter.js
  17. 32
      libjsqrc/ethereumjs/lib/httprpc.js
  18. 119
      libjsqrc/ethereumjs/lib/providermanager.js
  19. 12
      libjsqrc/ethereumjs/lib/qt.js
  20. 187
      libjsqrc/ethereumjs/lib/web3.js
  21. 24
      libjsqrc/ethereumjs/lib/websocket.js
  22. 5
      libjsqrc/ethereumjs/package.json
  23. 830
      libjsqrc/ethereumjs/test/abi.parsers.js
  24. 10
      libjsqrc/ethereumjs/test/db.methods.js
  25. 52
      libjsqrc/ethereumjs/test/eth.methods.js
  26. 2
      libjsqrc/ethereumjs/test/mocha.opts
  27. 12
      libjsqrc/ethereumjs/test/shh.methods.js
  28. 8
      libjsqrc/ethereumjs/test/utils.js
  29. 14
      libjsqrc/ethereumjs/test/web3.methods.js
  30. 1
      libjsqrc/js.qrc
  31. 1
      libqwebthree/QWebThree.h
  32. 67
      libsolidity/AST.cpp
  33. 59
      libsolidity/AST.h
  34. 1
      libsolidity/ASTForward.h
  35. 22
      libsolidity/AST_accept.h
  36. 44
      libsolidity/CallGraph.cpp
  37. 7
      libsolidity/CallGraph.h
  38. 99
      libsolidity/Compiler.cpp
  39. 15
      libsolidity/Compiler.h
  40. 11
      libsolidity/CompilerContext.cpp
  41. 4
      libsolidity/CompilerContext.h
  42. 4
      libsolidity/CompilerStack.cpp
  43. 5
      libsolidity/DeclarationContainer.h
  44. 130
      libsolidity/ExpressionCompiler.cpp
  45. 10
      libsolidity/ExpressionCompiler.h
  46. 12
      libsolidity/GlobalContext.cpp
  47. 2
      libsolidity/GlobalContext.h
  48. 10
      libsolidity/InterfaceHandler.cpp
  49. 116
      libsolidity/NameAndTypeResolver.cpp
  50. 19
      libsolidity/NameAndTypeResolver.h
  51. 30
      libsolidity/Parser.cpp
  52. 1
      libsolidity/Parser.h
  53. 2
      libsolidity/Token.h
  54. 59
      libsolidity/Types.cpp
  55. 21
      libsolidity/Types.h
  56. 5
      libsolidity/grammar.txt
  57. 32
      mix/AppContext.cpp
  58. 2
      mix/AppContext.h
  59. 27
      mix/ClientModel.cpp
  60. 22
      mix/ClientModel.h
  61. 20
      mix/DebuggingStateWrapper.cpp
  62. 15
      mix/DebuggingStateWrapper.h
  63. 31
      mix/Exceptions.cpp
  64. 45
      mix/Exceptions.h
  65. 1
      mix/FileIo.cpp
  66. 5
      mix/MixApplication.cpp
  67. 22
      mix/MixClient.cpp
  68. 22
      mix/MixClient.h
  69. 59
      mix/QBigInt.cpp
  70. 102
      mix/QBigInt.h
  71. 55
      mix/QEther.cpp
  72. 92
      mix/QEther.h
  73. 17
      mix/main.cpp
  74. 3
      mix/qml.qrc
  75. 13
      mix/qml/BigIntValue.qml
  76. 3
      mix/qml/CodeEditorView.qml
  77. 115
      mix/qml/Ether.qml
  78. 15
      mix/qml/EtherValue.qml
  79. 17
      mix/qml/ProjectList.qml
  80. 4
      mix/qml/ProjectModel.qml
  81. 25
      mix/qml/StateDialog.qml
  82. 12
      mix/qml/StateList.qml
  83. 48
      mix/qml/TransactionDialog.qml
  84. 6
      mix/qml/js/Debugger.js
  85. 29
      mix/qml/main.qml
  86. 8
      test/SolidityABIJSON.cpp
  87. 2
      test/SolidityCompiler.cpp
  88. 224
      test/SolidityEndToEndTest.cpp
  89. 25
      test/SolidityExpressionCompiler.cpp
  90. 123
      test/SolidityNameAndTypeResolution.cpp
  91. 36
      test/SolidityNatspecJSON.cpp
  92. 45
      test/SolidityParser.cpp
  93. 12
      test/solidityExecutionFramework.h

2
libdevcore/Exceptions.h

@ -31,7 +31,7 @@
namespace dev namespace dev
{ {
// base class for all exceptions // base class for all exceptions
struct Exception: virtual std::exception, virtual boost::exception {}; struct Exception: virtual std::exception, virtual boost::exception { mutable std::string m_message; };
struct BadHexCharacter: virtual Exception {}; struct BadHexCharacter: virtual Exception {};
struct RLPException: virtual Exception {}; struct RLPException: virtual Exception {};

11
libethcore/Exceptions.cpp

@ -27,11 +27,14 @@ using namespace std;
using namespace dev; using namespace dev;
using namespace dev::eth; using namespace dev::eth;
#if _MSC_VER #if ALL_COMPILERS_ARE_CPP11
#define thread_local __declspec( thread )
#endif
#define ETH_RETURN_STRING(S) thread_local static string s_what; s_what = S; return s_what.c_str(); #define ETH_RETURN_STRING(S) thread_local static string s_what; s_what = S; return s_what.c_str();
#elsif USE_BOOST_TLS
static boost::thread_specific_ptr<string> g_exceptionMessage;
#define ETH_RETURN_STRING(S) if (!g_exceptionMessage.get()); g_exceptionMessage.reset(new string); *g_exceptionMessage.get() = S; return g_exceptionMessage.get()->c_str();
#else
#define ETH_RETURN_STRING(S) m_message = S; return m_message.c_str();
#endif
const char* InvalidBlockFormat::what() const noexcept { ETH_RETURN_STRING("Invalid block format: Bad field " + toString(m_f) + " (" + toHex(m_d) + ")"); } const char* InvalidBlockFormat::what() const noexcept { ETH_RETURN_STRING("Invalid block format: Bad field " + toString(m_f) + " (" + toHex(m_d) + ")"); }
const char* UncleInChain::what() const noexcept { ETH_RETURN_STRING("Uncle in block already mentioned: Uncles " + toString(m_uncles) + " (" + m_block.abridged() + ")"); } const char* UncleInChain::what() const noexcept { ETH_RETURN_STRING("Uncle in block already mentioned: Uncles " + toString(m_uncles) + " (" + m_block.abridged() + ")"); }

2
libjsqrc/bignumber.min.js

File diff suppressed because one or more lines are too long

8
libjsqrc/ethereumjs/README.md

@ -66,16 +66,16 @@ sudo apt-get install npm
sudo apt-get install nodejs-legacy sudo apt-get install nodejs-legacy
``` ```
## Building ### Building (gulp)
```bash (gulp) ```bash
npm run-script build npm run-script build
``` ```
### Testing ### Testing (mocha)
```bash (mocha) ```bash
npm test npm test
``` ```

7
libjsqrc/ethereumjs/bower.json

@ -1,11 +1,12 @@
{ {
"name": "ethereum.js", "name": "ethereum.js",
"namespace": "ethereum", "namespace": "ethereum",
"version": "0.0.3", "version": "0.0.8",
"description": "Ethereum Compatible JavaScript API", "description": "Ethereum Compatible JavaScript API",
"main": ["./dist/ethereum.js", "./dist/ethereum.min.js"], "main": ["./dist/ethereum.js", "./dist/ethereum.min.js"],
"dependencies": { "dependencies": {
"es6-promise": "#master" "es6-promise": "#master",
"bignumber.js": ">=2.0.0"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@ -48,4 +49,4 @@
"index.js", "index.js",
"**/*.txt" "**/*.txt"
] ]
} }

900
libjsqrc/ethereumjs/dist/ethereum.js

File diff suppressed because it is too large

22
libjsqrc/ethereumjs/dist/ethereum.js.map

File diff suppressed because one or more lines are too long

2
libjsqrc/ethereumjs/dist/ethereum.min.js

File diff suppressed because one or more lines are too long

1
libjsqrc/ethereumjs/example/balance.html

@ -3,6 +3,7 @@
<head> <head>
<script type="text/javascript" src="js/es6-promise/promise.min.js"></script> <script type="text/javascript" src="js/es6-promise/promise.min.js"></script>
<script type="text/javascript" src="js/bignumber.js/bignumber.min.js"></script>
<script type="text/javascript" src="../dist/ethereum.js"></script> <script type="text/javascript" src="../dist/ethereum.js"></script>
<script type="text/javascript"> <script type="text/javascript">

3
libjsqrc/ethereumjs/example/contract.html

@ -3,6 +3,7 @@
<head> <head>
<script type="text/javascript" src="js/es6-promise/promise.min.js"></script> <script type="text/javascript" src="js/es6-promise/promise.min.js"></script>
<script type="text/javascript" src="js/bignumber.js/bignumber.min.js"></script>
<script type="text/javascript" src="../dist/ethereum.js"></script> <script type="text/javascript" src="../dist/ethereum.js"></script>
<script type="text/javascript"> <script type="text/javascript">
@ -43,7 +44,7 @@
// create contract // create contract
web3.eth.transact({code: web3.eth.solidity(source)}).then(function (address) { web3.eth.transact({code: web3.eth.solidity(source)}).then(function (address) {
contract = web3.contract(address, desc); contract = web3.eth.contract(address, desc);
document.getElementById('call').style.visibility = 'visible'; document.getElementById('call').style.visibility = 'visible';
}); });
} }

4
libjsqrc/ethereumjs/gulpfile.js

@ -90,7 +90,7 @@ gulp.task('uglify', ['build'], function(){
return uglifyFile('ethereum'); return uglifyFile('ethereum');
}); });
gulp.task('uglify', ['buildDev'], function(){ gulp.task('uglifyDev', ['buildDev'], function(){
return uglifyFile('ethereum'); return uglifyFile('ethereum');
}); });
@ -99,6 +99,6 @@ gulp.task('watch', function() {
}); });
gulp.task('release', ['bower', 'lint', 'build', 'uglify']); gulp.task('release', ['bower', 'lint', 'build', 'uglify']);
gulp.task('dev', ['bower', 'lint', 'buildDev', 'uglify']); gulp.task('dev', ['bower', 'lint', 'buildDev', 'uglifyDev']);
gulp.task('default', ['dev']); gulp.task('default', ['dev']);

5
libjsqrc/ethereumjs/index.js

@ -1,8 +1,11 @@
var web3 = require('./lib/web3'); var web3 = require('./lib/web3');
var ProviderManager = require('./lib/providermanager');
web3.provider = new ProviderManager();
web3.filter = require('./lib/filter');
web3.providers.WebSocketProvider = require('./lib/websocket'); web3.providers.WebSocketProvider = require('./lib/websocket');
web3.providers.HttpRpcProvider = require('./lib/httprpc'); web3.providers.HttpRpcProvider = require('./lib/httprpc');
web3.providers.QtProvider = require('./lib/qt'); web3.providers.QtProvider = require('./lib/qt');
web3.providers.AutoProvider = require('./lib/autoprovider'); web3.providers.AutoProvider = require('./lib/autoprovider');
web3.contract = require('./lib/contract'); web3.eth.contract = require('./lib/contract');
module.exports = web3; module.exports = web3;

370
libjsqrc/ethereumjs/lib/abi.js

@ -23,18 +23,19 @@
// TODO: is these line is supposed to be here? // TODO: is these line is supposed to be here?
if (process.env.NODE_ENV !== 'build') { if (process.env.NODE_ENV !== 'build') {
var web3 = require('./web3'); // jshint ignore:line var BigNumber = require('bignumber.js'); // jshint ignore:line
} }
// TODO: make these be actually accurate instead of falling back onto JS's doubles. var web3 = require('./web3'); // jshint ignore:line
var hexToDec = function (hex) {
return parseInt(hex, 16).toString();
};
var decToHex = function (dec) { BigNumber.config({ ROUNDING_MODE: BigNumber.ROUND_DOWN });
return parseInt(dec).toString(16);
}; var ETH_PADDING = 32;
/// Finds first index of array element matching pattern
/// @param array
/// @param callback pattern
/// @returns index of element
var findIndex = function (array, callback) { var findIndex = function (array, callback) {
var end = false; var end = false;
var i = 0; var i = 0;
@ -44,106 +45,114 @@ var findIndex = function (array, callback) {
return end ? i - 1 : -1; return end ? i - 1 : -1;
}; };
/// @returns a function that is used as a pattern for 'findIndex'
var findMethodIndex = function (json, methodName) { var findMethodIndex = function (json, methodName) {
return findIndex(json, function (method) { return findIndex(json, function (method) {
return method.name === methodName; return method.name === methodName;
}); });
}; };
var padLeft = function (string, chars) { /// @param string string to be padded
return new Array(chars - string.length + 1).join("0") + string; /// @param number of characters that result string should have
/// @param sign, by default 0
/// @returns right aligned string
var padLeft = function (string, chars, sign) {
return new Array(chars - string.length + 1).join(sign ? sign : "0") + string;
}; };
var calcBitPadding = function (type, expected) { /// @param expected type prefix (string)
var value = type.slice(expected.length); /// @returns function which checks if type has matching prefix. if yes, returns true, otherwise false
if (value === "") { var prefixedType = function (prefix) {
return 32; return function (type) {
} return type.indexOf(prefix) === 0;
return parseInt(value) / 8; };
}; };
var calcBytePadding = function (type, expected) { /// @param expected type name (string)
var value = type.slice(expected.length); /// @returns function which checks if type is matching expected one. if yes, returns true, otherwise false
if (value === "") { var namedType = function (name) {
return 32; return function (type) {
} return name === type;
return parseInt(value); };
}; };
var calcRealPadding = function (type, expected) { var arrayType = function (type) {
var value = type.slice(expected.length); return type.slice(-2) === '[]';
if (value === "") {
return 32;
}
var sizes = value.split('x');
for (var padding = 0, i = 0; i < sizes; i++) {
padding += (sizes[i] / 8);
}
return padding;
}; };
var setupInputTypes = function () { /// Formats input value to byte representation of int
/// If value is negative, return it's two's complement
// convert from int, decimal-string, prefixed hex string whatever into a bare hex string. /// If the value is floating point, round it down
var formatStandard = function (value) { /// @returns right-aligned byte representation of int
if (typeof value === "number") var formatInputInt = function (value) {
return value.toString(16); var padding = ETH_PADDING * 2;
else if (typeof value === "string" && value.indexOf('0x') === 0) if (value instanceof BigNumber || typeof value === 'number') {
return value.substr(2); if (typeof value === 'number')
// else if (typeof value === "string") value = new BigNumber(value);
// return web3.toHex(value); value = value.round();
else
return (+value).toString(16); if (value.lessThan(0))
}; value = new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16).plus(value).plus(1);
value = value.toString(16);
var prefixedType = function (prefix, calcPadding) { }
return function (type, value) { else if (value.indexOf('0x') === 0)
var expected = prefix; value = value.substr(2);
if (type.indexOf(expected) !== 0) { else if (typeof value === 'string')
return false; value = formatInputInt(new BigNumber(value));
} else
value = (+value).toString(16);
var padding = calcPadding(type, expected); return padLeft(value, padding);
if (padding > 32) };
return false; // not allowed to be so big.
padding = 32; // override as per the new ABI.
if (prefix === "string")
return web3.fromAscii(value, padding).substr(2);
return padLeft(formatStandard(value), padding * 2);
};
};
var namedType = function (name, padding, formatter) { /// Formats input value to byte representation of string
return function (type, value) { /// @returns left-algined byte representation of string
if (type !== name) { var formatInputString = function (value) {
return false; return web3.fromAscii(value, ETH_PADDING).substr(2);
} };
padding = 32; //override as per the new ABI. /// Formats input value to byte representation of bool
/// @returns right-aligned byte representation bool
var formatInputBool = function (value) {
return '000000000000000000000000000000000000000000000000000000000000000' + (value ? '1' : '0');
};
return padLeft(formatter ? formatter(value) : value, padding * 2); /// Formats input value to byte representation of real
}; /// Values are multiplied by 2^m and encoded as integers
}; /// @returns byte representation of real
var formatInputReal = function (value) {
return formatInputInt(new BigNumber(value).times(new BigNumber(2).pow(128)));
};
var formatBool = function (value) { var dynamicTypeBytes = function (type, value) {
return value ? '01' : '00'; // TODO: decide what to do with array of strings
}; if (arrayType(type) || prefixedType('string')(type))
return formatInputInt(value.length);
return "";
};
/// Setups input formatters for solidity types
/// @returns an array of input formatters
var setupInputTypes = function () {
return [ return [
prefixedType('uint', calcBitPadding), { type: prefixedType('uint'), format: formatInputInt },
prefixedType('int', calcBitPadding), { type: prefixedType('int'), format: formatInputInt },
prefixedType('hash', calcBitPadding), { type: prefixedType('hash'), format: formatInputInt },
prefixedType('string', calcBytePadding), { type: prefixedType('string'), format: formatInputString },
prefixedType('real', calcRealPadding), { type: prefixedType('real'), format: formatInputReal },
prefixedType('ureal', calcRealPadding), { type: prefixedType('ureal'), format: formatInputReal },
namedType('address', 20, formatStandard), { type: namedType('address'), format: formatInputInt },
namedType('bool', 1, formatBool), { type: namedType('bool'), format: formatInputBool }
]; ];
}; };
var inputTypes = setupInputTypes(); var inputTypes = setupInputTypes();
/// Formats input params to bytes
/// @param contract json abi
/// @param name of the method that we want to use
/// @param array of params that will be formatted to bytes
/// @returns bytes representation of input params
var toAbiInput = function (json, methodName, params) { var toAbiInput = function (json, methodName, params) {
var bytes = ""; var bytes = "";
var index = findMethodIndex(json, methodName); var index = findMethodIndex(json, methodName);
@ -153,74 +162,120 @@ var toAbiInput = function (json, methodName, params) {
} }
var method = json[index]; var method = json[index];
var padding = ETH_PADDING * 2;
/// first we iterate in search for dynamic
method.inputs.forEach(function (input, index) {
bytes += dynamicTypeBytes(input.type, params[index]);
});
for (var i = 0; i < method.inputs.length; i++) { method.inputs.forEach(function (input, i) {
var found = false; var typeMatch = false;
for (var j = 0; j < inputTypes.length && !found; j++) { for (var j = 0; j < inputTypes.length && !typeMatch; j++) {
found = inputTypes[j](method.inputs[i].type, params[i]); typeMatch = inputTypes[j].type(method.inputs[i].type, params[i]);
} }
if (!found) { if (!typeMatch) {
console.error('unsupported json type: ' + method.inputs[i].type); console.error('input parser does not support type: ' + method.inputs[i].type);
} }
bytes += found;
} var formatter = inputTypes[j - 1].format;
var toAppend = "";
if (arrayType(method.inputs[i].type))
toAppend = params[i].reduce(function (acc, curr) {
return acc + formatter(curr);
}, "");
else
toAppend = formatter(params[i]);
bytes += toAppend;
});
return bytes; return bytes;
}; };
var setupOutputTypes = function () { /// Check if input value is negative
/// @param value is hex format
/// @returns true if it is negative, otherwise false
var signedIsNegative = function (value) {
return (new BigNumber(value.substr(0, 1), 16).toString(2).substr(0, 1)) === '1';
};
var prefixedType = function (prefix, calcPadding) { /// Formats input right-aligned input bytes to int
return function (type) { /// @returns right-aligned input bytes formatted to int
var expected = prefix; var formatOutputInt = function (value) {
if (type.indexOf(expected) !== 0) { // check if it's negative number
return -1; // it it is, return two's complement
} if (signedIsNegative(value)) {
return new BigNumber(value, 16).minus(new BigNumber('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 16)).minus(1);
}
return new BigNumber(value, 16);
};
var padding = calcPadding(type, expected); /// Formats big right-aligned input bytes to uint
if (padding > 32) /// @returns right-aligned input bytes formatted to uint
return -1; // not allowed to be so big. var formatOutputUInt = function (value) {
padding = 32; // override as per the new ABI. return new BigNumber(value, 16);
return padding * 2; };
};
};
var namedType = function (name, padding) { /// @returns input bytes formatted to real
return function (type) { var formatOutputReal = function (value) {
padding = 32; // override as per the new ABI. return formatOutputInt(value).dividedBy(new BigNumber(2).pow(128));
return name === type ? padding * 2 : -1; };
};
};
var formatInt = function (value) { /// @returns input bytes formatted to ureal
return value.length <= 8 ? +parseInt(value, 16) : hexToDec(value); var formatOutputUReal = function (value) {
}; return formatOutputUInt(value).dividedBy(new BigNumber(2).pow(128));
};
var formatHash = function (value) { /// @returns right-aligned input bytes formatted to hex
return "0x" + value; var formatOutputHash = function (value) {
}; return "0x" + value;
};
var formatBool = function (value) { /// @returns right-aligned input bytes formatted to bool
return value === '1' ? true : false; var formatOutputBool = function (value) {
}; return value === '0000000000000000000000000000000000000000000000000000000000000001' ? true : false;
};
var formatString = function (value) { /// @returns left-aligned input bytes formatted to ascii string
return web3.toAscii(value); var formatOutputString = function (value) {
}; return web3.toAscii(value);
};
/// @returns right-aligned input bytes formatted to address
var formatOutputAddress = function (value) {
return "0x" + value.slice(value.length - 40, value.length);
};
var dynamicBytesLength = function (type) {
if (arrayType(type) || prefixedType('string')(type))
return ETH_PADDING * 2;
return 0;
};
/// Setups output formaters for solidity types
/// @returns an array of output formatters
var setupOutputTypes = function () {
return [ return [
{ padding: prefixedType('uint', calcBitPadding), format: formatInt }, { type: prefixedType('uint'), format: formatOutputUInt },
{ padding: prefixedType('int', calcBitPadding), format: formatInt }, { type: prefixedType('int'), format: formatOutputInt },
{ padding: prefixedType('hash', calcBitPadding), format: formatHash }, { type: prefixedType('hash'), format: formatOutputHash },
{ padding: prefixedType('string', calcBytePadding), format: formatString }, { type: prefixedType('string'), format: formatOutputString },
{ padding: prefixedType('real', calcRealPadding), format: formatInt }, { type: prefixedType('real'), format: formatOutputReal },
{ padding: prefixedType('ureal', calcRealPadding), format: formatInt }, { type: prefixedType('ureal'), format: formatOutputUReal },
{ padding: namedType('address', 20) }, { type: namedType('address'), format: formatOutputAddress },
{ padding: namedType('bool', 1), format: formatBool } { type: namedType('bool'), format: formatOutputBool }
]; ];
}; };
var outputTypes = setupOutputTypes(); var outputTypes = setupOutputTypes();
/// Formats output bytes back to param list
/// @param contract json abi
/// @param name of the method that we want to use
/// @param bytes representtion of output
/// @returns array of output params
var fromAbiOutput = function (json, methodName, output) { var fromAbiOutput = function (json, methodName, output) {
var index = findMethodIndex(json, methodName); var index = findMethodIndex(json, methodName);
@ -232,25 +287,51 @@ var fromAbiOutput = function (json, methodName, output) {
var result = []; var result = [];
var method = json[index]; var method = json[index];
for (var i = 0; i < method.outputs.length; i++) { var padding = ETH_PADDING * 2;
var padding = -1;
for (var j = 0; j < outputTypes.length && padding === -1; j++) { var dynamicPartLength = method.outputs.reduce(function (acc, curr) {
padding = outputTypes[j].padding(method.outputs[i].type); return acc + dynamicBytesLength(curr.type);
}, 0);
var dynamicPart = output.slice(0, dynamicPartLength);
output = output.slice(dynamicPartLength);
method.outputs.forEach(function (out, i) {
var typeMatch = false;
for (var j = 0; j < outputTypes.length && !typeMatch; j++) {
typeMatch = outputTypes[j].type(method.outputs[i].type);
} }
if (padding === -1) { if (!typeMatch) {
// not found output parsing console.error('output parser does not support type: ' + method.outputs[i].type);
continue;
} }
var res = output.slice(0, padding);
var formatter = outputTypes[j - 1].format; var formatter = outputTypes[j - 1].format;
result.push(formatter ? formatter(res) : ("0x" + res)); if (arrayType(method.outputs[i].type)) {
output = output.slice(padding); var size = formatOutputUInt(dynamicPart.slice(0, padding));
} dynamicPart = dynamicPart.slice(padding);
var array = [];
for (var k = 0; k < size; k++) {
array.push(formatter(output.slice(0, padding)));
output = output.slice(padding);
}
result.push(array);
}
else if (prefixedType('string')(method.outputs[i].type)) {
dynamicPart = dynamicPart.slice(padding);
result.push(formatter(output.slice(0, padding)));
output = output.slice(padding);
} else {
result.push(formatter(output.slice(0, padding)));
output = output.slice(padding);
}
});
return result; return result;
}; };
/// @param json abi for contract
/// @returns input parser object for given json abi
var inputParser = function (json) { var inputParser = function (json) {
var parser = {}; var parser = {};
json.forEach(function (method) { json.forEach(function (method) {
@ -263,6 +344,8 @@ var inputParser = function (json) {
return parser; return parser;
}; };
/// @param json abi for contract
/// @returns output parser for given json abi
var outputParser = function (json) { var outputParser = function (json) {
var parser = {}; var parser = {};
json.forEach(function (method) { json.forEach(function (method) {
@ -274,6 +357,9 @@ var outputParser = function (json) {
return parser; return parser;
}; };
/// @param json abi for contract
/// @param method name for which we want to get method signature
/// @returns (promise) contract method signature for method with given name
var methodSignature = function (json, name) { var methodSignature = function (json, name) {
var method = json[findMethodIndex(json, name)]; var method = json[findMethodIndex(json, name)];
var result = name + '('; var result = name + '(';

18
libjsqrc/ethereumjs/lib/autoprovider.js

@ -27,12 +27,21 @@
* if it fails, it uses HttpRpcProvider * if it fails, it uses HttpRpcProvider
*/ */
// TODO: is these line is supposed to be here? var web3 = require('./web3'); // jshint ignore:line
if (process.env.NODE_ENV !== 'build') { if (process.env.NODE_ENV !== 'build') {
var WebSocket = require('ws'); // jshint ignore:line var WebSocket = require('ws'); // jshint ignore:line
var web3 = require('./web3'); // jshint ignore:line
} }
/**
* AutoProvider object prototype is implementing 'provider protocol'
* Automatically tries to setup correct provider(Qt, WebSockets or HttpRpc)
* First it checkes if we are ethereum browser (if navigator.qt object is available)
* if yes, we are using QtProvider
* if no, we check if it is possible to establish websockets connection with ethereum (ws://localhost:40404/eth is default)
* if it's not possible, we are using httprpc provider (http://localhost:8080)
* The constructor allows you to specify uris on which we are trying to connect over http or websockets
* You can do that by passing objects with fields httrpc and websockets
*/
var AutoProvider = function (userOptions) { var AutoProvider = function (userOptions) {
if (web3.haveProvider()) { if (web3.haveProvider()) {
return; return;
@ -63,7 +72,7 @@ var AutoProvider = function (userOptions) {
self.poll = self.provider.poll.bind(self.provider); self.poll = self.provider.poll.bind(self.provider);
} }
self.sendQueue.forEach(function (payload) { self.sendQueue.forEach(function (payload) {
self.provider(payload); self.provider.send(payload);
}); });
self.onmessageQueue.forEach(function (handler) { self.onmessageQueue.forEach(function (handler) {
self.provider.onmessage = handler; self.provider.onmessage = handler;
@ -81,6 +90,8 @@ var AutoProvider = function (userOptions) {
}; };
}; };
/// Sends message forward to the provider, that is being used
/// if provider is not yet set, enqueues the message
AutoProvider.prototype.send = function (payload) { AutoProvider.prototype.send = function (payload) {
if (this.provider) { if (this.provider) {
this.provider.send(payload); this.provider.send(payload);
@ -89,6 +100,7 @@ AutoProvider.prototype.send = function (payload) {
this.sendQueue.push(payload); this.sendQueue.push(payload);
}; };
/// On incoming message sends the message to the provider that is currently being used
Object.defineProperty(AutoProvider.prototype, 'onmessage', { Object.defineProperty(AutoProvider.prototype, 'onmessage', {
set: function (handler) { set: function (handler) {
if (this.provider) { if (this.provider) {

29
libjsqrc/ethereumjs/lib/contract.js

@ -20,16 +20,32 @@
* @date 2014 * @date 2014
*/ */
// TODO: is these line is supposed to be here? var web3 = require('./web3'); // jshint ignore:line
if (process.env.NODE_ENV !== 'build') {
var web3 = require('./web3'); // jshint ignore:line
}
var abi = require('./abi'); var abi = require('./abi');
// method signature length in bytes /// method signature length in bytes
var ETH_METHOD_SIGNATURE_LENGTH = 4; var ETH_METHOD_SIGNATURE_LENGTH = 4;
/**
* This method should be called when we want to call / transact some solidity method from javascript
* it returns an object which has same methods available as solidity contract description
* usage example:
*
* var abi = [{
* name: 'myMethod',
* inputs: [{ name: 'a', type: 'string' }],
* outputs: [{name: 'd', type: 'string' }]
* }]; // contract abi
*
* var myContract = web3.eth.contract('0x0123123121', abi); // creation of contract object
*
* myContract.myMethod('this is test string param for call').call(); // myMethod call
* myContract.myMethod('this is test string param for transact').transact() // myMethod transact
*
* @param address - address of the contract, which should be called
* @param desc - abi json description of the contract, which is being created
* @returns contract object
*/
var contract = function (address, desc) { var contract = function (address, desc) {
var inputParser = abi.inputParser(desc); var inputParser = abi.inputParser(desc);
var outputParser = abi.outputParser(desc); var outputParser = abi.outputParser(desc);
@ -70,3 +86,4 @@ var contract = function (address, desc) {
}; };
module.exports = contract; module.exports = contract;

86
libjsqrc/ethereumjs/lib/filter.js

@ -0,0 +1,86 @@
/*
This file is part of ethereum.js.
ethereum.js is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ethereum.js is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file filter.js
* @authors:
* Jeffrey Wilcke <jeff@ethdev.com>
* Marek Kotewicz <marek@ethdev.com>
* Marian Oancea <marian@ethdev.com>
* Gav Wood <g@ethdev.com>
* @date 2014
*/
var web3 = require('./web3'); // jshint ignore:line
/// should be used when we want to watch something
/// it's using inner polling mechanism and is notified about changes
var Filter = function(options, impl) {
this.impl = impl;
this.callbacks = [];
var self = this;
this.promise = impl.newFilter(options);
this.promise.then(function (id) {
self.id = id;
web3.on(impl.changed, id, self.trigger.bind(self));
web3.provider.startPolling({call: impl.changed, args: [id]}, id);
});
};
/// alias for changed*
Filter.prototype.arrived = function(callback) {
this.changed(callback);
};
/// gets called when there is new eth/shh message
Filter.prototype.changed = function(callback) {
var self = this;
this.promise.then(function(id) {
self.callbacks.push(callback);
});
};
/// trigger calling new message from people
Filter.prototype.trigger = function(messages) {
for(var i = 0; i < this.callbacks.length; i++) {
this.callbacks[i].call(this, messages);
}
};
/// should be called to uninstall current filter
Filter.prototype.uninstall = function() {
var self = this;
this.promise.then(function (id) {
self.impl.uninstallFilter(id);
web3.provider.stopPolling(id);
web3.off(impl.changed, id);
});
};
/// should be called to manually trigger getting latest messages from the client
Filter.prototype.messages = function() {
var self = this;
return this.promise.then(function (id) {
return self.impl.getMessages(id);
});
};
/// alias for messages
Filter.prototype.logs = function () {
return this.messages();
};
module.exports = Filter;

32
libjsqrc/ethereumjs/lib/httprpc.js

@ -26,11 +26,21 @@ if (process.env.NODE_ENV !== 'build') {
var XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest; // jshint ignore:line var XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest; // jshint ignore:line
} }
/**
* HttpRpcProvider object prototype is implementing 'provider protocol'
* Should be used when we want to connect to ethereum backend over http && jsonrpc
* It's compatible with cpp client
* The contructor allows to specify host uri
* This provider is using in-browser polling mechanism
*/
var HttpRpcProvider = function (host) { var HttpRpcProvider = function (host) {
this.handlers = []; this.handlers = [];
this.host = host; this.host = host;
}; };
/// Transforms inner message to proper jsonrpc object
/// @param inner message object
/// @returns jsonrpc object
function formatJsonRpcObject(object) { function formatJsonRpcObject(object) {
return { return {
jsonrpc: '2.0', jsonrpc: '2.0',
@ -40,6 +50,9 @@ function formatJsonRpcObject(object) {
}; };
} }
/// Transforms jsonrpc object to inner message
/// @param incoming jsonrpc message
/// @returns inner message object
function formatJsonRpcMessage(message) { function formatJsonRpcMessage(message) {
var object = JSON.parse(message); var object = JSON.parse(message);
@ -50,6 +63,10 @@ function formatJsonRpcMessage(message) {
}; };
} }
/// Prototype object method
/// Asynchronously sends request to server
/// @param payload is inner message object
/// @param cb is callback which is being called when response is comes back
HttpRpcProvider.prototype.sendRequest = function (payload, cb) { HttpRpcProvider.prototype.sendRequest = function (payload, cb) {
var data = formatJsonRpcObject(payload); var data = formatJsonRpcObject(payload);
@ -63,6 +80,11 @@ HttpRpcProvider.prototype.sendRequest = function (payload, cb) {
}; };
}; };
/// Prototype object method
/// Should be called when we want to send single api request to server
/// Asynchronous
/// On response it passes message to handlers
/// @param payload is inner message object
HttpRpcProvider.prototype.send = function (payload) { HttpRpcProvider.prototype.send = function (payload) {
var self = this; var self = this;
this.sendRequest(payload, function (request) { this.sendRequest(payload, function (request) {
@ -72,6 +94,13 @@ HttpRpcProvider.prototype.send = function (payload) {
}); });
}; };
/// Prototype object method
/// Should be called only for polling requests
/// Asynchronous
/// On response it passege message to handlers, but only if message's result is true or not empty array
/// Otherwise response is being silently ignored
/// @param payload is inner message object
/// @id is id of poll that we are calling
HttpRpcProvider.prototype.poll = function (payload, id) { HttpRpcProvider.prototype.poll = function (payload, id) {
var self = this; var self = this;
this.sendRequest(payload, function (request) { this.sendRequest(payload, function (request) {
@ -85,6 +114,8 @@ HttpRpcProvider.prototype.poll = function (payload, id) {
}); });
}; };
/// Prototype object property
/// Should be used to set message handlers for this provider
Object.defineProperty(HttpRpcProvider.prototype, "onmessage", { Object.defineProperty(HttpRpcProvider.prototype, "onmessage", {
set: function (handler) { set: function (handler) {
this.handlers.push(handler); this.handlers.push(handler);
@ -92,3 +123,4 @@ Object.defineProperty(HttpRpcProvider.prototype, "onmessage", {
}); });
module.exports = HttpRpcProvider; module.exports = HttpRpcProvider;

119
libjsqrc/ethereumjs/lib/providermanager.js

@ -0,0 +1,119 @@
/*
This file is part of ethereum.js.
ethereum.js is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ethereum.js is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file providermanager.js
* @authors:
* Jeffrey Wilcke <jeff@ethdev.com>
* Marek Kotewicz <marek@ethdev.com>
* Marian Oancea <marian@ethdev.com>
* Gav Wood <g@ethdev.com>
* @date 2014
*/
var web3 = require('./web3'); // jshint ignore:line
/**
* Provider manager object prototype
* It's responsible for passing messages to providers
* If no provider is set it's responsible for queuing requests
* It's also responsible for polling the ethereum node for incoming messages
* Default poll timeout is 12 seconds
* If we are running ethereum.js inside ethereum browser, there are backend based tools responsible for polling,
* and provider manager polling mechanism is not used
*/
var ProviderManager = function() {
this.queued = [];
this.polls = [];
this.ready = false;
this.provider = undefined;
this.id = 1;
var self = this;
var poll = function () {
if (self.provider && self.provider.poll) {
self.polls.forEach(function (data) {
data.data._id = self.id;
self.id++;
self.provider.poll(data.data, data.id);
});
}
setTimeout(poll, 12000);
};
poll();
};
/// sends outgoing requests, if provider is not available, enqueue the request
ProviderManager.prototype.send = function(data, cb) {
data._id = this.id;
if (cb) {
web3._callbacks[data._id] = cb;
}
data.args = data.args || [];
this.id++;
if(this.provider !== undefined) {
this.provider.send(data);
} else {
console.warn("provider is not set");
this.queued.push(data);
}
};
/// setups provider, which will be used for sending messages
ProviderManager.prototype.set = function(provider) {
if(this.provider !== undefined && this.provider.unload !== undefined) {
this.provider.unload();
}
this.provider = provider;
this.ready = true;
};
/// resends queued messages
ProviderManager.prototype.sendQueued = function() {
for(var i = 0; this.queued.length; i++) {
// Resend
this.send(this.queued[i]);
}
};
/// @returns true if the provider i properly set
ProviderManager.prototype.installed = function() {
return this.provider !== undefined;
};
/// this method is only used, when we do not have native qt bindings and have to do polling on our own
/// should be callled, on start watching for eth/shh changes
ProviderManager.prototype.startPolling = function (data, pollId) {
if (!this.provider || !this.provider.poll) {
return;
}
this.polls.push({data: data, id: pollId});
};
/// should be called to stop polling for certain watch changes
ProviderManager.prototype.stopPolling = function (pollId) {
for (var i = this.polls.length; i--;) {
var poll = this.polls[i];
if (poll.id === pollId) {
this.polls.splice(i, 1);
}
}
};
module.exports = ProviderManager;

12
libjsqrc/ethereumjs/lib/qt.js

@ -21,6 +21,11 @@
* @date 2014 * @date 2014
*/ */
/**
* QtProvider object prototype is implementing 'provider protocol'
* Should be used inside ethereum browser. It's compatible with cpp and go clients.
* It uses navigator.qt object to pass the messages to native bindings
*/
var QtProvider = function() { var QtProvider = function() {
this.handlers = []; this.handlers = [];
@ -32,10 +37,17 @@ var QtProvider = function() {
}; };
}; };
/// Prototype object method
/// Should be called when we want to send single api request to native bindings
/// Asynchronous
/// Response will be received by navigator.qt.onmessage method and passed to handlers
/// @param payload is inner message object
QtProvider.prototype.send = function(payload) { QtProvider.prototype.send = function(payload) {
navigator.qt.postMessage(JSON.stringify(payload)); navigator.qt.postMessage(JSON.stringify(payload));
}; };
/// Prototype object property
/// Should be used to set message handlers for this provider
Object.defineProperty(QtProvider.prototype, "onmessage", { Object.defineProperty(QtProvider.prototype, "onmessage", {
set: function(handler) { set: function(handler) {
this.handlers.push(handler); this.handlers.push(handler);

187
libjsqrc/ethereumjs/lib/web3.js

@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License You should have received a copy of the GNU Lesser General Public License
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>. along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** @file main.js /** @file web3.js
* @authors: * @authors:
* Jeffrey Wilcke <jeff@ethdev.com> * Jeffrey Wilcke <jeff@ethdev.com>
* Marek Kotewicz <marek@ethdev.com> * Marek Kotewicz <marek@ethdev.com>
@ -23,6 +23,9 @@
* @date 2014 * @date 2014
*/ */
/// Recursively resolves all promises in given object and replaces the resolved values with promises
/// @param any object/array/promise/anything else..
/// @returns (resolves) object with replaced promises with their result
function flattenPromise (obj) { function flattenPromise (obj) {
if (obj instanceof Promise) { if (obj instanceof Promise) {
return Promise.resolve(obj); return Promise.resolve(obj);
@ -62,12 +65,14 @@ function flattenPromise (obj) {
return Promise.resolve(obj); return Promise.resolve(obj);
} }
/// @returns an array of objects describing web3 api methods
var web3Methods = function () { var web3Methods = function () {
return [ return [
{ name: 'sha3', call: 'web3_sha3' } { name: 'sha3', call: 'web3_sha3' }
]; ];
}; };
/// @returns an array of objects describing web3.eth api methods
var ethMethods = function () { var ethMethods = function () {
var blockCall = function (args) { var blockCall = function (args) {
return typeof args[0] === "string" ? "eth_blockByHash" : "eth_blockByNumber"; return typeof args[0] === "string" ? "eth_blockByHash" : "eth_blockByNumber";
@ -101,6 +106,7 @@ var ethMethods = function () {
return methods; return methods;
}; };
/// @returns an array of objects describing web3.eth api properties
var ethProperties = function () { var ethProperties = function () {
return [ return [
{ name: 'coinbase', getter: 'eth_coinbase', setter: 'eth_setCoinbase' }, { name: 'coinbase', getter: 'eth_coinbase', setter: 'eth_setCoinbase' },
@ -115,6 +121,7 @@ var ethProperties = function () {
]; ];
}; };
/// @returns an array of objects describing web3.db api methods
var dbMethods = function () { var dbMethods = function () {
return [ return [
{ name: 'put', call: 'db_put' }, { name: 'put', call: 'db_put' },
@ -124,6 +131,7 @@ var dbMethods = function () {
]; ];
}; };
/// @returns an array of objects describing web3.shh api methods
var shhMethods = function () { var shhMethods = function () {
return [ return [
{ name: 'post', call: 'shh_post' }, { name: 'post', call: 'shh_post' },
@ -134,6 +142,7 @@ var shhMethods = function () {
]; ];
}; };
/// @returns an array of objects describing web3.eth.watch api methods
var ethWatchMethods = function () { var ethWatchMethods = function () {
var newFilter = function (args) { var newFilter = function (args) {
return typeof args[0] === 'string' ? 'eth_newFilterString' : 'eth_newFilter'; return typeof args[0] === 'string' ? 'eth_newFilterString' : 'eth_newFilter';
@ -146,6 +155,7 @@ var ethWatchMethods = function () {
]; ];
}; };
/// @returns an array of objects describing web3.shh.watch api methods
var shhWatchMethods = function () { var shhWatchMethods = function () {
return [ return [
{ name: 'newFilter', call: 'shh_newFilter' }, { name: 'newFilter', call: 'shh_newFilter' },
@ -154,6 +164,8 @@ var shhWatchMethods = function () {
]; ];
}; };
/// creates methods in a given object based on method description on input
/// setups api calls for these methods
var setupMethods = function (obj, methods) { var setupMethods = function (obj, methods) {
methods.forEach(function (method) { methods.forEach(function (method) {
obj[method.name] = function () { obj[method.name] = function () {
@ -177,6 +189,8 @@ var setupMethods = function (obj, methods) {
}); });
}; };
/// creates properties in a given object based on properties description on input
/// setups api calls for these properties
var setupProperties = function (obj, properties) { var setupProperties = function (obj, properties) {
properties.forEach(function (property) { properties.forEach(function (property) {
var proto = {}; var proto = {};
@ -221,7 +235,7 @@ var decToHex = function (dec) {
return parseInt(dec).toString(16); return parseInt(dec).toString(16);
}; };
/// setups web3 object, and it's in-browser executed methods
var web3 = { var web3 = {
_callbacks: {}, _callbacks: {},
_events: {}, _events: {},
@ -237,6 +251,7 @@ var web3 = {
return hex; return hex;
}, },
/// @returns ascii string representation of hex value prefixed with 0x
toAscii: function(hex) { toAscii: function(hex) {
// Find termination // Find termination
var str = ""; var str = "";
@ -244,17 +259,18 @@ var web3 = {
if (hex.substring(0, 2) === '0x') if (hex.substring(0, 2) === '0x')
i = 2; i = 2;
for(; i < l; i+=2) { for(; i < l; i+=2) {
var code = hex.charCodeAt(i); var code = parseInt(hex.substr(i, 2), 16);
if(code === 0) { if(code === 0) {
break; break;
} }
str += String.fromCharCode(parseInt(hex.substr(i, 2), 16)); str += String.fromCharCode(code);
} }
return str; return str;
}, },
/// @returns hex representation (prefixed by 0x) of ascii string
fromAscii: function(str, pad) { fromAscii: function(str, pad) {
pad = pad === undefined ? 0 : pad; pad = pad === undefined ? 0 : pad;
var hex = this.toHex(str); var hex = this.toHex(str);
@ -263,14 +279,17 @@ var web3 = {
return "0x" + hex; return "0x" + hex;
}, },
/// @returns decimal representaton of hex value prefixed by 0x
toDecimal: function (val) { toDecimal: function (val) {
return hexToDec(val.substring(2)); return hexToDec(val.substring(2));
}, },
/// @returns hex representation (prefixed by 0x) of decimal value
fromDecimal: function (val) { fromDecimal: function (val) {
return "0x" + decToHex(val); return "0x" + decToHex(val);
}, },
/// used to transform value/string to eth string
toEth: function(str) { toEth: function(str) {
var val = typeof str === "string" ? str.indexOf('0x') === 0 ? parseInt(str.substr(2), 16) : parseInt(str) : str; var val = typeof str === "string" ? str.indexOf('0x') === 0 ? parseInt(str.substr(2), 16) : parseInt(str) : str;
var unit = 0; var unit = 0;
@ -294,24 +313,24 @@ var web3 = {
return s + ' ' + units[unit]; return s + ' ' + units[unit];
}, },
/// eth object prototype
eth: { eth: {
prototype: Object(), // jshint ignore:line
watch: function (params) { watch: function (params) {
return new Filter(params, ethWatch); return new web3.filter(params, ethWatch);
} }
}, },
db: { /// db object prototype
prototype: Object() // jshint ignore:line db: {},
},
/// shh object prototype
shh: { shh: {
prototype: Object(), // jshint ignore:line
watch: function (params) { watch: function (params) {
return new Filter(params, shhWatch); return new web3.filter(params, shhWatch);
} }
}, },
/// used by filter to register callback with given id
on: function(event, id, cb) { on: function(event, id, cb) {
if(web3._events[event] === undefined) { if(web3._events[event] === undefined) {
web3._events[event] = {}; web3._events[event] = {};
@ -321,6 +340,7 @@ var web3 = {
return this; return this;
}, },
/// used by filter to unregister callback with given id
off: function(event, id) { off: function(event, id) {
if(web3._events[event] !== undefined) { if(web3._events[event] !== undefined) {
delete web3._events[event][id]; delete web3._events[event][id];
@ -329,6 +349,7 @@ var web3 = {
return this; return this;
}, },
/// used to trigger callback registered by filter
trigger: function(event, id, data) { trigger: function(event, id, data) {
var callbacks = web3._events[event]; var callbacks = web3._events[event];
if (!callbacks || !callbacks[id]) { if (!callbacks || !callbacks[id]) {
@ -336,9 +357,15 @@ var web3 = {
} }
var cb = callbacks[id]; var cb = callbacks[id];
cb(data); cb(data);
},
/// @returns true if provider is installed
haveProvider: function() {
return !!web3.provider.provider;
} }
}; };
/// setups all api methods
setupMethods(web3, web3Methods()); setupMethods(web3, web3Methods());
setupMethods(web3.eth, ethMethods()); setupMethods(web3.eth, ethMethods());
setupProperties(web3.eth, ethProperties()); setupProperties(web3.eth, ethProperties());
@ -348,87 +375,14 @@ setupMethods(web3.shh, shhMethods());
var ethWatch = { var ethWatch = {
changed: 'eth_changed' changed: 'eth_changed'
}; };
setupMethods(ethWatch, ethWatchMethods()); setupMethods(ethWatch, ethWatchMethods());
var shhWatch = { var shhWatch = {
changed: 'shh_changed' changed: 'shh_changed'
}; };
setupMethods(shhWatch, shhWatchMethods());
var ProviderManager = function() {
this.queued = [];
this.polls = [];
this.ready = false;
this.provider = undefined;
this.id = 1;
var self = this;
var poll = function () {
if (self.provider && self.provider.poll) {
self.polls.forEach(function (data) {
data.data._id = self.id;
self.id++;
self.provider.poll(data.data, data.id);
});
}
setTimeout(poll, 12000);
};
poll();
};
ProviderManager.prototype.send = function(data, cb) {
data._id = this.id;
if (cb) {
web3._callbacks[data._id] = cb;
}
data.args = data.args || [];
this.id++;
if(this.provider !== undefined) {
this.provider.send(data);
} else {
console.warn("provider is not set");
this.queued.push(data);
}
};
ProviderManager.prototype.set = function(provider) {
if(this.provider !== undefined && this.provider.unload !== undefined) {
this.provider.unload();
}
this.provider = provider;
this.ready = true;
};
ProviderManager.prototype.sendQueued = function() { setupMethods(shhWatch, shhWatchMethods());
for(var i = 0; this.queued.length; i++) {
// Resend
this.send(this.queued[i]);
}
};
ProviderManager.prototype.installed = function() {
return this.provider !== undefined;
};
ProviderManager.prototype.startPolling = function (data, pollId) {
if (!this.provider || !this.provider.poll) {
return;
}
this.polls.push({data: data, id: pollId});
};
ProviderManager.prototype.stopPolling = function (pollId) {
for (var i = this.polls.length; i--;) {
var poll = this.polls[i];
if (poll.id === pollId) {
this.polls.splice(i, 1);
}
}
};
web3.provider = new ProviderManager();
web3.setProvider = function(provider) { web3.setProvider = function(provider) {
provider.onmessage = messageHandler; provider.onmessage = messageHandler;
@ -436,60 +390,7 @@ web3.setProvider = function(provider) {
web3.provider.sendQueued(); web3.provider.sendQueued();
}; };
web3.haveProvider = function() { /// callled when there is new incoming message
return !!web3.provider.provider;
};
var Filter = function(options, impl) {
this.impl = impl;
this.callbacks = [];
var self = this;
this.promise = impl.newFilter(options);
this.promise.then(function (id) {
self.id = id;
web3.on(impl.changed, id, self.trigger.bind(self));
web3.provider.startPolling({call: impl.changed, args: [id]}, id);
});
};
Filter.prototype.arrived = function(callback) {
this.changed(callback);
};
Filter.prototype.changed = function(callback) {
var self = this;
this.promise.then(function(id) {
self.callbacks.push(callback);
});
};
Filter.prototype.trigger = function(messages) {
for(var i = 0; i < this.callbacks.length; i++) {
this.callbacks[i].call(this, messages);
}
};
Filter.prototype.uninstall = function() {
var self = this;
this.promise.then(function (id) {
self.impl.uninstallFilter(id);
web3.provider.stopPolling(id);
web3.off(impl.changed, id);
});
};
Filter.prototype.messages = function() {
var self = this;
return this.promise.then(function (id) {
return self.impl.getMessages(id);
});
};
Filter.prototype.logs = function () {
return this.messages();
};
function messageHandler(data) { function messageHandler(data) {
if(data._event !== undefined) { if(data._event !== undefined) {
web3.trigger(data._event, data._id, data.data); web3.trigger(data._event, data._id, data.data);
@ -505,5 +406,5 @@ function messageHandler(data) {
} }
} }
if (typeof(module) !== "undefined") module.exports = web3;
module.exports = web3;

24
libjsqrc/ethereumjs/lib/websocket.js

@ -27,9 +27,17 @@ if (process.env.NODE_ENV !== 'build') {
var WebSocket = require('ws'); // jshint ignore:line var WebSocket = require('ws'); // jshint ignore:line
} }
/**
* WebSocketProvider object prototype is implementing 'provider protocol'
* Should be used when we want to connect to ethereum backend over websockets
* It's compatible with go client
* The constructor allows to specify host uri
*/
var WebSocketProvider = function(host) { var WebSocketProvider = function(host) {
// onmessage handlers // onmessage handlers
this.handlers = []; this.handlers = [];
// queue will be filled with messages if send is invoked before the ws is ready // queue will be filled with messages if send is invoked before the ws is ready
this.queued = []; this.queued = [];
this.ready = false; this.ready = false;
@ -46,15 +54,20 @@ var WebSocketProvider = function(host) {
this.ws.onopen = function() { this.ws.onopen = function() {
self.ready = true; self.ready = true;
for(var i = 0; i < self.queued.length; i++) { for (var i = 0; i < self.queued.length; i++) {
// Resend // Resend
self.send(self.queued[i]); self.send(self.queued[i]);
} }
}; };
}; };
/// Prototype object method
/// Should be called when we want to send single api request to server
/// Asynchronous, it's using websockets
/// Response for the call will be received by ws.onmessage
/// @param payload is inner message object
WebSocketProvider.prototype.send = function(payload) { WebSocketProvider.prototype.send = function(payload) {
if(this.ready) { if (this.ready) {
var data = JSON.stringify(payload); var data = JSON.stringify(payload);
this.ws.send(data); this.ws.send(data);
@ -63,13 +76,20 @@ WebSocketProvider.prototype.send = function(payload) {
} }
}; };
/// Prototype object method
/// Should be called to add handlers
WebSocketProvider.prototype.onMessage = function(handler) { WebSocketProvider.prototype.onMessage = function(handler) {
this.handlers.push(handler); this.handlers.push(handler);
}; };
/// Prototype object method
/// Should be called to close websockets connection
WebSocketProvider.prototype.unload = function() { WebSocketProvider.prototype.unload = function() {
this.ws.close(); this.ws.close();
}; };
/// Prototype object property
/// Should be used to set message handlers for this provider
Object.defineProperty(WebSocketProvider.prototype, "onmessage", { Object.defineProperty(WebSocketProvider.prototype, "onmessage", {
set: function(provider) { this.onMessage(provider); } set: function(provider) { this.onMessage(provider); }
}); });

5
libjsqrc/ethereumjs/package.json

@ -1,7 +1,7 @@
{ {
"name": "ethereum.js", "name": "ethereum.js",
"namespace": "ethereum", "namespace": "ethereum",
"version": "0.0.7", "version": "0.0.8",
"description": "Ethereum Compatible JavaScript API", "description": "Ethereum Compatible JavaScript API",
"main": "./index.js", "main": "./index.js",
"directories": { "directories": {
@ -10,7 +10,8 @@
"dependencies": { "dependencies": {
"es6-promise": "*", "es6-promise": "*",
"ws": "*", "ws": "*",
"xmlhttprequest": "*" "xmlhttprequest": "*",
"bignumber.js": ">=2.0.0"
}, },
"devDependencies": { "devDependencies": {
"bower": ">=1.3.0", "bower": ">=1.3.0",

830
libjsqrc/ethereumjs/test/abi.parsers.js

@ -1,37 +1,827 @@
var assert = require('assert'); var assert = require('assert');
var BigNumber = require('bignumber.js');
var abi = require('../lib/abi.js'); var abi = require('../lib/abi.js');
var clone = function (object) { return JSON.parse(JSON.stringify(object)); };
var description = [{
"name": "test",
"inputs": [{
"name": "a",
"type": "uint256"
}
],
"outputs": [
{
"name": "d",
"type": "uint256"
}
]
}];
describe('abi', function() { describe('abi', function() {
describe('inputParser', function() { describe('inputParser', function() {
it('should parse ...', function() { it('should parse input uint', function() {
var desc = [{ // given
"name": "multiply", var d = clone(description);
"inputs": [
{ d[0].inputs = [
"name": "a", { type: "uint" }
"type": "uint256" ];
}
], // when
"outputs": [ var parser = abi.inputParser(d);
{
"name": "d", // then
"type": "uint256" assert.equal(parser.test(1), "0000000000000000000000000000000000000000000000000000000000000001");
} assert.equal(parser.test(10), "000000000000000000000000000000000000000000000000000000000000000a");
] assert.equal(
parser.test("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"),
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
);
assert.equal(
parser.test(new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16)),
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
);
assert.equal(parser.test(0.1), "0000000000000000000000000000000000000000000000000000000000000000");
assert.equal(parser.test(3.9), "0000000000000000000000000000000000000000000000000000000000000003");
assert.equal(parser.test('0.1'), "0000000000000000000000000000000000000000000000000000000000000000");
assert.equal(parser.test('3.9'), "0000000000000000000000000000000000000000000000000000000000000003");
});
it('should parse input uint128', function() {
// given
var d = clone(description);
d[0].inputs = [
{ type: "uint128" }
];
// when
var parser = abi.inputParser(d);
// then
assert.equal(parser.test(1), "0000000000000000000000000000000000000000000000000000000000000001");
assert.equal(parser.test(10), "000000000000000000000000000000000000000000000000000000000000000a");
assert.equal(
parser.test("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"),
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
);
assert.equal(
parser.test(new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16)),
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
);
assert.equal(parser.test(0.1), "0000000000000000000000000000000000000000000000000000000000000000");
assert.equal(parser.test(3.9), "0000000000000000000000000000000000000000000000000000000000000003");
assert.equal(parser.test('0.1'), "0000000000000000000000000000000000000000000000000000000000000000");
assert.equal(parser.test('3.9'), "0000000000000000000000000000000000000000000000000000000000000003");
});
it('should parse input uint256', function() {
// given
var d = clone(description);
d[0].inputs = [
{ type: "uint256" }
];
// when
var parser = abi.inputParser(d);
// then
assert.equal(parser.test(1), "0000000000000000000000000000000000000000000000000000000000000001");
assert.equal(parser.test(10), "000000000000000000000000000000000000000000000000000000000000000a");
assert.equal(
parser.test("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"),
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
);
assert.equal(
parser.test(new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16)),
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
);
assert.equal(parser.test(0.1), "0000000000000000000000000000000000000000000000000000000000000000");
assert.equal(parser.test(3.9), "0000000000000000000000000000000000000000000000000000000000000003");
assert.equal(parser.test('0.1'), "0000000000000000000000000000000000000000000000000000000000000000");
assert.equal(parser.test('3.9'), "0000000000000000000000000000000000000000000000000000000000000003");
});
it('should parse input int', function() {
// given
var d = clone(description);
d[0].inputs = [
{ type: "int" }
];
// when
var parser = abi.inputParser(d);
// then
assert.equal(parser.test(1), "0000000000000000000000000000000000000000000000000000000000000001");
assert.equal(parser.test(10), "000000000000000000000000000000000000000000000000000000000000000a");
assert.equal(parser.test(-1), "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
assert.equal(parser.test(-2), "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe");
assert.equal(parser.test(-16), "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0");
assert.equal(
parser.test("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"),
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
);
assert.equal(
parser.test(new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16)),
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
);
assert.equal(parser.test(0.1), "0000000000000000000000000000000000000000000000000000000000000000");
assert.equal(parser.test(3.9), "0000000000000000000000000000000000000000000000000000000000000003");
assert.equal(parser.test('0.1'), "0000000000000000000000000000000000000000000000000000000000000000");
assert.equal(parser.test('3.9'), "0000000000000000000000000000000000000000000000000000000000000003");
});
it('should parse input int128', function() {
// given
var d = clone(description);
d[0].inputs = [
{ type: "int128" }
];
// when
var parser = abi.inputParser(d);
// then
assert.equal(parser.test(1), "0000000000000000000000000000000000000000000000000000000000000001");
assert.equal(parser.test(10), "000000000000000000000000000000000000000000000000000000000000000a");
assert.equal(parser.test(-1), "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
assert.equal(parser.test(-2), "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe");
assert.equal(parser.test(-16), "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0");
assert.equal(
parser.test("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"),
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
);
assert.equal(
parser.test(new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16)),
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
);
assert.equal(parser.test(0.1), "0000000000000000000000000000000000000000000000000000000000000000");
assert.equal(parser.test(3.9), "0000000000000000000000000000000000000000000000000000000000000003");
assert.equal(parser.test('0.1'), "0000000000000000000000000000000000000000000000000000000000000000");
assert.equal(parser.test('3.9'), "0000000000000000000000000000000000000000000000000000000000000003");
});
it('should parse input int256', function() {
// given
var d = clone(description);
d[0].inputs = [
{ type: "int256" }
];
// when
var parser = abi.inputParser(d);
// then
assert.equal(parser.test(1), "0000000000000000000000000000000000000000000000000000000000000001");
assert.equal(parser.test(10), "000000000000000000000000000000000000000000000000000000000000000a");
assert.equal(parser.test(-1), "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
assert.equal(parser.test(-2), "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe");
assert.equal(parser.test(-16), "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0");
assert.equal(
parser.test("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"),
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
);
assert.equal(
parser.test(new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16)),
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
);
assert.equal(parser.test(0.1), "0000000000000000000000000000000000000000000000000000000000000000");
assert.equal(parser.test(3.9), "0000000000000000000000000000000000000000000000000000000000000003");
assert.equal(parser.test('0.1'), "0000000000000000000000000000000000000000000000000000000000000000");
assert.equal(parser.test('3.9'), "0000000000000000000000000000000000000000000000000000000000000003");
});
it('should parse input bool', function() {
// given
var d = clone(description);
d[0].inputs = [
{ type: 'bool' }
];
// when
var parser = abi.inputParser(d);
// then
assert.equal(parser.test(true), "0000000000000000000000000000000000000000000000000000000000000001");
assert.equal(parser.test(false), "0000000000000000000000000000000000000000000000000000000000000000");
});
it('should parse input hash', function() {
// given
var d = clone(description);
d[0].inputs = [
{ type: "hash" }
];
// when
var parser = abi.inputParser(d);
// then
assert.equal(parser.test("0x407d73d8a49eeb85d32cf465507dd71d507100c1"), "000000000000000000000000407d73d8a49eeb85d32cf465507dd71d507100c1");
});
it('should parse input hash256', function() {
// given
var d = clone(description);
d[0].inputs = [
{ type: "hash256" }
];
// when
var parser = abi.inputParser(d);
// then
assert.equal(parser.test("0x407d73d8a49eeb85d32cf465507dd71d507100c1"), "000000000000000000000000407d73d8a49eeb85d32cf465507dd71d507100c1");
});
it('should parse input hash160', function() {
// given
var d = clone(description);
d[0].inputs = [
{ type: "hash160" }
];
// when
var parser = abi.inputParser(d);
// then
assert.equal(parser.test("0x407d73d8a49eeb85d32cf465507dd71d507100c1"), "000000000000000000000000407d73d8a49eeb85d32cf465507dd71d507100c1");
});
it('should parse input address', function () {
// given
var d = clone(description);
d[0].inputs = [
{ type: "address" }
];
// when
var parser = abi.inputParser(d)
// then
assert.equal(parser.test("0x407d73d8a49eeb85d32cf465507dd71d507100c1"), "000000000000000000000000407d73d8a49eeb85d32cf465507dd71d507100c1");
});
it('should parse input string', function () {
// given
var d = clone(description);
d[0].inputs = [
{ type: "string" }
];
// when
var parser = abi.inputParser(d);
// then
assert.equal(
parser.test('hello'),
"000000000000000000000000000000000000000000000000000000000000000568656c6c6f000000000000000000000000000000000000000000000000000000"
);
assert.equal(
parser.test('world'),
"0000000000000000000000000000000000000000000000000000000000000005776f726c64000000000000000000000000000000000000000000000000000000"
);
});
it('should use proper method name', function () {
// given
var d = clone(description);
d[0].name = 'helloworld';
d[0].inputs = [
{ type: "int" }
];
// when
var parser = abi.inputParser(d);
// then
assert.equal(parser.helloworld(1), "0000000000000000000000000000000000000000000000000000000000000001");
});
it('should parse multiple methods', function () {
// given
var d = [{
name: "test",
inputs: [{ type: "int" }],
outputs: [{ type: "int" }]
},{
name: "test2",
inputs: [{ type: "string" }],
outputs: [{ type: "string" }]
}]; }];
var iParser = abi.inputParser(desc); // when
assert.equal(iParser.multiply(1), "0x000000000000000000000000000000000000000000000000000000000000000001"); var parser = abi.inputParser(d);
//then
assert.equal(parser.test(1), "0000000000000000000000000000000000000000000000000000000000000001");
assert.equal(
parser.test2('hello'),
"000000000000000000000000000000000000000000000000000000000000000568656c6c6f000000000000000000000000000000000000000000000000000000"
);
}); });
});
it('should parse input array of ints', function () {
// given
var d = clone(description);
d[0].inputs = [
{ type: "int[]" }
];
// when
var parser = abi.inputParser(d);
// then
assert.equal(
parser.test([5, 6]),
"0000000000000000000000000000000000000000000000000000000000000002" +
"0000000000000000000000000000000000000000000000000000000000000005" +
"0000000000000000000000000000000000000000000000000000000000000006"
);
});
it('should parse input real', function () {
// given
var d = clone(description);
d[0].inputs = [
{ type: 'real' }
];
// when
var parser = abi.inputParser(d);
// then
assert.equal(parser.test([1]), "0000000000000000000000000000000100000000000000000000000000000000");
assert.equal(parser.test([2.125]), "0000000000000000000000000000000220000000000000000000000000000000");
assert.equal(parser.test([8.5]), "0000000000000000000000000000000880000000000000000000000000000000");
assert.equal(parser.test([-1]), "ffffffffffffffffffffffffffffffff00000000000000000000000000000000");
});
it('should parse input ureal', function () {
// given
var d = clone(description);
d[0].inputs = [
{ type: 'ureal' }
];
// when
var parser = abi.inputParser(d);
// then
assert.equal(parser.test([1]), "0000000000000000000000000000000100000000000000000000000000000000");
assert.equal(parser.test([2.125]), "0000000000000000000000000000000220000000000000000000000000000000");
assert.equal(parser.test([8.5]), "0000000000000000000000000000000880000000000000000000000000000000");
});
});
describe('outputParser', function() { describe('outputParser', function() {
it('parse ...', function() { it('should parse output string', function() {
// given
var d = clone(description);
d[0].outputs = [
{ type: "string" }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(
parser.test("0x" +
"0000000000000000000000000000000000000000000000000000000000000005" +
"68656c6c6f000000000000000000000000000000000000000000000000000000")[0],
'hello'
);
assert.equal(
parser.test("0x" +
"0000000000000000000000000000000000000000000000000000000000000005" +
"776f726c64000000000000000000000000000000000000000000000000000000")[0],
'world'
);
}); });
it('should parse output uint', function() {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'uint' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(parser.test("0x0000000000000000000000000000000000000000000000000000000000000001")[0], 1);
assert.equal(parser.test("0x000000000000000000000000000000000000000000000000000000000000000a")[0], 10);
assert.equal(
parser.test("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[0].toString(10),
new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16).toString(10)
);
assert.equal(
parser.test("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0")[0].toString(10),
new BigNumber("fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0", 16).toString(10)
);
});
it('should parse output uint256', function() {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'uint256' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(parser.test("0x0000000000000000000000000000000000000000000000000000000000000001")[0], 1);
assert.equal(parser.test("0x000000000000000000000000000000000000000000000000000000000000000a")[0], 10);
assert.equal(
parser.test("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[0].toString(10),
new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16).toString(10)
);
assert.equal(
parser.test("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0")[0].toString(10),
new BigNumber("fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0", 16).toString(10)
);
});
it('should parse output uint128', function() {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'uint128' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(parser.test("0x0000000000000000000000000000000000000000000000000000000000000001")[0], 1);
assert.equal(parser.test("0x000000000000000000000000000000000000000000000000000000000000000a")[0], 10);
assert.equal(
parser.test("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[0].toString(10),
new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16).toString(10)
);
assert.equal(
parser.test("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0")[0].toString(10),
new BigNumber("fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0", 16).toString(10)
);
});
it('should parse output int', function() {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'int' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(parser.test("0x0000000000000000000000000000000000000000000000000000000000000001")[0], 1);
assert.equal(parser.test("0x000000000000000000000000000000000000000000000000000000000000000a")[0], 10);
assert.equal(parser.test("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[0], -1);
assert.equal(parser.test("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0")[0], -16);
});
it('should parse output int256', function() {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'int256' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(parser.test("0x0000000000000000000000000000000000000000000000000000000000000001")[0], 1);
assert.equal(parser.test("0x000000000000000000000000000000000000000000000000000000000000000a")[0], 10);
assert.equal(parser.test("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[0], -1);
assert.equal(parser.test("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0")[0], -16);
});
it('should parse output int128', function() {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'int128' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(parser.test("0x0000000000000000000000000000000000000000000000000000000000000001")[0], 1);
assert.equal(parser.test("0x000000000000000000000000000000000000000000000000000000000000000a")[0], 10);
assert.equal(parser.test("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[0], -1);
assert.equal(parser.test("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0")[0], -16);
});
it('should parse output hash', function() {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'hash' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(
parser.test("0x000000000000000000000000407d73d8a49eeb85d32cf465507dd71d507100c1")[0],
"0x000000000000000000000000407d73d8a49eeb85d32cf465507dd71d507100c1"
);
});
it('should parse output hash256', function() {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'hash256' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(
parser.test("0x000000000000000000000000407d73d8a49eeb85d32cf465507dd71d507100c1")[0],
"0x000000000000000000000000407d73d8a49eeb85d32cf465507dd71d507100c1"
);
});
it('should parse output hash160', function() {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'hash160' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(
parser.test("0x000000000000000000000000407d73d8a49eeb85d32cf465507dd71d507100c1")[0],
"0x000000000000000000000000407d73d8a49eeb85d32cf465507dd71d507100c1"
);
// TODO shouldnt' the expected hash be shorter?
});
it('should parse output address', function() {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'address' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(
parser.test("0x000000000000000000000000407d73d8a49eeb85d32cf465507dd71d507100c1")[0],
"0x407d73d8a49eeb85d32cf465507dd71d507100c1"
);
});
it('should parse output bool', function() {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'bool' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(parser.test("0x0000000000000000000000000000000000000000000000000000000000000001")[0], true);
assert.equal(parser.test("0x0000000000000000000000000000000000000000000000000000000000000000")[0], false);
});
it('should parse output real', function() {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'real' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(parser.test("0x0000000000000000000000000000000100000000000000000000000000000000")[0], 1);
assert.equal(parser.test("0x0000000000000000000000000000000220000000000000000000000000000000")[0], 2.125);
assert.equal(parser.test("0x0000000000000000000000000000000880000000000000000000000000000000")[0], 8.5);
assert.equal(parser.test("0xffffffffffffffffffffffffffffffff00000000000000000000000000000000")[0], -1);
});
it('should parse output ureal', function() {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'ureal' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(parser.test("0x0000000000000000000000000000000100000000000000000000000000000000")[0], 1);
assert.equal(parser.test("0x0000000000000000000000000000000220000000000000000000000000000000")[0], 2.125);
assert.equal(parser.test("0x0000000000000000000000000000000880000000000000000000000000000000")[0], 8.5);
});
it('should parse multiple output strings', function() {
// given
var d = clone(description);
d[0].outputs = [
{ type: "string" },
{ type: "string" }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(
parser.test("0x" +
"0000000000000000000000000000000000000000000000000000000000000005" +
"0000000000000000000000000000000000000000000000000000000000000005" +
"68656c6c6f000000000000000000000000000000000000000000000000000000" +
"776f726c64000000000000000000000000000000000000000000000000000000")[0],
'hello'
);
assert.equal(
parser.test("0x" +
"0000000000000000000000000000000000000000000000000000000000000005" +
"0000000000000000000000000000000000000000000000000000000000000005" +
"68656c6c6f000000000000000000000000000000000000000000000000000000" +
"776f726c64000000000000000000000000000000000000000000000000000000")[1],
'world'
);
});
it('should use proper method name', function () {
// given
var d = clone(description);
d[0].name = 'helloworld';
d[0].outputs = [
{ type: "int" }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(parser.helloworld("0x0000000000000000000000000000000000000000000000000000000000000001")[0], 1);
});
it('should parse multiple methods', function () {
// given
var d = [{
name: "test",
inputs: [{ type: "int" }],
outputs: [{ type: "int" }]
},{
name: "test2",
inputs: [{ type: "string" }],
outputs: [{ type: "string" }]
}];
// when
var parser = abi.outputParser(d);
//then
assert.equal(parser.test("0000000000000000000000000000000000000000000000000000000000000001")[0], 1);
assert.equal(parser.test2("0x" +
"0000000000000000000000000000000000000000000000000000000000000005" +
"68656c6c6f000000000000000000000000000000000000000000000000000000")[0],
"hello"
);
});
it('should parse output array', function () {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'int[]' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(parser.test("0x" +
"0000000000000000000000000000000000000000000000000000000000000002" +
"0000000000000000000000000000000000000000000000000000000000000005" +
"0000000000000000000000000000000000000000000000000000000000000006")[0][0],
5
);
assert.equal(parser.test("0x" +
"0000000000000000000000000000000000000000000000000000000000000002" +
"0000000000000000000000000000000000000000000000000000000000000005" +
"0000000000000000000000000000000000000000000000000000000000000006")[0][1],
6
);
});
}); });
}); });

10
libjsqrc/ethereumjs/test/db.methods.js

@ -7,12 +7,10 @@ web3.setProvider(new web3.providers.WebSocketProvider('http://localhost:8080'));
describe('web3', function() { describe('web3', function() {
describe('db', function() { describe('db', function() {
it('should have all methods implemented', function() { u.methodExists(web3.db, 'put');
u.methodExists(web3.db, 'put'); u.methodExists(web3.db, 'get');
u.methodExists(web3.db, 'get'); u.methodExists(web3.db, 'putString');
u.methodExists(web3.db, 'putString'); u.methodExists(web3.db, 'getString');
u.methodExists(web3.db, 'getString');
});
}); });
}); });

52
libjsqrc/ethereumjs/test/eth.methods.js

@ -7,35 +7,31 @@ web3.setProvider(new web3.providers.WebSocketProvider('http://localhost:8080'));
describe('web3', function() { describe('web3', function() {
describe('eth', function() { describe('eth', function() {
it('should have all methods implemented', function() { u.methodExists(web3.eth, 'balanceAt');
u.methodExists(web3.eth, 'balanceAt'); u.methodExists(web3.eth, 'stateAt');
u.methodExists(web3.eth, 'stateAt'); u.methodExists(web3.eth, 'storageAt');
u.methodExists(web3.eth, 'storageAt'); u.methodExists(web3.eth, 'countAt');
u.methodExists(web3.eth, 'countAt'); u.methodExists(web3.eth, 'codeAt');
u.methodExists(web3.eth, 'codeAt'); u.methodExists(web3.eth, 'transact');
u.methodExists(web3.eth, 'transact'); u.methodExists(web3.eth, 'call');
u.methodExists(web3.eth, 'call'); u.methodExists(web3.eth, 'block');
u.methodExists(web3.eth, 'block'); u.methodExists(web3.eth, 'transaction');
u.methodExists(web3.eth, 'transaction'); u.methodExists(web3.eth, 'uncle');
u.methodExists(web3.eth, 'uncle'); u.methodExists(web3.eth, 'compilers');
u.methodExists(web3.eth, 'compilers'); u.methodExists(web3.eth, 'lll');
u.methodExists(web3.eth, 'lll'); u.methodExists(web3.eth, 'solidity');
u.methodExists(web3.eth, 'solidity'); u.methodExists(web3.eth, 'serpent');
u.methodExists(web3.eth, 'serpent'); u.methodExists(web3.eth, 'logs');
u.methodExists(web3.eth, 'logs');
});
it('should have all properties implemented', function () { u.propertyExists(web3.eth, 'coinbase');
u.propertyExists(web3.eth, 'coinbase'); u.propertyExists(web3.eth, 'listening');
u.propertyExists(web3.eth, 'listening'); u.propertyExists(web3.eth, 'mining');
u.propertyExists(web3.eth, 'mining'); u.propertyExists(web3.eth, 'gasPrice');
u.propertyExists(web3.eth, 'gasPrice'); u.propertyExists(web3.eth, 'account');
u.propertyExists(web3.eth, 'account'); u.propertyExists(web3.eth, 'accounts');
u.propertyExists(web3.eth, 'accounts'); u.propertyExists(web3.eth, 'peerCount');
u.propertyExists(web3.eth, 'peerCount'); u.propertyExists(web3.eth, 'defaultBlock');
u.propertyExists(web3.eth, 'defaultBlock'); u.propertyExists(web3.eth, 'number');
u.propertyExists(web3.eth, 'number');
});
}); });
}); });

2
libjsqrc/ethereumjs/test/mocha.opts

@ -1,2 +1,2 @@
--reporter Spec --reporter spec

12
libjsqrc/ethereumjs/test/shh.methods.js

@ -7,13 +7,11 @@ web3.setProvider(new web3.providers.WebSocketProvider('http://localhost:8080'));
describe('web3', function() { describe('web3', function() {
describe('shh', function() { describe('shh', function() {
it('should have all methods implemented', function() { u.methodExists(web3.shh, 'post');
u.methodExists(web3.shh, 'post'); u.methodExists(web3.shh, 'newIdentity');
u.methodExists(web3.shh, 'newIdentity'); u.methodExists(web3.shh, 'haveIdentity');
u.methodExists(web3.shh, 'haveIdentity'); u.methodExists(web3.shh, 'newGroup');
u.methodExists(web3.shh, 'newGroup'); u.methodExists(web3.shh, 'addToGroup');
u.methodExists(web3.shh, 'addToGroup');
});
}); });
}); });

8
libjsqrc/ethereumjs/test/utils.js

@ -1,11 +1,15 @@
var assert = require('assert'); var assert = require('assert');
var methodExists = function (object, method) { var methodExists = function (object, method) {
assert.equal('function', typeof object[method], 'method ' + method + ' is not implemented'); it('should have method ' + method + ' implemented', function() {
assert.equal('function', typeof object[method], 'method ' + method + ' is not implemented');
});
}; };
var propertyExists = function (object, property) { var propertyExists = function (object, property) {
assert.equal('object', typeof object[property], 'property ' + property + ' is not implemented'); it('should have property ' + property + ' implemented', function() {
assert.equal('object', typeof object[property], 'property ' + property + ' is not implemented');
});
}; };
module.exports = { module.exports = {

14
libjsqrc/ethereumjs/test/web3.methods.js

@ -6,13 +6,11 @@ var u = require('./utils.js');
web3.setProvider(new web3.providers.WebSocketProvider('http://localhost:8080')); // TODO: create some mock provider web3.setProvider(new web3.providers.WebSocketProvider('http://localhost:8080')); // TODO: create some mock provider
describe('web3', function() { describe('web3', function() {
it('should have all methods implemented', function() { u.methodExists(web3, 'sha3');
u.methodExists(web3, 'sha3'); u.methodExists(web3, 'toAscii');
u.methodExists(web3, 'toAscii'); u.methodExists(web3, 'fromAscii');
u.methodExists(web3, 'fromAscii'); u.methodExists(web3, 'toFixed');
u.methodExists(web3, 'toFixed'); u.methodExists(web3, 'fromFixed');
u.methodExists(web3, 'fromFixed'); u.methodExists(web3, 'offset');
u.methodExists(web3, 'offset');
});
}); });

1
libjsqrc/js.qrc

@ -1,6 +1,7 @@
<RCC> <RCC>
<qresource prefix="/js"> <qresource prefix="/js">
<file>es6-promise-2.0.0.js</file> <file>es6-promise-2.0.0.js</file>
<file>bignumber.min.js</file>
<file>setup.js</file> <file>setup.js</file>
<file alias="webthree.js">ethereumjs/dist/ethereum.js</file> <file alias="webthree.js">ethereumjs/dist/ethereum.js</file>
</qresource> </qresource>

1
libqwebthree/QWebThree.h

@ -85,6 +85,7 @@ private:
_frame->addToJavaScriptWindowObject("_web3", qweb, QWebFrame::ScriptOwnership); \ _frame->addToJavaScriptWindowObject("_web3", qweb, QWebFrame::ScriptOwnership); \
_frame->addToJavaScriptWindowObject("env", _env, QWebFrame::QtOwnership); \ _frame->addToJavaScriptWindowObject("env", _env, QWebFrame::QtOwnership); \
_frame->evaluateJavaScript(contentsOfQResource(":/js/es6-promise-2.0.0.js")); \ _frame->evaluateJavaScript(contentsOfQResource(":/js/es6-promise-2.0.0.js")); \
_frame->evaluateJavaScript(contentsOfQResource(":/js/bignumber.min.js")); \
_frame->evaluateJavaScript(contentsOfQResource(":/js/webthree.js")); \ _frame->evaluateJavaScript(contentsOfQResource(":/js/webthree.js")); \
_frame->evaluateJavaScript(contentsOfQResource(":/js/setup.js")); \ _frame->evaluateJavaScript(contentsOfQResource(":/js/setup.js")); \
} }

67
libsolidity/AST.cpp

@ -43,6 +43,11 @@ TypeError ASTNode::createTypeError(string const& _description) const
void ContractDefinition::checkTypeRequirements() void ContractDefinition::checkTypeRequirements()
{ {
for (ASTPointer<InheritanceSpecifier> const& base: getBaseContracts())
base->checkTypeRequirements();
checkIllegalOverrides();
FunctionDefinition const* constructor = getConstructor(); FunctionDefinition const* constructor = getConstructor();
if (constructor && !constructor->getReturnParameters().empty()) if (constructor && !constructor->getReturnParameters().empty())
BOOST_THROW_EXCEPTION(constructor->getReturnParameterList()->createTypeError( BOOST_THROW_EXCEPTION(constructor->getReturnParameterList()->createTypeError(
@ -52,7 +57,6 @@ void ContractDefinition::checkTypeRequirements()
function->checkTypeRequirements(); function->checkTypeRequirements();
// check for hash collisions in function signatures // check for hash collisions in function signatures
vector<pair<FixedHash<4>, FunctionDefinition const*>> exportedFunctionList = getInterfaceFunctionList();
set<FixedHash<4>> hashes; set<FixedHash<4>> hashes;
for (auto const& hashAndFunction: getInterfaceFunctionList()) for (auto const& hashAndFunction: getInterfaceFunctionList())
{ {
@ -83,17 +87,59 @@ FunctionDefinition const* ContractDefinition::getConstructor() const
return nullptr; return nullptr;
} }
vector<pair<FixedHash<4>, FunctionDefinition const*>> ContractDefinition::getInterfaceFunctionList() const void ContractDefinition::checkIllegalOverrides() const
{ {
vector<pair<FixedHash<4>, FunctionDefinition const*>> exportedFunctions; map<string, FunctionDefinition const*> functions;
for (ASTPointer<FunctionDefinition> const& f: m_definedFunctions)
if (f->isPublic() && f->getName() != getName()) // We search from derived to base, so the stored item causes the error.
for (ContractDefinition const* contract: getLinearizedBaseContracts())
for (ASTPointer<FunctionDefinition> const& function: contract->getDefinedFunctions())
{ {
FixedHash<4> hash(dev::sha3(f->getCanonicalSignature())); if (function->getName() == contract->getName())
exportedFunctions.push_back(make_pair(hash, f.get())); continue; // constructors can neither be overriden nor override anything
FunctionDefinition const*& override = functions[function->getName()];
if (!override)
override = function.get();
else if (override->isPublic() != function->isPublic() ||
override->isDeclaredConst() != function->isDeclaredConst() ||
FunctionType(*override) != FunctionType(*function))
BOOST_THROW_EXCEPTION(override->createTypeError("Override changes extended function signature."));
} }
}
return exportedFunctions; vector<pair<FixedHash<4>, FunctionDefinition const*>> const& ContractDefinition::getInterfaceFunctionList() const
{
if (!m_interfaceFunctionList)
{
set<string> functionsSeen;
m_interfaceFunctionList.reset(new vector<pair<FixedHash<4>, FunctionDefinition const*>>());
for (ContractDefinition const* contract: getLinearizedBaseContracts())
for (ASTPointer<FunctionDefinition> const& f: contract->getDefinedFunctions())
if (f->isPublic() && f->getName() != contract->getName() &&
functionsSeen.count(f->getName()) == 0)
{
functionsSeen.insert(f->getName());
FixedHash<4> hash(dev::sha3(f->getCanonicalSignature()));
m_interfaceFunctionList->push_back(make_pair(hash, f.get()));
}
}
return *m_interfaceFunctionList;
}
void InheritanceSpecifier::checkTypeRequirements()
{
m_baseName->checkTypeRequirements();
for (ASTPointer<Expression> const& argument: m_arguments)
argument->checkTypeRequirements();
ContractDefinition const* base = dynamic_cast<ContractDefinition const*>(m_baseName->getReferencedDeclaration());
solAssert(base, "Base contract not available.");
TypePointers parameterTypes = ContractType(*base).getConstructorType()->getParameterTypes();
if (parameterTypes.size() != m_arguments.size())
BOOST_THROW_EXCEPTION(createTypeError("Wrong argument count for constructor call."));
for (size_t i = 0; i < m_arguments.size(); ++i)
if (!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameterTypes[i]))
BOOST_THROW_EXCEPTION(createTypeError("Invalid type for argument in constructer call."));
} }
void StructDefinition::checkMemberTypes() const void StructDefinition::checkMemberTypes() const
@ -346,7 +392,8 @@ void MemberAccess::checkTypeRequirements()
Type const& type = *m_expression->getType(); Type const& type = *m_expression->getType();
m_type = type.getMemberType(*m_memberName); m_type = type.getMemberType(*m_memberName);
if (!m_type) if (!m_type)
BOOST_THROW_EXCEPTION(createTypeError("Member \"" + *m_memberName + "\" not found in " + type.toString())); BOOST_THROW_EXCEPTION(createTypeError("Member \"" + *m_memberName + "\" not found or not "
"visible in " + type.toString()));
//@todo later, this will not always be STORAGE //@todo later, this will not always be STORAGE
m_lvalue = type.getCategory() == Type::Category::STRUCT ? LValueType::STORAGE : LValueType::NONE; m_lvalue = type.getCategory() == Type::Category::STRUCT ? LValueType::STORAGE : LValueType::NONE;
} }
@ -396,7 +443,7 @@ void Identifier::checkTypeRequirements()
ContractDefinition const* contractDef = dynamic_cast<ContractDefinition const*>(m_referencedDeclaration); ContractDefinition const* contractDef = dynamic_cast<ContractDefinition const*>(m_referencedDeclaration);
if (contractDef) if (contractDef)
{ {
m_type = make_shared<TypeType>(make_shared<ContractType>(*contractDef)); m_type = make_shared<TypeType>(make_shared<ContractType>(*contractDef), m_currentContract);
return; return;
} }
MagicVariableDeclaration const* magicVariable = dynamic_cast<MagicVariableDeclaration const*>(m_referencedDeclaration); MagicVariableDeclaration const* magicVariable = dynamic_cast<MagicVariableDeclaration const*>(m_referencedDeclaration);

59
libsolidity/AST.h

@ -158,10 +158,12 @@ public:
ContractDefinition(Location const& _location, ContractDefinition(Location const& _location,
ASTPointer<ASTString> const& _name, ASTPointer<ASTString> const& _name,
ASTPointer<ASTString> const& _documentation, ASTPointer<ASTString> const& _documentation,
std::vector<ASTPointer<InheritanceSpecifier>> const& _baseContracts,
std::vector<ASTPointer<StructDefinition>> const& _definedStructs, std::vector<ASTPointer<StructDefinition>> const& _definedStructs,
std::vector<ASTPointer<VariableDeclaration>> const& _stateVariables, std::vector<ASTPointer<VariableDeclaration>> const& _stateVariables,
std::vector<ASTPointer<FunctionDefinition>> const& _definedFunctions): std::vector<ASTPointer<FunctionDefinition>> const& _definedFunctions):
Declaration(_location, _name), Declaration(_location, _name),
m_baseContracts(_baseContracts),
m_definedStructs(_definedStructs), m_definedStructs(_definedStructs),
m_stateVariables(_stateVariables), m_stateVariables(_stateVariables),
m_definedFunctions(_definedFunctions), m_definedFunctions(_definedFunctions),
@ -171,12 +173,13 @@ public:
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override; virtual void accept(ASTConstVisitor& _visitor) const override;
std::vector<ASTPointer<InheritanceSpecifier>> const& getBaseContracts() const { return m_baseContracts; }
std::vector<ASTPointer<StructDefinition>> const& getDefinedStructs() const { return m_definedStructs; } std::vector<ASTPointer<StructDefinition>> const& getDefinedStructs() const { return m_definedStructs; }
std::vector<ASTPointer<VariableDeclaration>> const& getStateVariables() const { return m_stateVariables; } std::vector<ASTPointer<VariableDeclaration>> const& getStateVariables() const { return m_stateVariables; }
std::vector<ASTPointer<FunctionDefinition>> const& getDefinedFunctions() const { return m_definedFunctions; } std::vector<ASTPointer<FunctionDefinition>> const& getDefinedFunctions() const { return m_definedFunctions; }
/// Checks that the constructor does not have a "returns" statement and calls /// Checks that there are no illegal overrides, that the constructor does not have a "returns"
/// checkTypeRequirements on all its functions. /// and calls checkTypeRequirements on all its functions.
void checkTypeRequirements(); void checkTypeRequirements();
/// @return A shared pointer of an ASTString. /// @return A shared pointer of an ASTString.
@ -187,16 +190,47 @@ public:
/// as intended for use by the ABI. /// as intended for use by the ABI.
std::map<FixedHash<4>, FunctionDefinition const*> getInterfaceFunctions() const; std::map<FixedHash<4>, FunctionDefinition const*> getInterfaceFunctions() const;
/// List of all (direct and indirect) base contracts in order from derived to base, including
/// the contract itself. Available after name resolution
std::vector<ContractDefinition const*> const& getLinearizedBaseContracts() const { return m_linearizedBaseContracts; }
void setLinearizedBaseContracts(std::vector<ContractDefinition const*> const& _bases) { m_linearizedBaseContracts = _bases; }
/// Returns the constructor or nullptr if no constructor was specified /// Returns the constructor or nullptr if no constructor was specified
FunctionDefinition const* getConstructor() const; FunctionDefinition const* getConstructor() const;
private: private:
std::vector<std::pair<FixedHash<4>, FunctionDefinition const*>> getInterfaceFunctionList() const; void checkIllegalOverrides() const;
std::vector<std::pair<FixedHash<4>, FunctionDefinition const*>> const& getInterfaceFunctionList() const;
std::vector<ASTPointer<InheritanceSpecifier>> m_baseContracts;
std::vector<ASTPointer<StructDefinition>> m_definedStructs; std::vector<ASTPointer<StructDefinition>> m_definedStructs;
std::vector<ASTPointer<VariableDeclaration>> m_stateVariables; std::vector<ASTPointer<VariableDeclaration>> m_stateVariables;
std::vector<ASTPointer<FunctionDefinition>> m_definedFunctions; std::vector<ASTPointer<FunctionDefinition>> m_definedFunctions;
ASTPointer<ASTString> m_documentation; ASTPointer<ASTString> m_documentation;
std::vector<ContractDefinition const*> m_linearizedBaseContracts;
mutable std::unique_ptr<std::vector<std::pair<FixedHash<4>, FunctionDefinition const*>>> m_interfaceFunctionList;
};
class InheritanceSpecifier: public ASTNode
{
public:
InheritanceSpecifier(Location const& _location, ASTPointer<Identifier> const& _baseName,
std::vector<ASTPointer<Expression>> _arguments):
ASTNode(_location), m_baseName(_baseName), m_arguments(_arguments) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
ASTPointer<Identifier> const& getName() const { return m_baseName; }
std::vector<ASTPointer<Expression>> const& getArguments() const { return m_arguments; }
void checkTypeRequirements();
private:
ASTPointer<Identifier> m_baseName;
std::vector<ASTPointer<Expression>> m_arguments;
}; };
class StructDefinition: public Declaration class StructDefinition: public Declaration
@ -581,7 +615,7 @@ public:
virtual void accept(ASTConstVisitor& _visitor) const override; virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override; virtual void checkTypeRequirements() override;
void setFunctionReturnParameters(ParameterList& _parameters) { m_returnParameters = &_parameters; } void setFunctionReturnParameters(ParameterList const& _parameters) { m_returnParameters = &_parameters; }
ParameterList const& getFunctionReturnParameters() const ParameterList const& getFunctionReturnParameters() const
{ {
solAssert(m_returnParameters, ""); solAssert(m_returnParameters, "");
@ -593,7 +627,7 @@ private:
ASTPointer<Expression> m_expression; ///< value to return, optional ASTPointer<Expression> m_expression; ///< value to return, optional
/// Pointer to the parameter list of the function, filled by the @ref NameAndTypeResolver. /// Pointer to the parameter list of the function, filled by the @ref NameAndTypeResolver.
ParameterList* m_returnParameters; ParameterList const* m_returnParameters;
}; };
/** /**
@ -870,21 +904,30 @@ class Identifier: public PrimaryExpression
{ {
public: public:
Identifier(Location const& _location, ASTPointer<ASTString> const& _name): Identifier(Location const& _location, ASTPointer<ASTString> const& _name):
PrimaryExpression(_location), m_name(_name), m_referencedDeclaration(nullptr) {} PrimaryExpression(_location), m_name(_name) {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override; virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override; virtual void checkTypeRequirements() override;
ASTString const& getName() const { return *m_name; } ASTString const& getName() const { return *m_name; }
void setReferencedDeclaration(Declaration const& _referencedDeclaration) { m_referencedDeclaration = &_referencedDeclaration; } void setReferencedDeclaration(Declaration const& _referencedDeclaration,
ContractDefinition const* _currentContract = nullptr)
{
m_referencedDeclaration = &_referencedDeclaration;
m_currentContract = _currentContract;
}
Declaration const* getReferencedDeclaration() const { return m_referencedDeclaration; } Declaration const* getReferencedDeclaration() const { return m_referencedDeclaration; }
ContractDefinition const* getCurrentContract() const { return m_currentContract; }
private: private:
ASTPointer<ASTString> m_name; ASTPointer<ASTString> m_name;
/// Declaration the name refers to. /// Declaration the name refers to.
Declaration const* m_referencedDeclaration; Declaration const* m_referencedDeclaration = nullptr;
/// Stores a reference to the current contract. This is needed because types of base contracts
/// change depending on the context.
ContractDefinition const* m_currentContract = nullptr;
}; };
/** /**

1
libsolidity/ASTForward.h

@ -38,6 +38,7 @@ class SourceUnit;
class ImportDirective; class ImportDirective;
class Declaration; class Declaration;
class ContractDefinition; class ContractDefinition;
class InheritanceSpecifier;
class StructDefinition; class StructDefinition;
class ParameterList; class ParameterList;
class FunctionDefinition; class FunctionDefinition;

22
libsolidity/AST_accept.h

@ -61,6 +61,7 @@ void ContractDefinition::accept(ASTVisitor& _visitor)
{ {
if (_visitor.visit(*this)) if (_visitor.visit(*this))
{ {
listAccept(m_baseContracts, _visitor);
listAccept(m_definedStructs, _visitor); listAccept(m_definedStructs, _visitor);
listAccept(m_stateVariables, _visitor); listAccept(m_stateVariables, _visitor);
listAccept(m_definedFunctions, _visitor); listAccept(m_definedFunctions, _visitor);
@ -72,6 +73,7 @@ void ContractDefinition::accept(ASTConstVisitor& _visitor) const
{ {
if (_visitor.visit(*this)) if (_visitor.visit(*this))
{ {
listAccept(m_baseContracts, _visitor);
listAccept(m_definedStructs, _visitor); listAccept(m_definedStructs, _visitor);
listAccept(m_stateVariables, _visitor); listAccept(m_stateVariables, _visitor);
listAccept(m_definedFunctions, _visitor); listAccept(m_definedFunctions, _visitor);
@ -79,6 +81,26 @@ void ContractDefinition::accept(ASTConstVisitor& _visitor) const
_visitor.endVisit(*this); _visitor.endVisit(*this);
} }
void InheritanceSpecifier::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
{
m_baseName->accept(_visitor);
listAccept(m_arguments, _visitor);
}
_visitor.endVisit(*this);
}
void InheritanceSpecifier::accept(ASTConstVisitor& _visitor) const
{
if (_visitor.visit(*this))
{
m_baseName->accept(_visitor);
listAccept(m_arguments, _visitor);
}
_visitor.endVisit(*this);
}
void StructDefinition::accept(ASTVisitor& _visitor) void StructDefinition::accept(ASTVisitor& _visitor)
{ {
if (_visitor.visit(*this)) if (_visitor.visit(*this))

44
libsolidity/CallGraph.cpp

@ -31,13 +31,9 @@ namespace dev
namespace solidity namespace solidity
{ {
void CallGraph::addFunction(FunctionDefinition const& _function) void CallGraph::addNode(ASTNode const& _node)
{ {
if (!m_functionsSeen.count(&_function)) _node.accept(*this);
{
m_functionsSeen.insert(&_function);
m_workQueue.push(&_function);
}
} }
set<FunctionDefinition const*> const& CallGraph::getCalls() set<FunctionDefinition const*> const& CallGraph::getCalls()
@ -63,5 +59,41 @@ bool CallGraph::visit(Identifier const& _identifier)
return true; return true;
} }
bool CallGraph::visit(FunctionDefinition const& _function)
{
addFunction(_function);
return true;
}
bool CallGraph::visit(MemberAccess const& _memberAccess)
{
// used for "BaseContract.baseContractFunction"
if (_memberAccess.getExpression().getType()->getCategory() == Type::Category::TYPE)
{
TypeType const& type = dynamic_cast<TypeType const&>(*_memberAccess.getExpression().getType());
if (type.getMembers().getMemberType(_memberAccess.getMemberName()))
{
ContractDefinition const& contract = dynamic_cast<ContractType const&>(*type.getActualType())
.getContractDefinition();
for (ASTPointer<FunctionDefinition> const& function: contract.getDefinedFunctions())
if (function->getName() == _memberAccess.getMemberName())
{
addFunction(*function);
return true;
}
}
}
return true;
}
void CallGraph::addFunction(FunctionDefinition const& _function)
{
if (!m_functionsSeen.count(&_function))
{
m_functionsSeen.insert(&_function);
m_workQueue.push(&_function);
}
}
} }
} }

7
libsolidity/CallGraph.h

@ -38,14 +38,17 @@ namespace solidity
class CallGraph: private ASTConstVisitor class CallGraph: private ASTConstVisitor
{ {
public: public:
void addFunction(FunctionDefinition const& _function); void addNode(ASTNode const& _node);
void computeCallGraph(); void computeCallGraph();
std::set<FunctionDefinition const*> const& getCalls(); std::set<FunctionDefinition const*> const& getCalls();
private: private:
void addFunctionToQueue(FunctionDefinition const& _function); virtual bool visit(FunctionDefinition const& _function) override;
virtual bool visit(Identifier const& _identifier) override; virtual bool visit(Identifier const& _identifier) override;
virtual bool visit(MemberAccess const& _memberAccess) override;
void addFunction(FunctionDefinition const& _function);
std::set<FunctionDefinition const*> m_functionsSeen; std::set<FunctionDefinition const*> m_functionsSeen;
std::queue<FunctionDefinition const*> m_workQueue; std::queue<FunctionDefinition const*> m_workQueue;

99
libsolidity/Compiler.cpp

@ -21,6 +21,7 @@
*/ */
#include <algorithm> #include <algorithm>
#include <boost/range/adaptor/reversed.hpp>
#include <libevmcore/Instruction.h> #include <libevmcore/Instruction.h>
#include <libevmcore/Assembly.h> #include <libevmcore/Assembly.h>
#include <libsolidity/AST.h> #include <libsolidity/AST.h>
@ -34,48 +35,84 @@ using namespace std;
namespace dev { namespace dev {
namespace solidity { namespace solidity {
void Compiler::compileContract(ContractDefinition const& _contract, vector<MagicVariableDeclaration const*> const& _magicGlobals, void Compiler::compileContract(ContractDefinition const& _contract,
map<ContractDefinition const*, bytes const*> const& _contracts) map<ContractDefinition const*, bytes const*> const& _contracts)
{ {
m_context = CompilerContext(); // clear it just in case m_context = CompilerContext(); // clear it just in case
initializeContext(_contract, _magicGlobals, _contracts); initializeContext(_contract, _contracts);
for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions()) for (ContractDefinition const* contract: _contract.getLinearizedBaseContracts())
if (function->getName() != _contract.getName()) // don't add the constructor here for (ASTPointer<FunctionDefinition> const& function: contract->getDefinedFunctions())
m_context.addFunction(*function); if (function->getName() != contract->getName()) // don't add the constructor here
m_context.addFunction(*function);
appendFunctionSelector(_contract); appendFunctionSelector(_contract);
for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions()) for (ContractDefinition const* contract: _contract.getLinearizedBaseContracts())
if (function->getName() != _contract.getName()) // don't add the constructor here for (ASTPointer<FunctionDefinition> const& function: contract->getDefinedFunctions())
function->accept(*this); if (function->getName() != contract->getName()) // don't add the constructor here
function->accept(*this);
// Swap the runtime context with the creation-time context // Swap the runtime context with the creation-time context
swap(m_context, m_runtimeContext); swap(m_context, m_runtimeContext);
initializeContext(_contract, _magicGlobals, _contracts); initializeContext(_contract, _contracts);
packIntoContractCreator(_contract, m_runtimeContext); packIntoContractCreator(_contract, m_runtimeContext);
} }
void Compiler::initializeContext(ContractDefinition const& _contract, vector<MagicVariableDeclaration const*> const& _magicGlobals, void Compiler::initializeContext(ContractDefinition const& _contract,
map<ContractDefinition const*, bytes const*> const& _contracts) map<ContractDefinition const*, bytes const*> const& _contracts)
{ {
m_context.setCompiledContracts(_contracts); m_context.setCompiledContracts(_contracts);
for (MagicVariableDeclaration const* variable: _magicGlobals)
m_context.addMagicGlobal(*variable);
registerStateVariables(_contract); registerStateVariables(_contract);
} }
void Compiler::packIntoContractCreator(ContractDefinition const& _contract, CompilerContext const& _runtimeContext) void Compiler::packIntoContractCreator(ContractDefinition const& _contract, CompilerContext const& _runtimeContext)
{ {
// arguments for base constructors, filled in derived-to-base order
map<ContractDefinition const*, vector<ASTPointer<Expression>> const*> baseArguments;
set<FunctionDefinition const*> neededFunctions; set<FunctionDefinition const*> neededFunctions;
FunctionDefinition const* constructor = _contract.getConstructor(); set<ASTNode const*> nodesUsedInConstructors;
if (constructor)
neededFunctions = getFunctionsNeededByConstructor(*constructor); // Determine the arguments that are used for the base constructors and also which functions
// are needed at compile time.
std::vector<ContractDefinition const*> const& bases = _contract.getLinearizedBaseContracts();
for (ContractDefinition const* contract: bases)
{
if (FunctionDefinition const* constructor = contract->getConstructor())
nodesUsedInConstructors.insert(constructor);
for (ASTPointer<InheritanceSpecifier> const& base: contract->getBaseContracts())
{
ContractDefinition const* baseContract = dynamic_cast<ContractDefinition const*>(
base->getName()->getReferencedDeclaration());
solAssert(baseContract, "");
if (baseArguments.count(baseContract) == 0)
{
baseArguments[baseContract] = &base->getArguments();
for (ASTPointer<Expression> const& arg: base->getArguments())
nodesUsedInConstructors.insert(arg.get());
}
}
}
//@TODO add virtual functions
neededFunctions = getFunctionsCalled(nodesUsedInConstructors);
for (FunctionDefinition const* fun: neededFunctions) for (FunctionDefinition const* fun: neededFunctions)
m_context.addFunction(*fun); m_context.addFunction(*fun);
if (constructor) // Call constructors in base-to-derived order.
appendConstructorCall(*constructor); // The Constructor for the most derived contract is called later.
for (unsigned i = 1; i < bases.size(); i++)
{
ContractDefinition const* base = bases[bases.size() - i];
solAssert(base, "");
FunctionDefinition const* baseConstructor = base->getConstructor();
if (!baseConstructor)
continue;
solAssert(baseArguments[base], "");
appendBaseConstructorCall(*baseConstructor, *baseArguments[base]);
}
if (_contract.getConstructor())
appendConstructorCall(*_contract.getConstructor());
eth::AssemblyItem sub = m_context.addSubroutine(_runtimeContext.getAssembly()); eth::AssemblyItem sub = m_context.addSubroutine(_runtimeContext.getAssembly());
// stack contains sub size // stack contains sub size
@ -88,6 +125,21 @@ void Compiler::packIntoContractCreator(ContractDefinition const& _contract, Comp
fun->accept(*this); fun->accept(*this);
} }
void Compiler::appendBaseConstructorCall(FunctionDefinition const& _constructor,
vector<ASTPointer<Expression>> const& _arguments)
{
FunctionType constructorType(_constructor);
eth::AssemblyItem returnLabel = m_context.pushNewTag();
for (unsigned i = 0; i < _arguments.size(); ++i)
{
compileExpression(*_arguments[i]);
ExpressionCompiler::appendTypeConversion(m_context, *_arguments[i]->getType(),
*constructorType.getParameterTypes()[i]);
}
m_context.appendJumpTo(m_context.getFunctionEntryLabel(_constructor));
m_context << returnLabel;
}
void Compiler::appendConstructorCall(FunctionDefinition const& _constructor) void Compiler::appendConstructorCall(FunctionDefinition const& _constructor)
{ {
eth::AssemblyItem returnTag = m_context.pushNewTag(); eth::AssemblyItem returnTag = m_context.pushNewTag();
@ -107,11 +159,12 @@ void Compiler::appendConstructorCall(FunctionDefinition const& _constructor)
m_context << returnTag; m_context << returnTag;
} }
set<FunctionDefinition const*> Compiler::getFunctionsNeededByConstructor(FunctionDefinition const& _constructor) set<FunctionDefinition const*> Compiler::getFunctionsCalled(set<ASTNode const*> const& _nodes)
{ {
// TODO this does not add virtual functions
CallGraph callgraph; CallGraph callgraph;
callgraph.addFunction(_constructor); for (ASTNode const* node: _nodes)
callgraph.computeCallGraph(); callgraph.addNode(*node);
return callgraph.getCalls(); return callgraph.getCalls();
} }
@ -193,9 +246,9 @@ void Compiler::appendReturnValuePacker(FunctionDefinition const& _function)
void Compiler::registerStateVariables(ContractDefinition const& _contract) void Compiler::registerStateVariables(ContractDefinition const& _contract)
{ {
//@todo sort them? for (ContractDefinition const* contract: boost::adaptors::reverse(_contract.getLinearizedBaseContracts()))
for (ASTPointer<VariableDeclaration> const& variable: _contract.getStateVariables()) for (ASTPointer<VariableDeclaration> const& variable: contract->getStateVariables())
m_context.addStateVariable(*variable); m_context.addStateVariable(*variable);
} }
bool Compiler::visit(FunctionDefinition const& _function) bool Compiler::visit(FunctionDefinition const& _function)

15
libsolidity/Compiler.h

@ -32,23 +32,24 @@ class Compiler: private ASTConstVisitor
public: public:
explicit Compiler(bool _optimize = false): m_optimize(_optimize), m_context(), m_returnTag(m_context.newTag()) {} explicit Compiler(bool _optimize = false): m_optimize(_optimize), m_context(), m_returnTag(m_context.newTag()) {}
void compileContract(ContractDefinition const& _contract, std::vector<MagicVariableDeclaration const*> const& _magicGlobals, void compileContract(ContractDefinition const& _contract,
std::map<ContractDefinition const*, bytes const*> const& _contracts); std::map<ContractDefinition const*, bytes const*> const& _contracts);
bytes getAssembledBytecode() { return m_context.getAssembledBytecode(m_optimize); } bytes getAssembledBytecode() { return m_context.getAssembledBytecode(m_optimize); }
bytes getRuntimeBytecode() { return m_runtimeContext.getAssembledBytecode(m_optimize);} bytes getRuntimeBytecode() { return m_runtimeContext.getAssembledBytecode(m_optimize);}
void streamAssembly(std::ostream& _stream) const { m_context.streamAssembly(_stream); } void streamAssembly(std::ostream& _stream) const { m_context.streamAssembly(_stream); }
private: private:
/// Registers the global objects and the non-function objects inside the contract with the context. /// Registers the non-function objects inside the contract with the context.
void initializeContext(ContractDefinition const& _contract, std::vector<MagicVariableDeclaration const*> const& _magicGlobals, void initializeContext(ContractDefinition const& _contract,
std::map<ContractDefinition const*, bytes const*> const& _contracts); std::map<ContractDefinition const*, bytes const*> const& _contracts);
/// Adds the code that is run at creation time. Should be run after exchanging the run-time context /// Adds the code that is run at creation time. Should be run after exchanging the run-time context
/// with a new and initialized context. /// with a new and initialized context. Adds the constructor code.
/// adds the constructor code.
void packIntoContractCreator(ContractDefinition const& _contract, CompilerContext const& _runtimeContext); void packIntoContractCreator(ContractDefinition const& _contract, CompilerContext const& _runtimeContext);
void appendBaseConstructorCall(FunctionDefinition const& _constructor,
std::vector<ASTPointer<Expression>> const& _arguments);
void appendConstructorCall(FunctionDefinition const& _constructor); void appendConstructorCall(FunctionDefinition const& _constructor);
/// Recursively searches the call graph and returns all functions needed by the constructor (including itself). /// Recursively searches the call graph and returns all functions referenced inside _nodes.
std::set<FunctionDefinition const*> getFunctionsNeededByConstructor(FunctionDefinition const& _constructor); std::set<FunctionDefinition const*> getFunctionsCalled(std::set<ASTNode const*> const& _nodes);
void appendFunctionSelector(ContractDefinition const& _contract); void appendFunctionSelector(ContractDefinition const& _contract);
/// Creates code that unpacks the arguments for the given function, from memory if /// Creates code that unpacks the arguments for the given function, from memory if
/// @a _fromMemory is true, otherwise from call data. @returns the size of the data in bytes. /// @a _fromMemory is true, otherwise from call data. @returns the size of the data in bytes.

11
libsolidity/CompilerContext.cpp

@ -61,7 +61,9 @@ void CompilerContext::addAndInitializeVariable(VariableDeclaration const& _decla
void CompilerContext::addFunction(FunctionDefinition const& _function) void CompilerContext::addFunction(FunctionDefinition const& _function)
{ {
m_functionEntryLabels.insert(std::make_pair(&_function, m_asm.newTag())); eth::AssemblyItem tag(m_asm.newTag());
m_functionEntryLabels.insert(make_pair(&_function, tag));
m_virtualFunctionEntryLabels.insert(make_pair(_function.getName(), tag));
} }
bytes const& CompilerContext::getCompiledContract(const ContractDefinition& _contract) const bytes const& CompilerContext::getCompiledContract(const ContractDefinition& _contract) const
@ -83,6 +85,13 @@ eth::AssemblyItem CompilerContext::getFunctionEntryLabel(FunctionDefinition cons
return res->second.tag(); return res->second.tag();
} }
eth::AssemblyItem CompilerContext::getVirtualFunctionEntryLabel(FunctionDefinition const& _function) const
{
auto res = m_virtualFunctionEntryLabels.find(_function.getName());
solAssert(res != m_virtualFunctionEntryLabels.end(), "Function entry label not found.");
return res->second.tag();
}
unsigned CompilerContext::getBaseStackOffsetOfVariable(Declaration const& _declaration) const unsigned CompilerContext::getBaseStackOffsetOfVariable(Declaration const& _declaration) const
{ {
auto res = m_localVariables.find(&_declaration); auto res = m_localVariables.find(&_declaration);

4
libsolidity/CompilerContext.h

@ -57,6 +57,8 @@ public:
bool isStateVariable(Declaration const* _declaration) const { return m_stateVariables.count(_declaration) != 0; } bool isStateVariable(Declaration const* _declaration) const { return m_stateVariables.count(_declaration) != 0; }
eth::AssemblyItem getFunctionEntryLabel(FunctionDefinition const& _function) const; eth::AssemblyItem getFunctionEntryLabel(FunctionDefinition const& _function) const;
/// @returns the entry label of the given function and takes overrides into account.
eth::AssemblyItem getVirtualFunctionEntryLabel(FunctionDefinition const& _function) const;
/// Returns the distance of the given local variable from the top of the local variable stack. /// Returns the distance of the given local variable from the top of the local variable stack.
unsigned getBaseStackOffsetOfVariable(Declaration const& _declaration) const; unsigned getBaseStackOffsetOfVariable(Declaration const& _declaration) const;
/// If supplied by a value returned by @ref getBaseStackOffsetOfVariable(variable), returns /// If supplied by a value returned by @ref getBaseStackOffsetOfVariable(variable), returns
@ -116,6 +118,8 @@ private:
unsigned m_localVariablesSize; unsigned m_localVariablesSize;
/// Labels pointing to the entry points of funcitons. /// Labels pointing to the entry points of funcitons.
std::map<Declaration const*, eth::AssemblyItem> m_functionEntryLabels; std::map<Declaration const*, eth::AssemblyItem> m_functionEntryLabels;
/// Labels pointing to the entry points of function overrides.
std::map<std::string, eth::AssemblyItem> m_virtualFunctionEntryLabels;
}; };
} }

4
libsolidity/CompilerStack.cpp

@ -113,10 +113,8 @@ void CompilerStack::compile(bool _optimize)
for (ASTPointer<ASTNode> const& node: source->ast->getNodes()) for (ASTPointer<ASTNode> const& node: source->ast->getNodes())
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get())) if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
{ {
m_globalContext->setCurrentContract(*contract);
shared_ptr<Compiler> compiler = make_shared<Compiler>(_optimize); shared_ptr<Compiler> compiler = make_shared<Compiler>(_optimize);
compiler->compileContract(*contract, m_globalContext->getMagicVariables(), compiler->compileContract(*contract, contractBytecode);
contractBytecode);
Contract& compiledContract = m_contracts[contract->getName()]; Contract& compiledContract = m_contracts[contract->getName()];
compiledContract.bytecode = compiler->getAssembledBytecode(); compiledContract.bytecode = compiler->getAssembledBytecode();
compiledContract.runtimeBytecode = compiler->getRuntimeBytecode(); compiledContract.runtimeBytecode = compiler->getRuntimeBytecode();

5
libsolidity/DeclarationContainer.h

@ -42,11 +42,12 @@ public:
explicit DeclarationContainer(Declaration const* _enclosingDeclaration = nullptr, explicit DeclarationContainer(Declaration const* _enclosingDeclaration = nullptr,
DeclarationContainer const* _enclosingContainer = nullptr): DeclarationContainer const* _enclosingContainer = nullptr):
m_enclosingDeclaration(_enclosingDeclaration), m_enclosingContainer(_enclosingContainer) {} m_enclosingDeclaration(_enclosingDeclaration), m_enclosingContainer(_enclosingContainer) {}
/// Registers the declaration in the scope unless its name is already declared. Returns true iff /// Registers the declaration in the scope unless its name is already declared.
/// it was not yet declared. /// @returns true iff it was not yet declared.
bool registerDeclaration(Declaration const& _declaration, bool _update = false); bool registerDeclaration(Declaration const& _declaration, bool _update = false);
Declaration const* resolveName(ASTString const& _name, bool _recursive = false) const; Declaration const* resolveName(ASTString const& _name, bool _recursive = false) const;
Declaration const* getEnclosingDeclaration() const { return m_enclosingDeclaration; } Declaration const* getEnclosingDeclaration() const { return m_enclosingDeclaration; }
std::map<ASTString, Declaration const*> const& getDeclarations() const { return m_declarations; }
private: private:
Declaration const* m_enclosingDeclaration; Declaration const* m_enclosingDeclaration;

130
libsolidity/ExpressionCompiler.cpp

@ -94,13 +94,8 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation)
m_context << eth::Instruction::NOT; m_context << eth::Instruction::NOT;
break; break;
case Token::DELETE: // delete case Token::DELETE: // delete
// @todo semantics change for complex types
solAssert(m_currentLValue.isValid(), "LValue not retrieved."); solAssert(m_currentLValue.isValid(), "LValue not retrieved.");
m_currentLValue.setToZero(_unaryOperation);
m_context << u256(0);
if (m_currentLValue.storesReferenceOnStack())
m_context << eth::Instruction::SWAP1;
m_currentLValue.storeValue(_unaryOperation);
m_currentLValue.reset(); m_currentLValue.reset();
break; break;
case Token::INC: // ++ (pre- or postfix) case Token::INC: // ++ (pre- or postfix)
@ -397,9 +392,7 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
m_context << eth::Instruction::COINBASE; m_context << eth::Instruction::COINBASE;
else if (member == "timestamp") else if (member == "timestamp")
m_context << eth::Instruction::TIMESTAMP; m_context << eth::Instruction::TIMESTAMP;
/* else if (member == "blockhash") else if (member == "difficulty")
m_context << eth::Instruction::BLOCKHASH;
*/ else if (member == "difficulty")
m_context << eth::Instruction::DIFFICULTY; m_context << eth::Instruction::DIFFICULTY;
else if (member == "number") else if (member == "number")
m_context << eth::Instruction::NUMBER; m_context << eth::Instruction::NUMBER;
@ -426,6 +419,22 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
m_currentLValue.retrieveValueIfLValueNotRequested(_memberAccess); m_currentLValue.retrieveValueIfLValueNotRequested(_memberAccess);
break; break;
} }
case Type::Category::TYPE:
{
TypeType const& type = dynamic_cast<TypeType const&>(*_memberAccess.getExpression().getType());
if (type.getMembers().getMemberType(member))
{
ContractDefinition const& contract = dynamic_cast<ContractType const&>(*type.getActualType())
.getContractDefinition();
for (ASTPointer<FunctionDefinition> const& function: contract.getDefinedFunctions())
if (function->getName() == member)
{
m_context << m_context.getFunctionEntryLabel(*function).pushTag();
return;
}
}
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid member access to " + type.toString()));
}
default: default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Member access to unknown type.")); BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Member access to unknown type."));
} }
@ -456,20 +465,22 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier)
{ {
if (magicVar->getType()->getCategory() == Type::Category::CONTRACT) // must be "this" if (magicVar->getType()->getCategory() == Type::Category::CONTRACT) // must be "this"
m_context << eth::Instruction::ADDRESS; m_context << eth::Instruction::ADDRESS;
return;
}
if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(declaration))
{
m_context << m_context.getFunctionEntryLabel(*functionDef).pushTag();
return;
} }
if (dynamic_cast<VariableDeclaration const*>(declaration)) else if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(declaration))
m_context << m_context.getVirtualFunctionEntryLabel(*functionDef).pushTag();
else if (dynamic_cast<VariableDeclaration const*>(declaration))
{ {
m_currentLValue.fromIdentifier(_identifier, *declaration); m_currentLValue.fromIdentifier(_identifier, *declaration);
m_currentLValue.retrieveValueIfLValueNotRequested(_identifier); m_currentLValue.retrieveValueIfLValueNotRequested(_identifier);
return;
} }
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Identifier type not expected in expression context.")); else if (dynamic_cast<ContractDefinition const*>(declaration))
{
// no-op
}
else
{
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Identifier type not expected in expression context."));
}
} }
void ExpressionCompiler::endVisit(Literal const& _literal) void ExpressionCompiler::endVisit(Literal const& _literal)
@ -753,9 +764,14 @@ unsigned ExpressionCompiler::appendArgumentCopyToMemory(TypePointers const& _typ
ExpressionCompiler::LValue::LValue(CompilerContext& _compilerContext, LValueType _type, Type const& _dataType, ExpressionCompiler::LValue::LValue(CompilerContext& _compilerContext, LValueType _type, Type const& _dataType,
unsigned _baseStackOffset): unsigned _baseStackOffset):
m_context(&_compilerContext), m_type(_type), m_baseStackOffset(_baseStackOffset), m_context(&_compilerContext), m_type(_type), m_baseStackOffset(_baseStackOffset)
m_stackSize(_dataType.getSizeOnStack())
{ {
//@todo change the type cast for arrays
solAssert(_dataType.getStorageSize() <= numeric_limits<unsigned>::max(), "The storage size of " +_dataType.toString() + " should fit in unsigned");
if (m_type == STORAGE)
m_size = unsigned(_dataType.getStorageSize());
else
m_size = unsigned(_dataType.getSizeOnStack());
} }
void ExpressionCompiler::LValue::retrieveValue(Expression const& _expression, bool _remove) const void ExpressionCompiler::LValue::retrieveValue(Expression const& _expression, bool _remove) const
@ -768,7 +784,7 @@ void ExpressionCompiler::LValue::retrieveValue(Expression const& _expression, bo
if (stackPos >= 15) //@todo correct this by fetching earlier or moving to memory if (stackPos >= 15) //@todo correct this by fetching earlier or moving to memory
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation()) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation())
<< errinfo_comment("Stack too deep.")); << errinfo_comment("Stack too deep."));
for (unsigned i = 0; i < m_stackSize; ++i) for (unsigned i = 0; i < m_size; ++i)
*m_context << eth::dupInstruction(stackPos + 1); *m_context << eth::dupInstruction(stackPos + 1);
break; break;
} }
@ -777,14 +793,14 @@ void ExpressionCompiler::LValue::retrieveValue(Expression const& _expression, bo
break; // no distinction between value and reference for non-value types break; // no distinction between value and reference for non-value types
if (!_remove) if (!_remove)
*m_context << eth::Instruction::DUP1; *m_context << eth::Instruction::DUP1;
if (m_stackSize == 1) if (m_size == 1)
*m_context << eth::Instruction::SLOAD; *m_context << eth::Instruction::SLOAD;
else else
for (unsigned i = 0; i < m_stackSize; ++i) for (unsigned i = 0; i < m_size; ++i)
{ {
*m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD << eth::Instruction::SWAP1; *m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD << eth::Instruction::SWAP1;
if (i + 1 < m_stackSize) if (i + 1 < m_size)
*m_context << u256(1) << eth::Instruction::ADD; *m_context << u256(1) << eth::Instruction::ADD;
else else
*m_context << eth::Instruction::POP; *m_context << eth::Instruction::POP;
} }
@ -808,12 +824,12 @@ void ExpressionCompiler::LValue::storeValue(Expression const& _expression, bool
{ {
case STACK: case STACK:
{ {
unsigned stackDiff = m_context->baseToCurrentStackOffset(unsigned(m_baseStackOffset)) - m_stackSize + 1; unsigned stackDiff = m_context->baseToCurrentStackOffset(unsigned(m_baseStackOffset)) - m_size + 1;
if (stackDiff > 16) if (stackDiff > 16)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation()) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation())
<< errinfo_comment("Stack too deep.")); << errinfo_comment("Stack too deep."));
else if (stackDiff > 0) else if (stackDiff > 0)
for (unsigned i = 0; i < m_stackSize; ++i) for (unsigned i = 0; i < m_size; ++i)
*m_context << eth::swapInstruction(stackDiff) << eth::Instruction::POP; *m_context << eth::swapInstruction(stackDiff) << eth::Instruction::POP;
if (!_move) if (!_move)
retrieveValue(_expression); retrieveValue(_expression);
@ -825,17 +841,17 @@ void ExpressionCompiler::LValue::storeValue(Expression const& _expression, bool
// stack layout: value value ... value ref // stack layout: value value ... value ref
if (!_move) // copy values if (!_move) // copy values
{ {
if (m_stackSize + 1 > 16) if (m_size + 1 > 16)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation()) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation())
<< errinfo_comment("Stack too deep.")); << errinfo_comment("Stack too deep."));
for (unsigned i = 0; i < m_stackSize; ++i) for (unsigned i = 0; i < m_size; ++i)
*m_context << eth::dupInstruction(m_stackSize + 1) << eth::Instruction::SWAP1; *m_context << eth::dupInstruction(m_size + 1) << eth::Instruction::SWAP1;
} }
if (m_stackSize > 0) // store high index value first if (m_size > 0) // store high index value first
*m_context << u256(m_stackSize - 1) << eth::Instruction::ADD; *m_context << u256(m_size - 1) << eth::Instruction::ADD;
for (unsigned i = 0; i < m_stackSize; ++i) for (unsigned i = 0; i < m_size; ++i)
{ {
if (i + 1 >= m_stackSize) if (i + 1 >= m_size)
*m_context << eth::Instruction::SSTORE; *m_context << eth::Instruction::SSTORE;
else else
// v v ... v v r+x // v v ... v v r+x
@ -857,6 +873,48 @@ void ExpressionCompiler::LValue::storeValue(Expression const& _expression, bool
} }
} }
void ExpressionCompiler::LValue::setToZero(Expression const& _expression) const
{
switch (m_type)
{
case STACK:
{
unsigned stackDiff = m_context->baseToCurrentStackOffset(unsigned(m_baseStackOffset));
if (stackDiff > 16)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation())
<< errinfo_comment("Stack too deep."));
solAssert(stackDiff >= m_size - 1, "");
for (unsigned i = 0; i < m_size; ++i)
*m_context << u256(0) << eth::swapInstruction(stackDiff + 1 - i)
<< eth::Instruction::POP;
break;
}
case LValue::STORAGE:
if (m_size == 0)
*m_context << eth::Instruction::POP;
for (unsigned i = 0; i < m_size; ++i)
{
if (i + 1 >= m_size)
*m_context << u256(0) << eth::Instruction::SWAP1 << eth::Instruction::SSTORE;
else
*m_context << u256(0) << eth::Instruction::DUP2 << eth::Instruction::SSTORE
<< u256(1) << eth::Instruction::ADD;
}
break;
case LValue::MEMORY:
if (!_expression.getType()->isValueType())
break; // no distinction between value and reference for non-value types
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_expression.getLocation())
<< errinfo_comment("Location type not yet implemented."));
break;
default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_expression.getLocation())
<< errinfo_comment("Unsupported location type."));
break;
}
}
void ExpressionCompiler::LValue::retrieveValueIfLValueNotRequested(Expression const& _expression) void ExpressionCompiler::LValue::retrieveValueIfLValueNotRequested(Expression const& _expression)
{ {
if (!_expression.lvalueRequested()) if (!_expression.lvalueRequested())
@ -868,15 +926,17 @@ void ExpressionCompiler::LValue::retrieveValueIfLValueNotRequested(Expression co
void ExpressionCompiler::LValue::fromIdentifier(Identifier const& _identifier, Declaration const& _declaration) void ExpressionCompiler::LValue::fromIdentifier(Identifier const& _identifier, Declaration const& _declaration)
{ {
m_stackSize = _identifier.getType()->getSizeOnStack();
if (m_context->isLocalVariable(&_declaration)) if (m_context->isLocalVariable(&_declaration))
{ {
m_type = STACK; m_type = STACK;
m_size = _identifier.getType()->getSizeOnStack();
m_baseStackOffset = m_context->getBaseStackOffsetOfVariable(_declaration); m_baseStackOffset = m_context->getBaseStackOffsetOfVariable(_declaration);
} }
else if (m_context->isStateVariable(&_declaration)) else if (m_context->isStateVariable(&_declaration))
{ {
m_type = STORAGE; m_type = STORAGE;
solAssert(_identifier.getType()->getStorageSize() <= numeric_limits<unsigned>::max(), "The storage size of " + _identifier.getType()->toString() + " should fit in unsigned");
m_size = unsigned(_identifier.getType()->getStorageSize());
*m_context << m_context->getStorageLocationOfVariable(_declaration); *m_context << m_context->getStorageLocationOfVariable(_declaration);
} }
else else

10
libsolidity/ExpressionCompiler.h

@ -111,7 +111,7 @@ private:
/// Set type according to the declaration and retrieve the reference. /// Set type according to the declaration and retrieve the reference.
/// @a _expression is the current expression /// @a _expression is the current expression
void fromIdentifier(Identifier const& _identifier, Declaration const& _declaration); void fromIdentifier(Identifier const& _identifier, Declaration const& _declaration);
void reset() { m_type = NONE; m_baseStackOffset = 0; } void reset() { m_type = NONE; m_baseStackOffset = 0; m_size = 0; }
bool isValid() const { return m_type != NONE; } bool isValid() const { return m_type != NONE; }
bool isInOnStack() const { return m_type == STACK; } bool isInOnStack() const { return m_type == STACK; }
@ -130,7 +130,9 @@ private:
/// Also removes the stored value from the stack if @a _move is /// Also removes the stored value from the stack if @a _move is
/// true. @a _expression is the current expression, used for error reporting. /// true. @a _expression is the current expression, used for error reporting.
void storeValue(Expression const& _expression, bool _move = false) const; void storeValue(Expression const& _expression, bool _move = false) const;
/// Stores zero in the lvalue.
/// @a _expression is the current expression, used for error reporting.
void setToZero(Expression const& _expression) const;
/// Convenience function to convert the stored reference to a value and reset type to NONE if /// Convenience function to convert the stored reference to a value and reset type to NONE if
/// the reference was not requested by @a _expression. /// the reference was not requested by @a _expression.
void retrieveValueIfLValueNotRequested(Expression const& _expression); void retrieveValueIfLValueNotRequested(Expression const& _expression);
@ -141,8 +143,8 @@ private:
/// If m_type is STACK, this is base stack offset (@see /// If m_type is STACK, this is base stack offset (@see
/// CompilerContext::getBaseStackOffsetOfVariable) of a local variable. /// CompilerContext::getBaseStackOffsetOfVariable) of a local variable.
unsigned m_baseStackOffset = 0; unsigned m_baseStackOffset = 0;
/// Size of the value of this lvalue on the stack. /// Size of the value of this lvalue on the stack or the storage.
unsigned m_stackSize = 0; unsigned m_size = 0;
}; };
bool m_optimize; bool m_optimize;

12
libsolidity/GlobalContext.cpp

@ -68,7 +68,7 @@ void GlobalContext::setCurrentContract(ContractDefinition const& _contract)
vector<Declaration const*> GlobalContext::getDeclarations() const vector<Declaration const*> GlobalContext::getDeclarations() const
{ {
vector<Declaration const*> declarations; vector<Declaration const*> declarations;
declarations.reserve(m_magicVariables.size() + 1); declarations.reserve(m_magicVariables.size());
for (ASTPointer<Declaration const> const& variable: m_magicVariables) for (ASTPointer<Declaration const> const& variable: m_magicVariables)
declarations.push_back(variable.get()); declarations.push_back(variable.get());
return declarations; return declarations;
@ -83,15 +83,5 @@ MagicVariableDeclaration const* GlobalContext::getCurrentThis() const
} }
vector<MagicVariableDeclaration const*> GlobalContext::getMagicVariables() const
{
vector<MagicVariableDeclaration const*> declarations;
declarations.reserve(m_magicVariables.size() + 1);
for (ASTPointer<MagicVariableDeclaration const> const& variable: m_magicVariables)
declarations.push_back(variable.get());
declarations.push_back(getCurrentThis());
return declarations;
}
} }
} }

2
libsolidity/GlobalContext.h

@ -49,8 +49,6 @@ public:
void setCurrentContract(ContractDefinition const& _contract); void setCurrentContract(ContractDefinition const& _contract);
MagicVariableDeclaration const* getCurrentThis() const; MagicVariableDeclaration const* getCurrentThis() const;
/// @returns all magic variables.
std::vector<MagicVariableDeclaration const*> getMagicVariables() const;
/// @returns a vector of all implicit global declarations excluding "this". /// @returns a vector of all implicit global declarations excluding "this".
std::vector<Declaration const*> getDeclarations() const; std::vector<Declaration const*> getDeclarations() const;

10
libsolidity/InterfaceHandler.cpp

@ -351,9 +351,15 @@ void InterfaceHandler::parseDocString(std::string const& _string, CommentOwner _
currPos = appendDocTag(currPos, end, _owner); currPos = appendDocTag(currPos, end, _owner);
else if (currPos != end) else if (currPos != end)
{ {
if (nlPos == end) //end of text // if it begins without a tag then consider it as @notice
if (currPos == _string.begin())
{
currPos = parseDocTag(currPos, end, "notice", CommentOwner::FUNCTION);
continue;
}
else if (nlPos == end) //end of text
return; return;
// else skip the line if a newline was found // else skip the line if a newline was found and we get here
currPos = nlPos + 1; currPos = nlPos + 1;
} }
} }

116
libsolidity/NameAndTypeResolver.cpp

@ -31,7 +31,6 @@ namespace dev
namespace solidity namespace solidity
{ {
NameAndTypeResolver::NameAndTypeResolver(std::vector<Declaration const*> const& _globals) NameAndTypeResolver::NameAndTypeResolver(std::vector<Declaration const*> const& _globals)
{ {
for (Declaration const* declaration: _globals) for (Declaration const* declaration: _globals)
@ -46,18 +45,27 @@ void NameAndTypeResolver::registerDeclarations(SourceUnit& _sourceUnit)
void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract) void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract)
{ {
m_currentScope = &m_scopes[nullptr];
for (ASTPointer<InheritanceSpecifier> const& baseContract: _contract.getBaseContracts())
ReferencesResolver resolver(*baseContract, *this, &_contract, nullptr);
m_currentScope = &m_scopes[&_contract]; m_currentScope = &m_scopes[&_contract];
linearizeBaseContracts(_contract);
for (ContractDefinition const* base: _contract.getLinearizedBaseContracts())
importInheritedScope(*base);
for (ASTPointer<StructDefinition> const& structDef: _contract.getDefinedStructs()) for (ASTPointer<StructDefinition> const& structDef: _contract.getDefinedStructs())
ReferencesResolver resolver(*structDef, *this, nullptr); ReferencesResolver resolver(*structDef, *this, &_contract, nullptr);
for (ASTPointer<VariableDeclaration> const& variable: _contract.getStateVariables()) for (ASTPointer<VariableDeclaration> const& variable: _contract.getStateVariables())
ReferencesResolver resolver(*variable, *this, nullptr); ReferencesResolver resolver(*variable, *this, &_contract, nullptr);
for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions()) for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions())
{ {
m_currentScope = &m_scopes[function.get()]; m_currentScope = &m_scopes[function.get()];
ReferencesResolver referencesResolver(*function, *this, ReferencesResolver referencesResolver(*function, *this, &_contract,
function->getReturnParameterList().get()); function->getReturnParameterList().get());
} }
m_currentScope = &m_scopes[nullptr];
} }
void NameAndTypeResolver::checkTypeRequirements(ContractDefinition& _contract) void NameAndTypeResolver::checkTypeRequirements(ContractDefinition& _contract)
@ -86,6 +94,96 @@ Declaration const* NameAndTypeResolver::getNameFromCurrentScope(ASTString const&
return m_currentScope->resolveName(_name, _recursive); return m_currentScope->resolveName(_name, _recursive);
} }
void NameAndTypeResolver::importInheritedScope(ContractDefinition const& _base)
{
auto iterator = m_scopes.find(&_base);
solAssert(iterator != end(m_scopes), "");
for (auto const& nameAndDeclaration: iterator->second.getDeclarations())
{
Declaration const* declaration = nameAndDeclaration.second;
// Import if it was declared in the base and is not the constructor
if (declaration->getScope() == &_base && declaration->getName() != _base.getName())
m_currentScope->registerDeclaration(*declaration);
}
}
void NameAndTypeResolver::linearizeBaseContracts(ContractDefinition& _contract) const
{
// order in the lists is from derived to base
// list of lists to linearize, the last element is the list of direct bases
list<list<ContractDefinition const*>> input(1, {&_contract});
for (ASTPointer<InheritanceSpecifier> const& baseSpecifier: _contract.getBaseContracts())
{
ASTPointer<Identifier> baseName = baseSpecifier->getName();
ContractDefinition const* base = dynamic_cast<ContractDefinition const*>(
baseName->getReferencedDeclaration());
if (!base)
BOOST_THROW_EXCEPTION(baseName->createTypeError("Contract expected."));
// "push_back" has the effect that bases mentioned earlier can overwrite members of bases
// mentioned later
input.back().push_back(base);
vector<ContractDefinition const*> const& basesBases = base->getLinearizedBaseContracts();
if (basesBases.empty())
BOOST_THROW_EXCEPTION(baseName->createTypeError("Definition of base has to precede definition of derived contract"));
input.push_front(list<ContractDefinition const*>(basesBases.begin(), basesBases.end()));
}
vector<ContractDefinition const*> result = cThreeMerge(input);
if (result.empty())
BOOST_THROW_EXCEPTION(_contract.createTypeError("Linearization of inheritance graph impossible"));
_contract.setLinearizedBaseContracts(result);
}
template <class _T>
vector<_T const*> NameAndTypeResolver::cThreeMerge(list<list<_T const*>>& _toMerge)
{
// returns true iff _candidate appears only as last element of the lists
auto appearsOnlyAtHead = [&](_T const* _candidate) -> bool
{
for (list<_T const*> const& bases: _toMerge)
{
solAssert(!bases.empty(), "");
if (find(++bases.begin(), bases.end(), _candidate) != bases.end())
return false;
}
return true;
};
// returns the next candidate to append to the linearized list or nullptr on failure
auto nextCandidate = [&]() -> _T const*
{
for (list<_T const*> const& bases: _toMerge)
{
solAssert(!bases.empty(), "");
if (appearsOnlyAtHead(bases.front()))
return bases.front();
}
return nullptr;
};
// removes the given contract from all lists
auto removeCandidate = [&](_T const* _candidate)
{
for (auto it = _toMerge.begin(); it != _toMerge.end();)
{
it->remove(_candidate);
if (it->empty())
it = _toMerge.erase(it);
else
++it;
}
};
_toMerge.remove_if([](list<_T const*> const& _bases) { return _bases.empty(); });
vector<_T const*> result;
while (!_toMerge.empty())
{
_T const* candidate = nextCandidate();
if (!candidate)
return vector<_T const*>();
result.push_back(candidate);
removeCandidate(candidate);
}
return result;
}
DeclarationRegistrationHelper::DeclarationRegistrationHelper(map<ASTNode const*, DeclarationContainer>& _scopes, DeclarationRegistrationHelper::DeclarationRegistrationHelper(map<ASTNode const*, DeclarationContainer>& _scopes,
ASTNode& _astRoot): ASTNode& _astRoot):
m_scopes(_scopes), m_currentScope(nullptr) m_scopes(_scopes), m_currentScope(nullptr)
@ -169,8 +267,10 @@ void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaratio
} }
ReferencesResolver::ReferencesResolver(ASTNode& _root, NameAndTypeResolver& _resolver, ReferencesResolver::ReferencesResolver(ASTNode& _root, NameAndTypeResolver& _resolver,
ParameterList* _returnParameters, bool _allowLazyTypes): ContractDefinition const* _currentContract,
m_resolver(_resolver), m_returnParameters(_returnParameters), m_allowLazyTypes(_allowLazyTypes) ParameterList const* _returnParameters, bool _allowLazyTypes):
m_resolver(_resolver), m_currentContract(_currentContract),
m_returnParameters(_returnParameters), m_allowLazyTypes(_allowLazyTypes)
{ {
_root.accept(*this); _root.accept(*this);
} }
@ -218,7 +318,7 @@ bool ReferencesResolver::visit(Identifier& _identifier)
if (!declaration) if (!declaration)
BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(_identifier.getLocation()) BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(_identifier.getLocation())
<< errinfo_comment("Undeclared identifier.")); << errinfo_comment("Undeclared identifier."));
_identifier.setReferencedDeclaration(*declaration); _identifier.setReferencedDeclaration(*declaration, m_currentContract);
return false; return false;
} }

19
libsolidity/NameAndTypeResolver.h

@ -23,6 +23,7 @@
#pragma once #pragma once
#include <map> #include <map>
#include <list>
#include <boost/noncopyable.hpp> #include <boost/noncopyable.hpp>
#include <libsolidity/DeclarationContainer.h> #include <libsolidity/DeclarationContainer.h>
@ -64,6 +65,17 @@ public:
private: private:
void reset(); void reset();
/// Imports all members declared directly in the given contract (i.e. does not import inherited
/// members) into the current scope if they are not present already.
void importInheritedScope(ContractDefinition const& _base);
/// Computes "C3-Linearization" of base contracts and stores it inside the contract.
void linearizeBaseContracts(ContractDefinition& _contract) const;
/// Computes the C3-merge of the given list of lists of bases.
/// @returns the linearized vector or an empty vector if linearization is not possible.
template <class _T>
static std::vector<_T const*> cThreeMerge(std::list<std::list<_T const*>>& _toMerge);
/// Maps nodes declaring a scope to scopes, i.e. ContractDefinition and FunctionDeclaration, /// Maps nodes declaring a scope to scopes, i.e. ContractDefinition and FunctionDeclaration,
/// where nullptr denotes the global scope. Note that structs are not scope since they do /// where nullptr denotes the global scope. Note that structs are not scope since they do
/// not contain code. /// not contain code.
@ -108,7 +120,9 @@ class ReferencesResolver: private ASTVisitor
{ {
public: public:
ReferencesResolver(ASTNode& _root, NameAndTypeResolver& _resolver, ReferencesResolver(ASTNode& _root, NameAndTypeResolver& _resolver,
ParameterList* _returnParameters, bool _allowLazyTypes = true); ContractDefinition const* _currentContract,
ParameterList const* _returnParameters,
bool _allowLazyTypes = true);
private: private:
virtual void endVisit(VariableDeclaration& _variable) override; virtual void endVisit(VariableDeclaration& _variable) override;
@ -118,7 +132,8 @@ private:
virtual bool visit(Return& _return) override; virtual bool visit(Return& _return) override;
NameAndTypeResolver& m_resolver; NameAndTypeResolver& m_resolver;
ParameterList* m_returnParameters; ContractDefinition const* m_currentContract;
ParameterList const* m_returnParameters;
bool m_allowLazyTypes; bool m_allowLazyTypes;
}; };

30
libsolidity/Parser.cpp

@ -117,10 +117,18 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition()
docstring = make_shared<ASTString>(m_scanner->getCurrentCommentLiteral()); docstring = make_shared<ASTString>(m_scanner->getCurrentCommentLiteral());
expectToken(Token::CONTRACT); expectToken(Token::CONTRACT);
ASTPointer<ASTString> name = expectIdentifierToken(); ASTPointer<ASTString> name = expectIdentifierToken();
expectToken(Token::LBRACE); vector<ASTPointer<InheritanceSpecifier>> baseContracts;
vector<ASTPointer<StructDefinition>> structs; vector<ASTPointer<StructDefinition>> structs;
vector<ASTPointer<VariableDeclaration>> stateVariables; vector<ASTPointer<VariableDeclaration>> stateVariables;
vector<ASTPointer<FunctionDefinition>> functions; vector<ASTPointer<FunctionDefinition>> functions;
if (m_scanner->getCurrentToken() == Token::IS)
do
{
m_scanner->next();
baseContracts.push_back(parseInheritanceSpecifier());
}
while (m_scanner->getCurrentToken() == Token::COMMA);
expectToken(Token::LBRACE);
bool visibilityIsPublic = true; bool visibilityIsPublic = true;
while (true) while (true)
{ {
@ -149,7 +157,25 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition()
} }
nodeFactory.markEndPosition(); nodeFactory.markEndPosition();
expectToken(Token::RBRACE); expectToken(Token::RBRACE);
return nodeFactory.createNode<ContractDefinition>(name, docstring, structs, stateVariables, functions); return nodeFactory.createNode<ContractDefinition>(name, docstring, baseContracts, structs,
stateVariables, functions);
}
ASTPointer<InheritanceSpecifier> Parser::parseInheritanceSpecifier()
{
ASTNodeFactory nodeFactory(*this);
ASTPointer<Identifier> name = ASTNodeFactory(*this).createNode<Identifier>(expectIdentifierToken());
vector<ASTPointer<Expression>> arguments;
if (m_scanner->getCurrentToken() == Token::LPAREN)
{
m_scanner->next();
arguments = parseFunctionCallArguments();
nodeFactory.markEndPosition();
expectToken(Token::RPAREN);
}
else
nodeFactory.setEndPositionFromNode(name);
return nodeFactory.createNode<InheritanceSpecifier>(name, arguments);
} }
ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(bool _isPublic) ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(bool _isPublic)

1
libsolidity/Parser.h

@ -49,6 +49,7 @@ private:
///@name Parsing functions for the AST nodes ///@name Parsing functions for the AST nodes
ASTPointer<ImportDirective> parseImportDirective(); ASTPointer<ImportDirective> parseImportDirective();
ASTPointer<ContractDefinition> parseContractDefinition(); ASTPointer<ContractDefinition> parseContractDefinition();
ASTPointer<InheritanceSpecifier> parseInheritanceSpecifier();
ASTPointer<FunctionDefinition> parseFunctionDefinition(bool _isPublic); ASTPointer<FunctionDefinition> parseFunctionDefinition(bool _isPublic);
ASTPointer<StructDefinition> parseStructDefinition(); ASTPointer<StructDefinition> parseStructDefinition();
ASTPointer<VariableDeclaration> parseVariableDeclaration(bool _allowVar); ASTPointer<VariableDeclaration> parseVariableDeclaration(bool _allowVar);

2
libsolidity/Token.h

@ -153,7 +153,7 @@ namespace solidity
K(DEFAULT, "default", 0) \ K(DEFAULT, "default", 0) \
K(DO, "do", 0) \ K(DO, "do", 0) \
K(ELSE, "else", 0) \ K(ELSE, "else", 0) \
K(EXTENDS, "extends", 0) \ K(IS, "is", 0) \
K(FOR, "for", 0) \ K(FOR, "for", 0) \
K(FUNCTION, "function", 0) \ K(FUNCTION, "function", 0) \
K(IF, "if", 0) \ K(IF, "if", 0) \

59
libsolidity/Types.cpp

@ -147,7 +147,7 @@ TypePointer IntegerType::unaryOperatorResult(Token::Value _operator) const
{ {
// "delete" is ok for all integer types // "delete" is ok for all integer types
if (_operator == Token::DELETE) if (_operator == Token::DELETE)
return shared_from_this(); return make_shared<VoidType>();
// no further unary operators for addresses // no further unary operators for addresses
else if (isAddress()) else if (isAddress())
return TypePointer(); return TypePointer();
@ -408,6 +408,13 @@ u256 BoolType::literalValue(Literal const* _literal) const
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Bool type constructed from non-boolean literal.")); BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Bool type constructed from non-boolean literal."));
} }
TypePointer BoolType::unaryOperatorResult(Token::Value _operator) const
{
if (_operator == Token::DELETE)
return make_shared<VoidType>();
return (_operator == Token::NOT) ? shared_from_this() : TypePointer();
}
TypePointer BoolType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const TypePointer BoolType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const
{ {
if (getCategory() != _other->getCategory()) if (getCategory() != _other->getCategory())
@ -424,12 +431,24 @@ bool ContractType::isImplicitlyConvertibleTo(Type const& _convertTo) const
return true; return true;
if (_convertTo.getCategory() == Category::INTEGER) if (_convertTo.getCategory() == Category::INTEGER)
return dynamic_cast<IntegerType const&>(_convertTo).isAddress(); return dynamic_cast<IntegerType const&>(_convertTo).isAddress();
if (_convertTo.getCategory() == Category::CONTRACT)
{
auto const& bases = getContractDefinition().getLinearizedBaseContracts();
return find(bases.begin(), bases.end(),
&dynamic_cast<ContractType const&>(_convertTo).getContractDefinition()) != bases.end();
}
return false; return false;
} }
bool ContractType::isExplicitlyConvertibleTo(Type const& _convertTo) const bool ContractType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{ {
return isImplicitlyConvertibleTo(_convertTo) || _convertTo.getCategory() == Category::INTEGER; return isImplicitlyConvertibleTo(_convertTo) || _convertTo.getCategory() == Category::INTEGER ||
_convertTo.getCategory() == Category::CONTRACT;
}
TypePointer ContractType::unaryOperatorResult(Token::Value _operator) const
{
return _operator == Token::DELETE ? make_shared<VoidType>() : TypePointer();
} }
bool ContractType::operator==(Type const& _other) const bool ContractType::operator==(Type const& _other) const
@ -440,14 +459,6 @@ bool ContractType::operator==(Type const& _other) const
return other.m_contract == m_contract; return other.m_contract == m_contract;
} }
u256 ContractType::getStorageSize() const
{
u256 size = 0;
for (ASTPointer<VariableDeclaration> const& variable: m_contract.getStateVariables())
size += variable->getType()->getStorageSize();
return max<u256>(1, size);
}
string ContractType::toString() const string ContractType::toString() const
{ {
return "contract " + m_contract.getName(); return "contract " + m_contract.getName();
@ -491,6 +502,11 @@ u256 ContractType::getFunctionIdentifier(string const& _functionName) const
return Invalid256; return Invalid256;
} }
TypePointer StructType::unaryOperatorResult(Token::Value _operator) const
{
return _operator == Token::DELETE ? make_shared<VoidType>() : TypePointer();
}
bool StructType::operator==(Type const& _other) const bool StructType::operator==(Type const& _other) const
{ {
if (_other.getCategory() != getCategory()) if (_other.getCategory() != getCategory())
@ -686,6 +702,29 @@ bool TypeType::operator==(Type const& _other) const
return *getActualType() == *other.getActualType(); return *getActualType() == *other.getActualType();
} }
MemberList const& TypeType::getMembers() const
{
// We need to lazy-initialize it because of recursive references.
if (!m_members)
{
map<string, TypePointer> members;
if (m_actualType->getCategory() == Category::CONTRACT && m_currentContract != nullptr)
{
ContractDefinition const& contract = dynamic_cast<ContractType const&>(*m_actualType).getContractDefinition();
vector<ContractDefinition const*> currentBases = m_currentContract->getLinearizedBaseContracts();
if (find(currentBases.begin(), currentBases.end(), &contract) != currentBases.end())
// We are accessing the type of a base contract, so add all public and private
// functions. Note that this does not add inherited functions on purpose.
for (ASTPointer<FunctionDefinition> const& f: contract.getDefinedFunctions())
if (f->getName() != contract.getName())
members[f->getName()] = make_shared<FunctionType>(*f);
}
m_members.reset(new MemberList(members));
}
return *m_members;
}
MagicType::MagicType(MagicType::Kind _kind): MagicType::MagicType(MagicType::Kind _kind):
m_kind(_kind) m_kind(_kind)
{ {

21
libsolidity/Types.h

@ -259,10 +259,7 @@ public:
BoolType() {} BoolType() {}
virtual Category getCategory() const { return Category::BOOL; } virtual Category getCategory() const { return Category::BOOL; }
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
{
return (_operator == Token::NOT || _operator == Token::DELETE) ? shared_from_this() : TypePointer();
}
virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override; virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override;
virtual unsigned getCalldataEncodedSize() const { return 1; } virtual unsigned getCalldataEncodedSize() const { return 1; }
@ -284,8 +281,8 @@ public:
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
/// Contracts can be converted to themselves and to integers. /// Contracts can be converted to themselves and to integers.
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
virtual bool operator==(Type const& _other) const override; virtual bool operator==(Type const& _other) const override;
virtual u256 getStorageSize() const override;
virtual bool isValueType() const override { return true; } virtual bool isValueType() const override { return true; }
virtual std::string toString() const override; virtual std::string toString() const override;
@ -317,11 +314,7 @@ class StructType: public Type
public: public:
virtual Category getCategory() const override { return Category::STRUCT; } virtual Category getCategory() const override { return Category::STRUCT; }
StructType(StructDefinition const& _struct): m_struct(_struct) {} StructType(StructDefinition const& _struct): m_struct(_struct) {}
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
{
return _operator == Token::DELETE ? shared_from_this() : TypePointer();
}
virtual bool operator==(Type const& _other) const override; virtual bool operator==(Type const& _other) const override;
virtual u256 getStorageSize() const override; virtual u256 getStorageSize() const override;
virtual bool canLiveOutsideStorage() const override; virtual bool canLiveOutsideStorage() const override;
@ -449,7 +442,8 @@ class TypeType: public Type
{ {
public: public:
virtual Category getCategory() const override { return Category::TYPE; } virtual Category getCategory() const override { return Category::TYPE; }
TypeType(TypePointer const& _actualType): m_actualType(_actualType) {} TypeType(TypePointer const& _actualType, ContractDefinition const* _currentContract = nullptr):
m_actualType(_actualType), m_currentContract(_currentContract) {}
TypePointer const& getActualType() const { return m_actualType; } TypePointer const& getActualType() const { return m_actualType; }
virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); } virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); }
@ -458,9 +452,14 @@ public:
virtual u256 getStorageSize() const override { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable type type requested.")); } virtual u256 getStorageSize() const override { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable type type requested.")); }
virtual bool canLiveOutsideStorage() const override { return false; } virtual bool canLiveOutsideStorage() const override { return false; }
virtual std::string toString() const override { return "type(" + m_actualType->toString() + ")"; } virtual std::string toString() const override { return "type(" + m_actualType->toString() + ")"; }
virtual MemberList const& getMembers() const override;
private: private:
TypePointer m_actualType; TypePointer m_actualType;
/// Context in which this type is used (influences visibility etc.), can be nullptr.
ContractDefinition const* m_currentContract;
/// List of member types, will be lazy-initialized because of recursive references.
mutable std::unique_ptr<MemberList> m_members;
}; };

5
libsolidity/grammar.txt

@ -1,7 +1,10 @@
ContractDefinition = 'contract' Identifier '{' ContractPart* '}' ContractDefinition = 'contract' Identifier
( 'is' InheritanceSpecifier (',' InheritanceSpecifier )* )?
'{' ContractPart* '}'
ContractPart = VariableDeclaration ';' | StructDefinition | ContractPart = VariableDeclaration ';' | StructDefinition |
FunctionDefinition | 'public:' | 'private:' FunctionDefinition | 'public:' | 'private:'
InheritanceSpecifier = Identifier ( '(' Expression ( ',' Expression )* ')' )?
StructDefinition = 'struct' Identifier '{' StructDefinition = 'struct' Identifier '{'
( VariableDeclaration (';' VariableDeclaration)* )? '} ( VariableDeclaration (';' VariableDeclaration)* )? '}

32
mix/AppContext.cpp

@ -22,7 +22,6 @@
* - KeyEventManager * - KeyEventManager
*/ */
#include <QDebug>
#include <QMessageBox> #include <QMessageBox>
#include <QQmlComponent> #include <QQmlComponent>
#include <QQmlContext> #include <QQmlContext>
@ -30,7 +29,10 @@
#include "CodeModel.h" #include "CodeModel.h"
#include "FileIo.h" #include "FileIo.h"
#include "ClientModel.h" #include "ClientModel.h"
#include "CodeEditorExtensionManager.h"
#include "Exceptions.h"
#include "AppContext.h" #include "AppContext.h"
#include "QEther.h"
#include <libwebthree/WebThree.h> #include <libwebthree/WebThree.h>
using namespace dev; using namespace dev;
@ -46,18 +48,44 @@ AppContext::AppContext(QQmlApplicationEngine* _engine)
m_codeModel.reset(new CodeModel(this)); m_codeModel.reset(new CodeModel(this));
m_clientModel.reset(new ClientModel(this)); m_clientModel.reset(new ClientModel(this));
m_fileIo.reset(new FileIo()); m_fileIo.reset(new FileIo());
/*
m_applicationEngine->rootContext()->setContextProperty("appContext", this); m_applicationEngine->rootContext()->setContextProperty("appContext", this);
qmlRegisterType<FileIo>("org.ethereum.qml", 1, 0, "FileIo"); qmlRegisterType<FileIo>("org.ethereum.qml", 1, 0, "FileIo");
qmlRegisterSingletonType(QUrl("qrc:/qml/ProjectModel.qml"), "org.ethereum.qml.ProjectModel", 1, 0, "ProjectModel"); qmlRegisterSingletonType(QUrl("qrc:/qml/ProjectModel.qml"), "org.ethereum.qml.ProjectModel", 1, 0, "ProjectModel");
qmlRegisterType<QEther>("org.ethereum.qml.QEther", 1, 0, "QEther");
qmlRegisterType<QBigInt>("org.ethereum.qml.QBigInt", 1, 0, "QBigInt");
m_applicationEngine->rootContext()->setContextProperty("codeModel", m_codeModel.get()); m_applicationEngine->rootContext()->setContextProperty("codeModel", m_codeModel.get());
m_applicationEngine->rootContext()->setContextProperty("fileIo", m_fileIo.get()); m_applicationEngine->rootContext()->setContextProperty("fileIo", m_fileIo.get());
*/
} }
AppContext::~AppContext() AppContext::~AppContext()
{ {
} }
void AppContext::load()
{
m_applicationEngine->rootContext()->setContextProperty("appContext", this);
qmlRegisterType<FileIo>("org.ethereum.qml", 1, 0, "FileIo");
m_applicationEngine->rootContext()->setContextProperty("codeModel", m_codeModel.get());
m_applicationEngine->rootContext()->setContextProperty("fileIo", m_fileIo.get());
qmlRegisterType<QEther>("org.ethereum.qml.QEther", 1, 0, "QEther");
qmlRegisterType<QBigInt>("org.ethereum.qml.QBigInt", 1, 0, "QBigInt");
QQmlComponent projectModelComponent(m_applicationEngine, QUrl("qrc:/qml/ProjectModel.qml"));
QObject* projectModel = projectModelComponent.create();
if (projectModelComponent.isError())
{
QmlLoadException exception;
for (auto const& e : projectModelComponent.errors())
exception << QmlErrorInfo(e);
BOOST_THROW_EXCEPTION(exception);
}
m_applicationEngine->rootContext()->setContextProperty("projectModel", projectModel);
qmlRegisterType<CodeEditorExtensionManager>("CodeEditorExtensionManager", 1, 0, "CodeEditorExtensionManager");
m_applicationEngine->load(QUrl("qrc:/qml/main.qml"));
appLoaded();
}
QQmlApplicationEngine* AppContext::appEngine() QQmlApplicationEngine* AppContext::appEngine()
{ {
return m_applicationEngine; return m_applicationEngine;

2
mix/AppContext.h

@ -60,6 +60,8 @@ class AppContext: public QObject
public: public:
AppContext(QQmlApplicationEngine* _engine); AppContext(QQmlApplicationEngine* _engine);
virtual ~AppContext(); virtual ~AppContext();
/// Load the UI from qml files
void load();
/// Get the current QQMLApplicationEngine instance. /// Get the current QQMLApplicationEngine instance.
QQmlApplicationEngine* appEngine(); QQmlApplicationEngine* appEngine();
/// Get code model /// Get code model

27
mix/ClientModel.cpp

@ -32,28 +32,17 @@
#include "ContractCallDataEncoder.h" #include "ContractCallDataEncoder.h"
#include "CodeModel.h" #include "CodeModel.h"
#include "ClientModel.h" #include "ClientModel.h"
#include "QEther.h"
using namespace dev; using namespace dev;
using namespace dev::eth; using namespace dev::eth;
using namespace dev::mix; using namespace dev::mix;
/// @todo Move this to QML
dev::u256 fromQString(QString const& _s)
{
return dev::jsToU256(_s.toStdString());
}
/// @todo Move this to QML
QString toQString(dev::u256 _value)
{
std::ostringstream s;
s << _value;
return QString::fromStdString(s.str());
}
ClientModel::ClientModel(AppContext* _context): ClientModel::ClientModel(AppContext* _context):
m_context(_context), m_running(false) m_context(_context), m_running(false)
{ {
qRegisterMetaType<QBigInt*>("QBigInt*");
qRegisterMetaType<QEther*>("QEther*");
qRegisterMetaType<QVariableDefinition*>("QVariableDefinition*"); qRegisterMetaType<QVariableDefinition*>("QVariableDefinition*");
qRegisterMetaType<QVariableDefinitionList*>("QVariableDefinitionList*"); qRegisterMetaType<QVariableDefinitionList*>("QVariableDefinitionList*");
qRegisterMetaType<QList<QVariableDefinition*>>("QList<QVariableDefinition*>"); qRegisterMetaType<QList<QVariableDefinition*>>("QList<QVariableDefinition*>");
@ -74,7 +63,7 @@ void ClientModel::debugDeployment()
void ClientModel::debugState(QVariantMap _state) void ClientModel::debugState(QVariantMap _state)
{ {
u256 balance = fromQString(_state.value("balance").toString()); u256 balance = (qvariant_cast<QEther*>(_state.value("balance")))->toU256Wei();
QVariantList transactions = _state.value("transactions").toList(); QVariantList transactions = _state.value("transactions").toList();
std::vector<TransactionSettings> transactionSequence; std::vector<TransactionSettings> transactionSequence;
@ -84,14 +73,14 @@ void ClientModel::debugState(QVariantMap _state)
QVariantMap transaction = t.toMap(); QVariantMap transaction = t.toMap();
QString functionId = transaction.value("functionId").toString(); QString functionId = transaction.value("functionId").toString();
u256 value = fromQString(transaction.value("value").toString()); u256 gas = (qvariant_cast<QEther*>(transaction.value("gas")))->toU256Wei();
u256 gas = fromQString(transaction.value("gas").toString()); u256 value = (qvariant_cast<QEther*>(transaction.value("value")))->toU256Wei();
u256 gasPrice = fromQString(transaction.value("gasPrice").toString()); u256 gasPrice = (qvariant_cast<QEther*>(transaction.value("gasPrice")))->toU256Wei();
QVariantMap params = transaction.value("parameters").toMap(); QVariantMap params = transaction.value("parameters").toMap();
TransactionSettings transactionSettings(functionId, value, gas, gasPrice); TransactionSettings transactionSettings(functionId, value, gas, gasPrice);
for (auto p = params.cbegin(); p != params.cend(); ++p) for (auto p = params.cbegin(); p != params.cend(); ++p)
transactionSettings.parameterValues.insert(std::make_pair(p.key(), fromQString(p.value().toString()))); transactionSettings.parameterValues.insert(std::make_pair(p.key(), (qvariant_cast<QEther*>(p.value()))->toU256Wei()));
transactionSequence.push_back(transactionSettings); transactionSequence.push_back(transactionSettings);
} }

22
mix/ClientModel.h

@ -1,18 +1,18 @@
/* /*
This file is part of cpp-ethereum. This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful, cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** @file ClientModel.h /** @file ClientModel.h
* @author Yann yann@ethdev.com * @author Yann yann@ethdev.com

20
mix/DebuggingStateWrapper.cpp

@ -23,12 +23,14 @@
#include <QApplication> #include <QApplication>
#include <QDebug> #include <QDebug>
#include <QPointer> #include <QPointer>
#include <QQmlEngine>
#include <libevmcore/Instruction.h> #include <libevmcore/Instruction.h>
#include <libdevcore/CommonJS.h> #include <libdevcore/CommonJS.h>
#include <libdevcrypto/Common.h> #include <libdevcrypto/Common.h>
#include <libevmcore/Instruction.h> #include <libevmcore/Instruction.h>
#include <libdevcore/Common.h> #include <libdevcore/Common.h>
#include "DebuggingStateWrapper.h" #include "DebuggingStateWrapper.h"
#include "QBigInt.h"
using namespace dev; using namespace dev;
using namespace dev::eth; using namespace dev::eth;
using namespace dev::mix; using namespace dev::mix;
@ -66,25 +68,19 @@ std::tuple<QList<QObject*>, QQMLMap*> DebuggingStateWrapper::getHumanReadableCod
return std::make_tuple(codeStr, QPointer<QQMLMap>(new QQMLMap(codeMapping))); return std::make_tuple(codeStr, QPointer<QQMLMap>(new QQMLMap(codeMapping)));
} }
QString DebuggingStateWrapper::gasCost() QBigInt* DebuggingStateWrapper::gasCost()
{ {
std::ostringstream ss; return new QBigInt(m_state.gasCost);
ss << std::dec << m_state.gasCost;
return QString::fromStdString(ss.str());
} }
QString DebuggingStateWrapper::gas() QBigInt* DebuggingStateWrapper::gas()
{ {
std::ostringstream ss; return new QBigInt(m_state.gas);
ss << std::dec << m_state.gas;
return QString::fromStdString(ss.str());
} }
QString DebuggingStateWrapper::newMemSize() QBigInt* DebuggingStateWrapper::newMemSize()
{ {
std::ostringstream ss; return new QBigInt(m_state.newMemSize);
ss << std::dec << m_state.newMemSize;
return QString::fromStdString(ss.str());
} }
QStringList DebuggingStateWrapper::debugStack() QStringList DebuggingStateWrapper::debugStack()

15
mix/DebuggingStateWrapper.h

@ -29,6 +29,7 @@
#include <libethereum/Executive.h> #include <libethereum/Executive.h>
#include "QVariableDefinition.h" #include "QVariableDefinition.h"
#include "MixClient.h" #include "MixClient.h"
#include "QBigInt.h"
namespace dev namespace dev
{ {
@ -81,8 +82,8 @@ class DebuggingStateWrapper: public QObject
Q_OBJECT Q_OBJECT
Q_PROPERTY(int step READ step CONSTANT) Q_PROPERTY(int step READ step CONSTANT)
Q_PROPERTY(int curPC READ curPC CONSTANT) Q_PROPERTY(int curPC READ curPC CONSTANT)
Q_PROPERTY(QString gasCost READ gasCost CONSTANT) Q_PROPERTY(QBigInt* gasCost READ gasCost CONSTANT)
Q_PROPERTY(QString gas READ gas CONSTANT) Q_PROPERTY(QBigInt* gas READ gas CONSTANT)
Q_PROPERTY(QString instruction READ instruction CONSTANT) Q_PROPERTY(QString instruction READ instruction CONSTANT)
Q_PROPERTY(QStringList debugStack READ debugStack CONSTANT) Q_PROPERTY(QStringList debugStack READ debugStack CONSTANT)
Q_PROPERTY(QStringList debugStorage READ debugStorage CONSTANT) Q_PROPERTY(QStringList debugStorage READ debugStorage CONSTANT)
@ -90,7 +91,7 @@ class DebuggingStateWrapper: public QObject
Q_PROPERTY(QVariantList debugCallData READ debugCallData CONSTANT) Q_PROPERTY(QVariantList debugCallData READ debugCallData CONSTANT)
Q_PROPERTY(QString headerInfo READ headerInfo CONSTANT) Q_PROPERTY(QString headerInfo READ headerInfo CONSTANT)
Q_PROPERTY(QString endOfDebug READ endOfDebug CONSTANT) Q_PROPERTY(QString endOfDebug READ endOfDebug CONSTANT)
Q_PROPERTY(QString newMemSize READ newMemSize CONSTANT) Q_PROPERTY(QBigInt* newMemSize READ newMemSize CONSTANT)
Q_PROPERTY(QStringList levels READ levels CONSTANT) Q_PROPERTY(QStringList levels READ levels CONSTANT)
public: public:
@ -99,12 +100,10 @@ public:
int step() { return (int)m_state.steps; } int step() { return (int)m_state.steps; }
/// Get the proccessed code index. /// Get the proccessed code index.
int curPC() { return (int)m_state.curPC; } int curPC() { return (int)m_state.curPC; }
/// Get gas left.
QString gasLeft();
/// Get gas cost. /// Get gas cost.
QString gasCost(); QBigInt* gasCost();
/// Get gas used. /// Get gas used.
QString gas(); QBigInt* gas();
/// Get stack. /// Get stack.
QStringList debugStack(); QStringList debugStack();
/// Get storage. /// Get storage.
@ -118,7 +117,7 @@ public:
/// get end of debug information. /// get end of debug information.
QString endOfDebug(); QString endOfDebug();
/// Get the new memory size. /// Get the new memory size.
QString newMemSize(); QBigInt* newMemSize();
/// Get current instruction /// Get current instruction
QString instruction(); QString instruction();
/// Get all previous steps. /// Get all previous steps.

31
mix/Exceptions.cpp

@ -0,0 +1,31 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file Exceptions.cpp
* @author Arkadiy Paronyan arkadiy@ethdev.com
* @date 2015
* Ethereum IDE client.
*/
#include <ostream>
#include <QQmlError>
#include "Exceptions.h"
std::ostream& operator<<(std::ostream& _out, QQmlError const& _error)
{
_out << _error.toString().toStdString();
return _out;
}

45
mix/Exceptions.h

@ -0,0 +1,45 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file Exceptions.h
* @author Arkadiy Paronyan arkadiy@ethdev.com
* @date 2015
* Ethereum IDE client.
*/
#pragma once
#include <iosfwd>
#include <libdevcore/Exceptions.h>
class QTextDocument;
class QQmlError;
namespace dev
{
namespace mix
{
struct QmlLoadException: virtual Exception {};
struct FileIoException: virtual Exception {};
typedef boost::error_info<struct tagQmlError, QQmlError> QmlErrorInfo;
typedef boost::error_info<struct tagFileError, std::string> FileError;
}
}
std::ostream& operator<<(std::ostream& _out, QQmlError const& _error);

1
mix/FileIo.cpp

@ -20,7 +20,6 @@
* Ethereum IDE client. * Ethereum IDE client.
*/ */
#include <stdexcept>
#include <QDir> #include <QDir>
#include <QFile> #include <QFile>
#include <QFileInfo> #include <QFileInfo>

5
mix/MixApplication.cpp

@ -21,7 +21,6 @@
#include <QDebug> #include <QDebug>
#include <QQmlApplicationEngine> #include <QQmlApplicationEngine>
#include "CodeEditorExtensionManager.h"
#include "MixApplication.h" #include "MixApplication.h"
#include "AppContext.h" #include "AppContext.h"
@ -32,10 +31,8 @@ using namespace dev::mix;
MixApplication::MixApplication(int _argc, char* _argv[]): MixApplication::MixApplication(int _argc, char* _argv[]):
QApplication(_argc, _argv), m_engine(new QQmlApplicationEngine()), m_appContext(new AppContext(m_engine.get())) QApplication(_argc, _argv), m_engine(new QQmlApplicationEngine()), m_appContext(new AppContext(m_engine.get()))
{ {
qmlRegisterType<CodeEditorExtensionManager>("CodeEditorExtensionManager", 1, 0, "CodeEditorExtensionManager");
QObject::connect(this, SIGNAL(lastWindowClosed()), context(), SLOT(quitApplication())); //use to kill ApplicationContext and other stuff QObject::connect(this, SIGNAL(lastWindowClosed()), context(), SLOT(quitApplication())); //use to kill ApplicationContext and other stuff
m_engine->load(QUrl("qrc:/qml/main.qml")); m_appContext->load();
m_appContext->appLoaded();
} }
MixApplication::~MixApplication() MixApplication::~MixApplication()

22
mix/MixClient.cpp

@ -1,18 +1,18 @@
/* /*
This file is part of cpp-ethereum. This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful, cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** @file MixClient.cpp /** @file MixClient.cpp
* @author Yann yann@ethdev.com * @author Yann yann@ethdev.com

22
mix/MixClient.h

@ -1,18 +1,18 @@
/* /*
This file is part of cpp-ethereum. This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful, cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** @file MixClient.h /** @file MixClient.h
* @author Yann yann@ethdev.com * @author Yann yann@ethdev.com

59
mix/QBigInt.cpp

@ -0,0 +1,59 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file QBigInt.cpp
* @author Yann yann@ethdev.com
* @date 2015
*/
#include <boost/variant/multivisitors.hpp>
#include <boost/variant.hpp>
#include <libdevcore/CommonJS.h>
#include "QBigInt.h"
using namespace dev;
using namespace dev::mix;
QString QBigInt::value() const
{
std::ostringstream s;
s << m_internalValue;
return QString::fromStdString(s.str());
}
QBigInt* QBigInt::subtract(QBigInt* const& _value) const
{
BigIntVariant toSubtract = _value->internalValue();
return new QBigInt(boost::apply_visitor(mix::subtract(), m_internalValue, toSubtract));
}
QBigInt* QBigInt::add(QBigInt* const& _value) const
{
BigIntVariant toAdd = _value->internalValue();
return new QBigInt(boost::apply_visitor(mix::add(), m_internalValue, toAdd));
}
QBigInt* QBigInt::multiply(QBigInt* const& _value) const
{
BigIntVariant toMultiply = _value->internalValue();
return new QBigInt(boost::apply_visitor(mix::multiply(), m_internalValue, toMultiply));
}
QBigInt* QBigInt::divide(QBigInt* const& _value) const
{
BigIntVariant toDivide = _value->internalValue();
return new QBigInt(boost::apply_visitor(mix::divide(), m_internalValue, toDivide));
}

102
mix/QBigInt.h

@ -0,0 +1,102 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file QBigInt.h
* @author Yann yann@ethdev.com
* @date 2015
* Represent a big integer (u256, bigint) to be used in QML.
*/
#pragma once
#include "boost/variant.hpp"
#include "boost/variant/multivisitors.hpp"
#include <QObject>
#include <QQmlEngine>
#include <libdevcore/CommonJS.h>
#include <libdevcore/Common.h>
using namespace dev;
namespace dev
{
namespace mix
{
using BigIntVariant = boost::variant<dev::u256, dev::bigint>;
struct add: public boost::static_visitor<BigIntVariant>
{
template<class T1, class T2>
BigIntVariant operator()(T1 const& _value, T2 const& _otherValue) const { return _value + _otherValue; }
};
struct subtract: public boost::static_visitor<BigIntVariant>
{
template<class T1, class T2>
BigIntVariant operator()(T1 const& _value, T2 const& _otherValue) const { return _value - _otherValue; }
};
struct multiply: public boost::static_visitor<BigIntVariant>
{
template<class T1, class T2>
BigIntVariant operator()(T1 const& _value, T2 const& _otherValue) const { return _value * _otherValue; }
};
struct divide: public boost::static_visitor<BigIntVariant>
{
template<class T1, class T2>
BigIntVariant operator()(T1 const& _value, T2 const& _otherValue) const { return _value / _otherValue; }
};
/*
* Represent big integer like big int and u256 in QML.
* The ownership is set by default to Javascript.
*/
class QBigInt: public QObject
{
Q_OBJECT
public:
QBigInt(QObject* _parent = 0): QObject(_parent), m_internalValue(dev::u256(0)) { QQmlEngine::setObjectOwnership(this, QQmlEngine::JavaScriptOwnership); }
QBigInt(dev::u256 const& _value, QObject* _parent = 0): QObject(_parent), m_internalValue(_value) { QQmlEngine::setObjectOwnership(this, QQmlEngine::JavaScriptOwnership); }
QBigInt(dev::bigint const& _value, QObject* _parent = 0): QObject(_parent), m_internalValue(_value) { QQmlEngine::setObjectOwnership(this, QQmlEngine::JavaScriptOwnership); }
QBigInt(BigIntVariant const& _value, QObject* _parent = 0): QObject(_parent), m_internalValue(_value){ QQmlEngine::setObjectOwnership(this, QQmlEngine::JavaScriptOwnership); }
~QBigInt() {}
/// @returns the current used big integer.
BigIntVariant internalValue() { return m_internalValue; }
/// @returns a string representation of the big integer used. Invokable from QML.
Q_INVOKABLE QString value() const;
/// Set the value of the BigInteger used. Will use u256 type. Invokable from QML.
Q_INVOKABLE void setValue(QString const& _value) { m_internalValue = dev::jsToU256(_value.toStdString()); }
/// Subtract by @a _value. Invokable from QML.
Q_INVOKABLE QBigInt* subtract(QBigInt* const& _value) const;
/// Add @a _value to the current big integer. Invokable from QML.
Q_INVOKABLE QBigInt* add(QBigInt* const& _value) const;
/// Multiply by @a _value. Invokable from QML.
Q_INVOKABLE QBigInt* multiply(QBigInt* const& _value) const;
/// divide by @a _value. Invokable from QML.
Q_INVOKABLE QBigInt* divide(QBigInt* const& _value) const;
protected:
BigIntVariant m_internalValue;
};
}
}

55
mix/QEther.cpp

@ -0,0 +1,55 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file QEther.cpp
* @author Yann yann@ethdev.com
* @date 2014
*/
#include <QMetaEnum>
#include "QEther.h"
using namespace dev::mix;
QString QEther::format() const
{
return QString::fromStdString(dev::eth::formatBalance(boost::get<dev::u256>(toWei()->internalValue())));
}
QBigInt* QEther::toWei() const
{
QMetaEnum units = staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("EtherUnit"));
const char* key = units.valueToKey(m_currentUnit);
for (std::pair<dev::u256, std::string> rawUnit: dev::eth::units())
{
if (rawUnit.second == QString(key).toLower().toStdString())
return multiply(new QBigInt(rawUnit.first));
}
return new QBigInt(dev::u256(0));
}
void QEther::setUnit(QString const& _unit)
{
QMetaEnum units = staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("EtherUnit"));
for (int k = 0; k < units.keyCount(); k++)
{
if (QString(units.key(k)).toLower() == _unit)
{
m_currentUnit = static_cast<EtherUnit>(units.keysToValue(units.key(k)));
return;
}
}
}

92
mix/QEther.h

@ -0,0 +1,92 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file QEther.h
* @author Yann yann@ethdev.com
* @date 2014
* Represent an amount of Ether in QML (mapped to u256 in c++).
*/
#pragma once
#include <QObject>
#include <libethcore/CommonEth.h>
#include "QBigInt.h"
namespace dev
{
namespace mix
{
class QEther: public QBigInt
{
Q_OBJECT
Q_ENUMS(EtherUnit)
Q_PROPERTY(QString value READ value WRITE setValue NOTIFY valueChanged)
Q_PROPERTY(EtherUnit unit READ unit WRITE setUnit NOTIFY unitChanged)
public:
enum EtherUnit
{
Uether,
Vether,
Dether,
Nether,
Yether,
Zether,
Eether,
Pether,
Tether,
Gether,
Mether,
Grand,
Ether,
Finney,
Szabo,
Gwei,
Mwei,
Kwei,
Wei
};
QEther(QObject* _parent = 0): QBigInt(dev::u256(0), _parent), m_currentUnit(EtherUnit::Wei) {}
QEther(dev::u256 _value, EtherUnit _unit, QObject* _parent = 0): QBigInt(_value, _parent), m_currentUnit(_unit) {}
~QEther() {}
/// @returns user-friendly string representation of the amount of ether. Invokable from QML.
Q_INVOKABLE QString format() const;
/// @returns the current amount of Ether in Wei. Invokable from QML.
Q_INVOKABLE QBigInt* toWei() const;
/// @returns the current unit used. Invokable from QML.
Q_INVOKABLE EtherUnit unit() const { return m_currentUnit; }
/// Set the unit to be used. Invokable from QML.
Q_INVOKABLE void setUnit(EtherUnit const& _unit) { m_currentUnit = _unit; }
/// Set the unit to be used. Invokable from QML.
Q_INVOKABLE void setUnit(QString const& _unit);
/// @returns the u256 value of the current amount of Ether in Wei.
dev::u256 toU256Wei() { return boost::get<dev::u256>(toWei()->internalValue()); }
private:
EtherUnit m_currentUnit;
signals:
void valueChanged();
void unitChanged();
};
}
}

17
mix/main.cpp

@ -20,11 +20,24 @@
* Ethereum IDE client. * Ethereum IDE client.
*/ */
#include <iostream>
#include "MixApplication.h" #include "MixApplication.h"
#include "Exceptions.h"
using namespace dev::mix; using namespace dev::mix;
int main(int _argc, char* _argv[]) int main(int _argc, char* _argv[])
{ {
MixApplication app(_argc, _argv); try
return app.exec(); {
MixApplication app(_argc, _argv);
return app.exec();
}
catch (boost::exception const& _e)
{
std::cerr << boost::diagnostic_information(_e);
}
catch (std::exception const& _e)
{
std::cerr << _e.what();
}
} }

3
mix/qml.qrc

@ -40,5 +40,8 @@
<file>qml/CodeEditorView.qml</file> <file>qml/CodeEditorView.qml</file>
<file>qml/js/ProjectModel.js</file> <file>qml/js/ProjectModel.js</file>
<file>qml/WebPreview.qml</file> <file>qml/WebPreview.qml</file>
<file>qml/Ether.qml</file>
<file>qml/EtherValue.qml</file>
<file>qml/BigIntValue.qml</file>
</qresource> </qresource>
</RCC> </RCC>

13
mix/qml/BigIntValue.qml

@ -0,0 +1,13 @@
/*
* Used to instanciate a QEther obj using Qt.createComponent function.
*/
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.1
import QtQuick.Controls.Styles 1.1
import org.ethereum.qml.QBigInt 1.0
QBigInt
{
id: bigInt
}

3
mix/qml/CodeEditorView.qml

@ -2,7 +2,6 @@ import QtQuick 2.0
import QtQuick.Window 2.0 import QtQuick.Window 2.0
import QtQuick.Layouts 1.0 import QtQuick.Layouts 1.0
import QtQuick.Controls 1.0 import QtQuick.Controls 1.0
import org.ethereum.qml.ProjectModel 1.0
Item { Item {
@ -40,7 +39,7 @@ Item {
} }
Connections { Connections {
target: ProjectModel target: projectModel
onDocumentOpened: { onDocumentOpened: {
openDocument(document); openDocument(document);
} }

115
mix/qml/Ether.qml

@ -0,0 +1,115 @@
/*
* Display a row containing :
* - The amount of Ether.
* - The unit used.
* - User-friendly string representation of the amout of Ether (if displayFormattedValue == true).
* 'value' has to be a QEther obj.
*/
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.1
import QtQuick.Controls.Styles 1.1
Rectangle {
id: etherEdition
property bool displayFormattedValue;
property bool edit;
property variant value;
onValueChanged: update()
Component.onCompleted: update()
function update()
{
if (value !== undefined)
{
etherValueEdit.text = value.value;
selectUnit(value.unit);
}
}
function selectUnit(unit)
{
units.currentIndex = unit;
}
RowLayout
{
anchors.fill: parent;
id: row
width: 200
height: parent.height
Rectangle
{
width : 200
color: edit ? "blue" : "white"
TextField
{
onTextChanged:
{
if (value !== undefined)
{
value.setValue(text)
formattedValue.text = value.format();
}
}
width: parent.width
readOnly: !edit
visible: edit
id: etherValueEdit;
}
}
Rectangle
{
Layout.fillWidth: true
id: unitContainer
width: 20
anchors.verticalCenter: parent.verticalCenter
ComboBox
{
id: units
onCurrentTextChanged:
{
if (value !== undefined)
{
value.setUnit(currentText);
formattedValue.text = value.format();
}
}
model: ListModel {
id: unitsModel
ListElement { text: "Uether"; }
ListElement { text: "Vether"; }
ListElement { text: "Dether"; }
ListElement { text: "Nether"; }
ListElement { text: "Yether"; }
ListElement { text: "Zether"; }
ListElement { text: "Eether"; }
ListElement { text: "Pether"; }
ListElement { text: "Tether"; }
ListElement { text: "Gether"; }
ListElement { text: "Mether"; }
ListElement { text: "grand"; }
ListElement { text: "ether"; }
ListElement { text: "finney"; }
ListElement { text: "szabo"; }
ListElement { text: "Gwei"; }
ListElement { text: "Mwei"; }
ListElement { text: "Kwei"; }
ListElement { text: "wei"; }
}
}
Rectangle
{
anchors.verticalCenter: parent.verticalCenter
anchors.left: units.right
visible: displayFormattedValue
width: 20
Text
{
id: formattedValue
}
}
}
}
}

15
mix/qml/EtherValue.qml

@ -0,0 +1,15 @@
/*
* Used to instanciate a QEther obj using Qt.createComponent function.
*/
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.1
import QtQuick.Controls.Styles 1.1
import org.ethereum.qml.QEther 1.0
QEther
{
id: basicEther
value: "100000000000"
unit: QEther.Wei
}

17
mix/qml/ProjectList.qml

@ -2,7 +2,6 @@ import QtQuick 2.0
import QtQuick.Window 2.0 import QtQuick.Window 2.0
import QtQuick.Layouts 1.0 import QtQuick.Layouts 1.0
import QtQuick.Controls 1.0 import QtQuick.Controls 1.0
import org.ethereum.qml.ProjectModel 1.0
Item { Item {
property bool renameMode: false; property bool renameMode: false;
@ -11,16 +10,16 @@ Item {
Text { Text {
Layout.fillWidth: true Layout.fillWidth: true
color: "blue" color: "blue"
text: ProjectModel.projectData ? ProjectModel.projectData.title : "" text: projectModel.projectData ? projectModel.projectData.title : ""
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
visible: !ProjectModel.isEmpty; visible: !projectModel.isEmpty;
} }
ListView { ListView {
id: projectList id: projectList
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: true Layout.fillHeight: true
model: ProjectModel.listModel model: projectModel.listModel
delegate: renderDelegate delegate: renderDelegate
highlight: Rectangle { highlight: Rectangle {
@ -31,8 +30,8 @@ Item {
clip: true clip: true
onCurrentIndexChanged: { onCurrentIndexChanged: {
if (currentIndex >= 0 && currentIndex < ProjectModel.listModel.count) if (currentIndex >= 0 && currentIndex < projectModel.listModel.count)
ProjectModel.openDocument(ProjectModel.listModel.get(currentIndex).documentId); projectModel.openDocument(projectModel.listModel.get(currentIndex).documentId);
} }
} }
Menu { Menu {
@ -46,7 +45,7 @@ Item {
MenuItem { MenuItem {
text: qsTr("Delete") text: qsTr("Delete")
onTriggered: { onTriggered: {
ProjectModel.removeDocument(projectList.model.get(projectList.currentIndex).documentId); projectModel.removeDocument(projectList.model.get(projectList.currentIndex).documentId);
} }
} }
} }
@ -103,7 +102,7 @@ Item {
function close(accept) { function close(accept) {
renameMode = false; renameMode = false;
if (accept) if (accept)
ProjectModel.renameDocument(projectList.model.get(projectList.currentIndex).documentId, textInput.text); projectModel.renameDocument(projectList.model.get(projectList.currentIndex).documentId, textInput.text);
} }
} }
MouseArea { MouseArea {
@ -119,7 +118,7 @@ Item {
} }
} }
Connections { Connections {
target: ProjectModel target: projectModel
onProjectLoaded: { onProjectLoaded: {
projectList.currentIndex = 0; projectList.currentIndex = 0;
} }

4
mix/qml/ProjectModel.qml

@ -1,5 +1,3 @@
pragma Singleton
import QtQuick 2.0 import QtQuick 2.0
import QtQuick.Window 2.0 import QtQuick.Window 2.0
import QtQuick.Layouts 1.0 import QtQuick.Layouts 1.0
@ -46,7 +44,7 @@ Item {
target: appContext target: appContext
onAppLoaded: { onAppLoaded: {
if (projectSettings.lastProjectPath) if (projectSettings.lastProjectPath)
loadProject(projectSettings.lastProjectPath) projectModel.loadProject(projectSettings.lastProjectPath)
} }
} }

25
mix/qml/StateDialog.qml

@ -2,8 +2,10 @@ import QtQuick 2.2
import QtQuick.Controls 1.1 import QtQuick.Controls 1.1
import QtQuick.Layouts 1.1 import QtQuick.Layouts 1.1
import QtQuick.Window 2.0 import QtQuick.Window 2.0
import org.ethereum.qml.QEther 1.0
Window { Window {
id: modalStateDialog
modality: Qt.WindowModal modality: Qt.WindowModal
width:640 width:640
@ -12,7 +14,7 @@ Window {
visible: false visible: false
property alias stateTitle: titleField.text property alias stateTitle: titleField.text
property alias stateBalance: balanceField.text property alias stateBalance: balanceField.value
property int stateIndex property int stateIndex
property var stateTransactions: [] property var stateTransactions: []
signal accepted signal accepted
@ -20,7 +22,7 @@ Window {
function open(index, item) { function open(index, item) {
stateIndex = index; stateIndex = index;
stateTitle = item.title; stateTitle = item.title;
stateBalance = item.balance; balanceField.value = item.balance;
transactionsModel.clear(); transactionsModel.clear();
stateTransactions = []; stateTransactions = [];
var transactions = item.transactions; var transactions = item.transactions;
@ -66,8 +68,10 @@ Window {
Label { Label {
text: qsTr("Balance") text: qsTr("Balance")
} }
TextField { Ether {
id: balanceField id: balanceField
edit: true
displayFormattedValue: true
Layout.fillWidth: true Layout.fillWidth: true
} }
@ -114,16 +118,25 @@ Window {
transactionDialog.open(index, transactionsModel.get(index)); transactionDialog.open(index, transactionsModel.get(index));
} }
function ether(_value, _unit)
{
var etherComponent = Qt.createComponent("qrc:/qml/EtherValue.qml");
var ether = etherComponent.createObject(modalStateDialog);
ether.setValue(_value);
ether.setUnit(_unit);
return ether;
}
function addTransaction() { function addTransaction() {
// Set next id here to work around Qt bug // Set next id here to work around Qt bug
// https://bugreports.qt-project.org/browse/QTBUG-41327 // https://bugreports.qt-project.org/browse/QTBUG-41327
// Second call to signal handler would just edit the item that was just created, no harm done // Second call to signal handler would just edit the item that was just created, no harm done
var item = { var item = {
value: "0", value: ether("0", QEther.Wei),
functionId: "", functionId: "",
gas: "125000", gas: ether("125000", QEther.Wei),
gasPrice: "100000" gasPrice: ether("100000", QEther.Wei)
}; };
transactionDialog.open(transactionsModel.count, item); transactionDialog.open(transactionsModel.count, item);

12
mix/qml/StateList.qml

@ -3,7 +3,7 @@ import QtQuick.Controls.Styles 1.1
import QtQuick.Controls 1.1 import QtQuick.Controls 1.1
import QtQuick.Dialogs 1.1 import QtQuick.Dialogs 1.1
import QtQuick.Layouts 1.1 import QtQuick.Layouts 1.1
import org.ethereum.qml.ProjectModel 1.0 import org.ethereum.qml.QEther 1.0
Rectangle { Rectangle {
color: "#ededed" color: "#ededed"
@ -16,7 +16,7 @@ Rectangle {
property var stateList: [] property var stateList: []
Connections { Connections {
target: ProjectModel target: projectModel
onProjectClosed: { onProjectClosed: {
stateListModel.clear(); stateListModel.clear();
} }
@ -67,9 +67,13 @@ Rectangle {
id: stateListModel id: stateListModel
function addState() { function addState() {
var etherComponent = Qt.createComponent("qrc:/qml/EtherValue.qml");
var ether = etherComponent.createObject(stateListContainer);
ether.setValue("100000000000000000000000000");
ether.setUnit(QEther.Wei);
var item = { var item = {
title: "", title: "",
balance: "100000000000000000000000000", balance: ether,
transactions: [] transactions: []
}; };
stateDialog.open(stateListModel.count, item); stateDialog.open(stateListModel.count, item);
@ -91,7 +95,7 @@ Rectangle {
} }
function save() { function save() {
ProjectModel.saveProject(); projectModel.saveProject();
} }
} }

48
mix/qml/TransactionDialog.qml

@ -2,8 +2,10 @@ import QtQuick 2.2
import QtQuick.Controls 1.1 import QtQuick.Controls 1.1
import QtQuick.Layouts 1.1 import QtQuick.Layouts 1.1
import QtQuick.Window 2.0 import QtQuick.Window 2.0
import org.ethereum.qml.QEther 1.0
Window { Window {
id: modalTransactionDialog
modality: Qt.WindowModal modality: Qt.WindowModal
width:640 width:640
height:480 height:480
@ -11,9 +13,9 @@ Window {
property int transactionIndex property int transactionIndex
property alias transactionParams: paramsModel; property alias transactionParams: paramsModel;
property alias gas: gasField.text; property alias gas: gasField.value;
property alias gasPrice: gasPriceField.text; property alias gasPrice: gasPriceField.value;
property alias transactionValue: valueField.text; property alias transactionValue: valueField.value;
property alias functionId: functionComboBox.currentText; property alias functionId: functionComboBox.currentText;
property var itemParams; property var itemParams;
@ -21,9 +23,9 @@ Window {
function open(index, item) { function open(index, item) {
transactionIndex = index; transactionIndex = index;
gas = item.gas; gasField.value = item.gas;
gasPrice = item.gasPrice; gasPriceField.value = item.gasPrice;
transactionValue = item.value; valueField.value = item.value;
var functionId = item.functionId; var functionId = item.functionId;
itemParams = item.parameters !== undefined ? item.parameters : {}; itemParams = item.parameters !== undefined ? item.parameters : {};
functionsModel.clear(); functionsModel.clear();
@ -53,7 +55,7 @@ Window {
var parameters = func.parameters; var parameters = func.parameters;
for (var p = 0; p < parameters.length; p++) { for (var p = 0; p < parameters.length; p++) {
var pname = parameters[p].name; var pname = parameters[p].name;
paramsModel.append({ name: pname, type: parameters[p].type, value: itemParams[pname] !== undefined ? itemParams[pname] : "" }); paramsModel.append({ name: pname, type: parameters[p].type, value: itemParams[pname] !== undefined ? itemParams[pname].value() : "" });
} }
} }
} }
@ -74,7 +76,10 @@ Window {
} }
for (var p = 0; p < transactionDialog.transactionParams.count; p++) { for (var p = 0; p < transactionDialog.transactionParams.count; p++) {
var parameter = transactionDialog.transactionParams.get(p); var parameter = transactionDialog.transactionParams.get(p);
item.parameters[parameter.name] = parameter.value; var intComponent = Qt.createComponent("qrc:/qml/BigIntValue.qml");
var param = intComponent.createObject(modalTransactionDialog);
param.setValue(parameter.value);
item.parameters[parameter.name] = param;
} }
return item; return item;
} }
@ -107,25 +112,40 @@ Window {
Label { Label {
text: qsTr("Value") text: qsTr("Value")
} }
TextField { Rectangle
id: valueField {
Layout.fillWidth: true Layout.fillWidth: true
Ether {
id: valueField
edit: true
displayFormattedValue: true
}
} }
Label { Label {
text: qsTr("Gas") text: qsTr("Gas")
} }
TextField { Rectangle
id: gasField {
Layout.fillWidth: true Layout.fillWidth: true
Ether {
id: gasField
edit: true
displayFormattedValue: true
}
} }
Label { Label {
text: qsTr("Gas price") text: qsTr("Gas price")
} }
TextField { Rectangle
id: gasPriceField {
Layout.fillWidth: true Layout.fillWidth: true
Ether {
id: gasPriceField
edit: true
displayFormattedValue: true
}
} }
Label { Label {

6
mix/qml/js/Debugger.js

@ -65,9 +65,9 @@ function highlightSelection(index)
function completeCtxInformation(state) function completeCtxInformation(state)
{ {
currentStep.update(state.step); currentStep.update(state.step);
mem.update(state.newMemSize + " " + qsTr("words")); mem.update(state.newMemSize.value() + " " + qsTr("words"));
stepCost.update(state.gasCost); stepCost.update(state.gasCost.value());
gasSpent.update(debugStates[0].gas - state.gas); gasSpent.update(debugStates[0].gas.subtract(state.gas).value());
stack.listModel = state.debugStack; stack.listModel = state.debugStack;
storage.listModel = state.debugStorage; storage.listModel = state.debugStorage;

29
mix/qml/main.qml

@ -5,7 +5,6 @@ import QtQuick.Dialogs 1.1
import QtQuick.Layouts 1.1 import QtQuick.Layouts 1.1
import QtQuick.Window 2.1 import QtQuick.Window 2.1
import CodeEditorExtensionManager 1.0 import CodeEditorExtensionManager 1.0
import org.ethereum.qml.ProjectModel 1.0
ApplicationWindow { ApplicationWindow {
id: mainApplication id: mainApplication
@ -101,7 +100,7 @@ ApplicationWindow {
text: qsTr("&New project") text: qsTr("&New project")
shortcut: "Ctrl+N" shortcut: "Ctrl+N"
enabled: true; enabled: true;
onTriggered: ProjectModel.createProject(); onTriggered: projectModel.createProject();
} }
Action { Action {
@ -109,54 +108,54 @@ ApplicationWindow {
text: qsTr("&Open project") text: qsTr("&Open project")
shortcut: "Ctrl+O" shortcut: "Ctrl+O"
enabled: true; enabled: true;
onTriggered: ProjectModel.browseProject(); onTriggered: projectModel.browseProject();
} }
Action { Action {
id: addNewJsFileAction id: addNewJsFileAction
text: qsTr("New JavaScript file") text: qsTr("New JavaScript file")
shortcut: "Ctrl+Alt+J" shortcut: "Ctrl+Alt+J"
enabled: !ProjectModel.isEmpty enabled: !projectModel.isEmpty
onTriggered: ProjectModel.newJsFile(); onTriggered: projectModel.newJsFile();
} }
Action { Action {
id: addNewHtmlFileAction id: addNewHtmlFileAction
text: qsTr("New HTML file") text: qsTr("New HTML file")
shortcut: "Ctrl+Alt+H" shortcut: "Ctrl+Alt+H"
enabled: !ProjectModel.isEmpty enabled: !projectModel.isEmpty
onTriggered: ProjectModel.newHtmlFile(); onTriggered: projectModel.newHtmlFile();
} }
Action { Action {
id: addNewContractAction id: addNewContractAction
text: qsTr("New contract") text: qsTr("New contract")
shortcut: "Ctrl+Alt+C" shortcut: "Ctrl+Alt+C"
enabled: !ProjectModel.isEmpty enabled: !projectModel.isEmpty
onTriggered: ProjectModel.newContract(); onTriggered: projectModel.newContract();
} }
Action { Action {
id: addExistingFileAction id: addExistingFileAction
text: qsTr("Add existing file") text: qsTr("Add existing file")
shortcut: "Ctrl+Alt+A" shortcut: "Ctrl+Alt+A"
enabled: !ProjectModel.isEmpty enabled: !projectModel.isEmpty
onTriggered: ProjectModel.addExistingFile(); onTriggered: projectModel.addExistingFile();
} }
Action { Action {
id: saveAllFilesAction id: saveAllFilesAction
text: qsTr("Save all") text: qsTr("Save all")
shortcut: "Ctrl+S" shortcut: "Ctrl+S"
enabled: !ProjectModel.isEmpty enabled: !projectModel.isEmpty
onTriggered: ProjectModel.saveAll(); onTriggered: projectModel.saveAll();
} }
Action { Action {
id: closeProjectAction id: closeProjectAction
text: qsTr("Close project") text: qsTr("Close project")
shortcut: "Ctrl+W" shortcut: "Ctrl+W"
enabled: !ProjectModel.isEmpty enabled: !projectModel.isEmpty
onTriggered: ProjectModel.closeProject(); onTriggered: projectModel.closeProject();
} }
} }

8
test/SolidityABIJSON.cpp

@ -41,13 +41,9 @@ public:
{ {
m_compilerStack.parse(_code); m_compilerStack.parse(_code);
} }
catch (const std::exception& e) catch(boost::exception const& _e)
{ {
std::string const* extra = boost::get_error_info<errinfo_comment>(e); auto msg = std::string("Parsing contract failed with: ") + boost::diagnostic_information(_e);
std::string msg = std::string("Parsing contract failed with: ") +
e.what() + std::string("\n");
if (extra)
msg += *extra;
BOOST_FAIL(msg); BOOST_FAIL(msg);
} }
std::string generatedInterfaceString = m_compilerStack.getMetadata("", DocumentationType::ABI_INTERFACE); std::string generatedInterfaceString = m_compilerStack.getMetadata("", DocumentationType::ABI_INTERFACE);

2
test/SolidityCompiler.cpp

@ -64,7 +64,7 @@ bytes compileContract(const string& _sourceCode)
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get())) if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
{ {
Compiler compiler; Compiler compiler;
compiler.compileContract(*contract, {}, map<ContractDefinition const*, bytes const*>{}); compiler.compileContract(*contract, map<ContractDefinition const*, bytes const*>{});
// debug // debug
//compiler.streamAssembly(cout); //compiler.streamAssembly(cout);

224
test/SolidityEndToEndTest.cpp

@ -772,6 +772,94 @@ BOOST_AUTO_TEST_CASE(struct_reference)
BOOST_CHECK(callContractFunction("check()") == encodeArgs(true)); BOOST_CHECK(callContractFunction("check()") == encodeArgs(true));
} }
BOOST_AUTO_TEST_CASE(deleteStruct)
{
char const* sourceCode = R"(
contract test {
struct topStruct {
nestedStruct nstr;
emptyStruct empty;
uint topValue;
mapping (uint => uint) topMapping;
}
uint toDelete;
topStruct str;
struct nestedStruct {
uint nestedValue;
mapping (uint => bool) nestedMapping;
}
struct emptyStruct{
}
function test(){
toDelete = 5;
str.topValue = 1;
str.topMapping[0] = 1;
str.topMapping[1] = 2;
str.nstr.nestedValue = 2;
str.nstr.nestedMapping[0] = true;
str.nstr.nestedMapping[1] = false;
delete str;
delete toDelete;
}
function getToDelete() returns (uint res){
res = toDelete;
}
function getTopValue() returns(uint topValue){
topValue = str.topValue;
}
function getNestedValue() returns(uint nestedValue){
nestedValue = str.nstr.nestedValue;
}
function getTopMapping(uint index) returns(uint ret) {
ret = str.topMapping[index];
}
function getNestedMapping(uint index) returns(bool ret) {
return str.nstr.nestedMapping[index];
}
})";
compileAndRun(sourceCode);
BOOST_CHECK(callContractFunction("getToDelete()") == encodeArgs(0));
BOOST_CHECK(callContractFunction("getTopValue()") == encodeArgs(0));
BOOST_CHECK(callContractFunction("getNestedValue()") == encodeArgs(0));
// mapping values should be the same
BOOST_CHECK(callContractFunction("getTopMapping(uint256)", 0) == encodeArgs(1));
BOOST_CHECK(callContractFunction("getTopMapping(uint256)", 1) == encodeArgs(2));
BOOST_CHECK(callContractFunction("getNestedMapping(uint256)", 0) == encodeArgs(true));
BOOST_CHECK(callContractFunction("getNestedMapping(uint256)", 1) == encodeArgs(false));
}
BOOST_AUTO_TEST_CASE(deleteLocal)
{
char const* sourceCode = R"(
contract test {
function delLocal() returns (uint res){
uint v = 5;
delete v;
res = v;
}
})";
compileAndRun(sourceCode);
BOOST_CHECK(callContractFunction("delLocal()") == encodeArgs(0));
}
BOOST_AUTO_TEST_CASE(deleteLocals)
{
char const* sourceCode = R"(
contract test {
function delLocal() returns (uint res1, uint res2){
uint v = 5;
uint w = 6;
uint x = 7;
delete v;
res1 = w;
res2 = x;
}
})";
compileAndRun(sourceCode);
BOOST_CHECK(callContractFunction("delLocal()") == encodeArgs(6, 7));
}
BOOST_AUTO_TEST_CASE(constructor) BOOST_AUTO_TEST_CASE(constructor)
{ {
char const* sourceCode = "contract test {\n" char const* sourceCode = "contract test {\n"
@ -1243,6 +1331,7 @@ BOOST_AUTO_TEST_CASE(constructor_arguments)
contract Helper { contract Helper {
string3 name; string3 name;
bool flag; bool flag;
function Helper(string3 x, bool f) { function Helper(string3 x, bool f) {
name = x; name = x;
flag = f; flag = f;
@ -1404,6 +1493,141 @@ BOOST_AUTO_TEST_CASE(value_for_constructor)
BOOST_REQUIRE(callContractFunction("getBalances()") == encodeArgs(12, 10)); BOOST_REQUIRE(callContractFunction("getBalances()") == encodeArgs(12, 10));
} }
BOOST_AUTO_TEST_CASE(virtual_function_calls)
{
char const* sourceCode = R"(
contract Base {
function f() returns (uint i) { return g(); }
function g() returns (uint i) { return 1; }
}
contract Derived is Base {
function g() returns (uint i) { return 2; }
}
)";
compileAndRun(sourceCode, 0, "Derived");
BOOST_CHECK(callContractFunction("g()") == encodeArgs(2));
BOOST_CHECK(callContractFunction("f()") == encodeArgs(2));
}
BOOST_AUTO_TEST_CASE(access_base_storage)
{
char const* sourceCode = R"(
contract Base {
uint dataBase;
function getViaBase() returns (uint i) { return dataBase; }
}
contract Derived is Base {
uint dataDerived;
function setData(uint base, uint derived) returns (bool r) {
dataBase = base;
dataDerived = derived;
return true;
}
function getViaDerived() returns (uint base, uint derived) {
base = dataBase;
derived = dataDerived;
}
}
)";
compileAndRun(sourceCode, 0, "Derived");
BOOST_CHECK(callContractFunction("setData(uint256,uint256)", 1, 2) == encodeArgs(true));
BOOST_CHECK(callContractFunction("getViaBase()") == encodeArgs(1));
BOOST_CHECK(callContractFunction("getViaDerived()") == encodeArgs(1, 2));
}
BOOST_AUTO_TEST_CASE(single_copy_with_multiple_inheritance)
{
char const* sourceCode = R"(
contract Base {
uint data;
function setData(uint i) { data = i; }
function getViaBase() returns (uint i) { return data; }
}
contract A is Base { function setViaA(uint i) { setData(i); } }
contract B is Base { function getViaB() returns (uint i) { return getViaBase(); } }
contract Derived is A, B, Base { }
)";
compileAndRun(sourceCode, 0, "Derived");
BOOST_CHECK(callContractFunction("getViaB()") == encodeArgs(0));
BOOST_CHECK(callContractFunction("setViaA(uint256)", 23) == encodeArgs());
BOOST_CHECK(callContractFunction("getViaB()") == encodeArgs(23));
}
BOOST_AUTO_TEST_CASE(explicit_base_cass)
{
char const* sourceCode = R"(
contract BaseBase { function g() returns (uint r) { return 1; } }
contract Base is BaseBase { function g() returns (uint r) { return 2; } }
contract Derived is Base {
function f() returns (uint r) { return BaseBase.g(); }
function g() returns (uint r) { return 3; }
}
)";
compileAndRun(sourceCode, 0, "Derived");
BOOST_CHECK(callContractFunction("g()") == encodeArgs(3));
BOOST_CHECK(callContractFunction("f()") == encodeArgs(1));
}
BOOST_AUTO_TEST_CASE(base_constructor_arguments)
{
char const* sourceCode = R"(
contract BaseBase {
uint m_a;
function BaseBase(uint a) {
m_a = a;
}
}
contract Base is BaseBase(7) {
function Base() {
m_a *= m_a;
}
}
contract Derived is Base() {
function getA() returns (uint r) { return m_a; }
}
)";
compileAndRun(sourceCode, 0, "Derived");
BOOST_CHECK(callContractFunction("getA()") == encodeArgs(7 * 7));
}
BOOST_AUTO_TEST_CASE(function_usage_in_constructor_arguments)
{
char const* sourceCode = R"(
contract BaseBase {
uint m_a;
function BaseBase(uint a) {
m_a = a;
}
function g() returns (uint r) { return 2; }
}
contract Base is BaseBase(BaseBase.g()) {
}
contract Derived is Base() {
function getA() returns (uint r) { return m_a; }
}
)";
compileAndRun(sourceCode, 0, "Derived");
BOOST_CHECK(callContractFunction("getA()") == encodeArgs(2));
}
BOOST_AUTO_TEST_CASE(constructor_argument_overriding)
{
char const* sourceCode = R"(
contract BaseBase {
uint m_a;
function BaseBase(uint a) {
m_a = a;
}
}
contract Base is BaseBase(2) { }
contract Derived is Base, BaseBase(3) {
function getA() returns (uint r) { return m_a; }
}
)";
compileAndRun(sourceCode, 0, "Derived");
BOOST_CHECK(callContractFunction("getA()") == encodeArgs(3));
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
} }

25
test/SolidityExpressionCompiler.cpp

@ -86,13 +86,19 @@ Declaration const& resolveDeclaration(vector<string> const& _namespacedName,
} }
bytes compileFirstExpression(const string& _sourceCode, vector<vector<string>> _functions = {}, bytes compileFirstExpression(const string& _sourceCode, vector<vector<string>> _functions = {},
vector<vector<string>> _localVariables = {}) vector<vector<string>> _localVariables = {}, vector<shared_ptr<MagicVariableDeclaration const>> _globalDeclarations = {})
{ {
Parser parser; Parser parser;
ASTPointer<SourceUnit> sourceUnit; ASTPointer<SourceUnit> sourceUnit;
BOOST_REQUIRE_NO_THROW(sourceUnit = parser.parse(make_shared<Scanner>(CharStream(_sourceCode)))); BOOST_REQUIRE_NO_THROW(sourceUnit = parser.parse(make_shared<Scanner>(CharStream(_sourceCode))));
NameAndTypeResolver resolver({});
vector<Declaration const*> declarations;
declarations.reserve(_globalDeclarations.size() + 1);
for (ASTPointer<Declaration const> const& variable: _globalDeclarations)
declarations.push_back(variable.get());
NameAndTypeResolver resolver(declarations);
resolver.registerDeclarations(*sourceUnit); resolver.registerDeclarations(*sourceUnit);
for (ASTPointer<ASTNode> const& node: sourceUnit->getNodes()) for (ASTPointer<ASTNode> const& node: sourceUnit->getNodes())
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get())) if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
{ {
@ -390,6 +396,21 @@ BOOST_AUTO_TEST_CASE(intermediately_overflowing_literals)
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
} }
BOOST_AUTO_TEST_CASE(blockhash)
{
char const* sourceCode = "contract test {\n"
" function f() {\n"
" block.blockhash(3);\n"
" }\n"
"}\n";
bytes code = compileFirstExpression(sourceCode, {}, {},
{make_shared<MagicVariableDeclaration>("block", make_shared<MagicType>(MagicType::Kind::BLOCK))});
bytes expectation({byte(eth::Instruction::PUSH1), 0x03,
byte(eth::Instruction::BLOCKHASH)});
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
} }

123
test/SolidityNameAndTypeResolution.cpp

@ -357,7 +357,6 @@ BOOST_AUTO_TEST_CASE(function_canonical_signature_type_aliases)
} }
} }
BOOST_AUTO_TEST_CASE(hash_collision_in_interface) BOOST_AUTO_TEST_CASE(hash_collision_in_interface)
{ {
char const* text = "contract test {\n" char const* text = "contract test {\n"
@ -369,6 +368,128 @@ BOOST_AUTO_TEST_CASE(hash_collision_in_interface)
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
} }
BOOST_AUTO_TEST_CASE(inheritance_basic)
{
char const* text = R"(
contract base { uint baseMember; struct BaseType { uint element; } }
contract derived is base {
BaseType data;
function f() { baseMember = 7; }
}
)";
BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text));
}
BOOST_AUTO_TEST_CASE(inheritance_diamond_basic)
{
char const* text = R"(
contract root { function rootFunction() {} }
contract inter1 is root { function f() {} }
contract inter2 is root { function f() {} }
contract derived is inter1, inter2, root {
function g() { f(); rootFunction(); }
}
)";
BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text));
}
BOOST_AUTO_TEST_CASE(cyclic_inheritance)
{
char const* text = R"(
contract A is B { }
contract B is A { }
)";
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
}
BOOST_AUTO_TEST_CASE(illegal_override_direct)
{
char const* text = R"(
contract B { function f() {} }
contract C is B { function f(uint i) {} }
)";
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
}
BOOST_AUTO_TEST_CASE(illegal_override_indirect)
{
char const* text = R"(
contract A { function f(uint a) {} }
contract B { function f() {} }
contract C is A, B { }
)";
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
}
BOOST_AUTO_TEST_CASE(complex_inheritance)
{
char const* text = R"(
contract A { function f() { uint8 x = C(0).g(); } }
contract B { function f() {} function g() returns (uint8 r) {} }
contract C is A, B { }
)";
BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text));
}
BOOST_AUTO_TEST_CASE(constructor_visibility)
{
// The constructor of a base class should not be visible in the derived class
char const* text = R"(
contract A { function A() { } }
contract B is A { function f() { A x = A(0); } }
)";
BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text));
}
BOOST_AUTO_TEST_CASE(overriding_constructor)
{
// It is fine to "override" constructor of a base class since it is invisible
char const* text = R"(
contract A { function A() { } }
contract B is A { function A() returns (uint8 r) {} }
)";
BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text));
}
BOOST_AUTO_TEST_CASE(missing_base_constructor_arguments)
{
char const* text = R"(
contract A { function A(uint a) { } }
contract B is A { }
)";
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
}
BOOST_AUTO_TEST_CASE(base_constructor_arguments_override)
{
char const* text = R"(
contract A { function A(uint a) { } }
contract B is A { }
)";
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
}
BOOST_AUTO_TEST_CASE(implicit_derived_to_base_conversion)
{
char const* text = R"(
contract A { }
contract B is A {
function f() { A a = B(1); }
}
)";
BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text));
}
BOOST_AUTO_TEST_CASE(implicit_base_to_derived_conversion)
{
char const* text = R"(
contract A { }
contract B is A {
function f() { B b = A(1); }
}
)";
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
} }

36
test/SolidityNatspecJSON.cpp

@ -45,13 +45,9 @@ public:
{ {
m_compilerStack.parse(_code); m_compilerStack.parse(_code);
} }
catch (const std::exception& e) catch(boost::exception const& _e)
{ {
std::string const* extra = boost::get_error_info<errinfo_comment>(e); auto msg = std::string("Parsing contract failed with: ") + boost::diagnostic_information(_e);
std::string msg = std::string("Parsing contract failed with: ") +
e.what() + std::string("\n");
if (extra)
msg += *extra;
BOOST_FAIL(msg); BOOST_FAIL(msg);
} }
@ -510,17 +506,35 @@ BOOST_AUTO_TEST_CASE(dev_title_at_function_error)
BOOST_CHECK_THROW(checkNatspec(sourceCode, natspec, false), DocstringParsingError); BOOST_CHECK_THROW(checkNatspec(sourceCode, natspec, false), DocstringParsingError);
} }
// test for bug where having no tags in docstring would cause infinite loop BOOST_AUTO_TEST_CASE(natspec_notice_without_tag)
BOOST_AUTO_TEST_CASE(natspec_no_tags)
{ {
char const* sourceCode = "contract test {\n" char const* sourceCode = "contract test {\n"
" /// I do something awesome\n" " /// I do something awesome\n"
" function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n" " function mul(uint a) returns(uint d) { return a * 7; }\n"
"}\n"; "}\n";
char const* natspec = "{\"methods\": {}}"; char const* natspec = "{"
"\"methods\":{"
" \"mul(uint256)\":{ \"notice\": \"I do something awesome\"}"
"}}";
checkNatspec(sourceCode, natspec, true);
}
checkNatspec(sourceCode, natspec, false); BOOST_AUTO_TEST_CASE(natspec_multiline_notice_without_tag)
{
char const* sourceCode = "contract test {\n"
" /// I do something awesome\n"
" /// which requires two lines to explain\n"
" function mul(uint a) returns(uint d) { return a * 7; }\n"
"}\n";
char const* natspec = "{"
"\"methods\":{"
" \"mul(uint256)\":{ \"notice\": \"I do something awesome which requires two lines to explain\"}"
"}}";
checkNatspec(sourceCode, natspec, true);
} }
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()

45
test/SolidityParser.cpp

@ -495,6 +495,51 @@ BOOST_AUTO_TEST_CASE(multiple_contracts_and_imports)
BOOST_CHECK_NO_THROW(parseText(text)); BOOST_CHECK_NO_THROW(parseText(text));
} }
BOOST_AUTO_TEST_CASE(contract_inheritance)
{
char const* text = "contract base {\n"
" function fun() {\n"
" uint64(2);\n"
" }\n"
"}\n"
"contract derived is base {\n"
" function fun() {\n"
" uint64(2);\n"
" }\n"
"}\n";
BOOST_CHECK_NO_THROW(parseText(text));
}
BOOST_AUTO_TEST_CASE(contract_multiple_inheritance)
{
char const* text = "contract base {\n"
" function fun() {\n"
" uint64(2);\n"
" }\n"
"}\n"
"contract derived is base, nonExisting {\n"
" function fun() {\n"
" uint64(2);\n"
" }\n"
"}\n";
BOOST_CHECK_NO_THROW(parseText(text));
}
BOOST_AUTO_TEST_CASE(contract_multiple_inheritance_with_arguments)
{
char const* text = "contract base {\n"
" function fun() {\n"
" uint64(2);\n"
" }\n"
"}\n"
"contract derived is base(2), nonExisting(\"abc\", \"def\", base.fun()) {\n"
" function fun() {\n"
" uint64(2);\n"
" }\n"
"}\n";
BOOST_CHECK_NO_THROW(parseText(text));
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
} }

12
test/solidityExecutionFramework.h

@ -46,7 +46,16 @@ public:
bytes const& compileAndRun(std::string const& _sourceCode, u256 const& _value = 0, std::string const& _contractName = "") bytes const& compileAndRun(std::string const& _sourceCode, u256 const& _value = 0, std::string const& _contractName = "")
{ {
dev::solidity::CompilerStack compiler; dev::solidity::CompilerStack compiler;
compiler.compile(_sourceCode, m_optimize); try
{
compiler.compile(_sourceCode, m_optimize);
}
catch(boost::exception const& _e)
{
auto msg = std::string("Compiling contract failed with: ") + boost::diagnostic_information(_e);
BOOST_FAIL(msg);
}
bytes code = compiler.getBytecode(_contractName); bytes code = compiler.getBytecode(_contractName);
sendMessage(code, true, _value); sendMessage(code, true, _value);
BOOST_REQUIRE(!m_output.empty()); BOOST_REQUIRE(!m_output.empty());
@ -97,6 +106,7 @@ public:
static bytes encode(char const* _value) { return encode(std::string(_value)); } static bytes encode(char const* _value) { return encode(std::string(_value)); }
static bytes encode(byte _value) { return bytes(31, 0) + bytes{_value}; } static bytes encode(byte _value) { return bytes(31, 0) + bytes{_value}; }
static bytes encode(u256 const& _value) { return toBigEndian(_value); } static bytes encode(u256 const& _value) { return toBigEndian(_value); }
static bytes encode(h256 const& _value) { return _value.asBytes(); }
static bytes encode(bytes const& _value, bool _padLeft = true) static bytes encode(bytes const& _value, bool _padLeft = true)
{ {
bytes padding = bytes((32 - _value.size() % 32) % 32, 0); bytes padding = bytes((32 - _value.size() % 32) % 32, 0);

Loading…
Cancel
Save