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. 5
      libjsqrc/ethereumjs/bower.json
  6. 858
      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. 336
      libjsqrc/ethereumjs/lib/abi.js
  14. 18
      libjsqrc/ethereumjs/lib/autoprovider.js
  15. 27
      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. 185
      libjsqrc/ethereumjs/lib/web3.js
  21. 20
      libjsqrc/ethereumjs/lib/websocket.js
  22. 5
      libjsqrc/ethereumjs/package.json
  23. 812
      libjsqrc/ethereumjs/test/abi.parsers.js
  24. 2
      libjsqrc/ethereumjs/test/db.methods.js
  25. 4
      libjsqrc/ethereumjs/test/eth.methods.js
  26. 2
      libjsqrc/ethereumjs/test/mocha.opts
  27. 2
      libjsqrc/ethereumjs/test/shh.methods.js
  28. 4
      libjsqrc/ethereumjs/test/utils.js
  29. 2
      libjsqrc/ethereumjs/test/web3.methods.js
  30. 1
      libjsqrc/js.qrc
  31. 1
      libqwebthree/QWebThree.h
  32. 65
      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. 93
      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. 126
      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. 20
      mix/DebuggingStateWrapper.cpp
  61. 15
      mix/DebuggingStateWrapper.h
  62. 31
      mix/Exceptions.cpp
  63. 45
      mix/Exceptions.h
  64. 1
      mix/FileIo.cpp
  65. 5
      mix/MixApplication.cpp
  66. 59
      mix/QBigInt.cpp
  67. 102
      mix/QBigInt.h
  68. 55
      mix/QEther.cpp
  69. 92
      mix/QEther.h
  70. 13
      mix/main.cpp
  71. 3
      mix/qml.qrc
  72. 13
      mix/qml/BigIntValue.qml
  73. 3
      mix/qml/CodeEditorView.qml
  74. 115
      mix/qml/Ether.qml
  75. 15
      mix/qml/EtherValue.qml
  76. 17
      mix/qml/ProjectList.qml
  77. 4
      mix/qml/ProjectModel.qml
  78. 25
      mix/qml/StateDialog.qml
  79. 12
      mix/qml/StateList.qml
  80. 48
      mix/qml/TransactionDialog.qml
  81. 6
      mix/qml/js/Debugger.js
  82. 29
      mix/qml/main.qml
  83. 8
      test/SolidityABIJSON.cpp
  84. 2
      test/SolidityCompiler.cpp
  85. 224
      test/SolidityEndToEndTest.cpp
  86. 25
      test/SolidityExpressionCompiler.cpp
  87. 123
      test/SolidityNameAndTypeResolution.cpp
  88. 36
      test/SolidityNatspecJSON.cpp
  89. 45
      test/SolidityParser.cpp
  90. 10
      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
``` ```

5
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",

858
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;

336
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
/// If the value is floating point, round it down
/// @returns right-aligned byte representation of int
var formatInputInt = function (value) {
var padding = ETH_PADDING * 2;
if (value instanceof BigNumber || typeof value === 'number') {
if (typeof value === 'number')
value = new BigNumber(value);
value = value.round();
// convert from int, decimal-string, prefixed hex string whatever into a bare hex string. if (value.lessThan(0))
var formatStandard = function (value) { value = new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16).plus(value).plus(1);
if (typeof value === "number") value = value.toString(16);
return value.toString(16); }
else if (typeof value === "string" && value.indexOf('0x') === 0) else if (value.indexOf('0x') === 0)
return value.substr(2); value = value.substr(2);
// else if (typeof value === "string") else if (typeof value === 'string')
// return web3.toHex(value); value = formatInputInt(new BigNumber(value));
else else
return (+value).toString(16); value = (+value).toString(16);
return padLeft(value, padding);
}; };
var prefixedType = function (prefix, calcPadding) { /// Formats input value to byte representation of string
return function (type, value) { /// @returns left-algined byte representation of string
var expected = prefix; var formatInputString = function (value) {
if (type.indexOf(expected) !== 0) { return web3.fromAscii(value, ETH_PADDING).substr(2);
return false;
}
var padding = calcPadding(type, expected);
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) {
return function (type, value) {
if (type !== name) {
return false;
}
padding = 32; //override as per the new ABI. /// Formats input value to byte representation of bool
/// @returns right-aligned byte representation bool
return padLeft(formatter ? formatter(value) : value, padding * 2); var formatInputBool = function (value) {
return '000000000000000000000000000000000000000000000000000000000000000' + (value ? '1' : '0');
}; };
/// 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;
for (var i = 0; i < method.inputs.length; i++) { /// first we iterate in search for dynamic
var found = false; method.inputs.forEach(function (input, index) {
for (var j = 0; j < inputTypes.length && !found; j++) { bytes += dynamicTypeBytes(input.type, params[index]);
found = inputTypes[j](method.inputs[i].type, params[i]); });
}
if (!found) { method.inputs.forEach(function (input, i) {
console.error('unsupported json type: ' + method.inputs[i].type); var typeMatch = false;
for (var j = 0; j < inputTypes.length && !typeMatch; j++) {
typeMatch = inputTypes[j].type(method.inputs[i].type, params[i]);
} }
bytes += found; if (!typeMatch) {
console.error('input parser does not support type: ' + method.inputs[i].type);
} }
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);
if (padding > 32)
return -1; // not allowed to be so big.
padding = 32; // override as per the new ABI.
return padding * 2;
};
}; };
var namedType = function (name, padding) { /// Formats big right-aligned input bytes to uint
return function (type) { /// @returns right-aligned input bytes formatted to uint
padding = 32; // override as per the new ABI. var formatOutputUInt = function (value) {
return name === type ? padding * 2 : -1; return new BigNumber(value, 16);
}; };
/// @returns input bytes formatted to real
var formatOutputReal = function (value) {
return formatOutputInt(value).dividedBy(new BigNumber(2).pow(128));
}; };
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
var formatOutputHash = function (value) {
return "0x" + 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
var formatOutputString = function (value) {
return web3.toAscii(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)) {
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); 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) {

27
libjsqrc/ethereumjs/lib/contract.js

@ -20,16 +20,32 @@
* @date 2014 * @date 2014
*/ */
// TODO: is these line is supposed to be here?
if (process.env.NODE_ENV !== 'build') {
var web3 = require('./web3'); // jshint ignore:line 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);

185
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() {
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(); setupMethods(shhWatch, shhWatchMethods());
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;

20
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;
@ -53,6 +61,11 @@ var WebSocketProvider = function(host) {
}; };
}; };
/// 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);
@ -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",

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

@ -1,14 +1,11 @@
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)); };
describe('abi', function() { var description = [{
describe('inputParser', function() { "name": "test",
it('should parse ...', function() { "inputs": [{
var desc = [{
"name": "multiply",
"inputs": [
{
"name": "a", "name": "a",
"type": "uint256" "type": "uint256"
} }
@ -21,17 +18,810 @@ describe('abi', function() {
] ]
}]; }];
var iParser = abi.inputParser(desc); describe('abi', function() {
assert.equal(iParser.multiply(1), "0x000000000000000000000000000000000000000000000000000000000000000001"); describe('inputParser', function() {
it('should parse input uint', function() {
// given
var d = clone(description);
d[0].inputs = [
{ type: "uint" }
];
// 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 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" }]
}];
// when
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
);
});
}); });
}); });

2
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');
}); });
}); });
});

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

@ -7,7 +7,6 @@ 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');
@ -23,9 +22,7 @@ describe('web3', function() {
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');
@ -37,6 +34,5 @@ describe('web3', function() {
u.propertyExists(web3.eth, 'number'); u.propertyExists(web3.eth, 'number');
}); });
}); });
});

2
libjsqrc/ethereumjs/test/mocha.opts

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

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

@ -7,7 +7,6 @@ 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');
@ -15,5 +14,4 @@ describe('web3', function() {
u.methodExists(web3.shh, 'addToGroup'); u.methodExists(web3.shh, 'addToGroup');
}); });
}); });
});

4
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) {
it('should have method ' + method + ' implemented', function() {
assert.equal('function', typeof object[method], 'method ' + method + ' is not implemented'); assert.equal('function', typeof object[method], 'method ' + method + ' is not implemented');
});
}; };
var propertyExists = function (object, property) { var propertyExists = function (object, property) {
it('should have property ' + property + ' implemented', function() {
assert.equal('object', typeof object[property], 'property ' + property + ' is not implemented'); assert.equal('object', typeof object[property], 'property ' + property + ' is not implemented');
});
}; };
module.exports = { module.exports = {

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

@ -6,7 +6,6 @@ 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');
@ -14,5 +13,4 @@ describe('web3', function() {
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")); \
} }

65
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())
{
if (function->getName() == contract->getName())
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."));
}
}
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())); FixedHash<4> hash(dev::sha3(f->getCanonicalSignature()));
exportedFunctions.push_back(make_pair(hash, f.get())); m_interfaceFunctionList->push_back(make_pair(hash, f.get()));
}
}
return *m_interfaceFunctionList;
} }
return exportedFunctions; 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))
{ {
m_functionsSeen.insert(&_function); _node.accept(*this);
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;

93
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())
if (function->getName() != contract->getName()) // don't add the constructor here
m_context.addFunction(*function); 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())
if (function->getName() != contract->getName()) // don't add the constructor here
function->accept(*this); 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,8 +246,8 @@ 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);
} }

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;

126
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,21 +465,23 @@ 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)) else if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(declaration))
{ m_context << m_context.getVirtualFunctionEntryLabel(*functionDef).pushTag();
m_context << m_context.getFunctionEntryLabel(*functionDef).pushTag(); else if (dynamic_cast<VariableDeclaration const*>(declaration))
return;
}
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;
} }
else if (dynamic_cast<ContractDefinition const*>(declaration))
{
// no-op
}
else
{
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Identifier type not expected in expression context.")); 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,13 +793,13 @@ 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);
} }

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()

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();
};
}
}

13
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[])
{
try
{ {
MixApplication app(_argc, _argv); MixApplication app(_argc, _argv);
return app.exec(); 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, false); checkNatspec(sourceCode, natspec, true);
}
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()
} }

10
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;
try
{
compiler.compile(_sourceCode, m_optimize); 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