From 9ddf68b7f49b629d06962fbd3489479ac7178b97 Mon Sep 17 00:00:00 2001 From: yann300 Date: Tue, 16 Jun 2015 12:58:52 +0200 Subject: [PATCH 1/9] - array validation in QML - compliant with multi dim/dynamic array --- mix/ContractCallDataEncoder.cpp | 116 +++++++++++++++++++++++--------- mix/ContractCallDataEncoder.h | 5 +- mix/qml/js/InputValidator.js | 113 +++++++++++++++++++++++++++---- 3 files changed, 187 insertions(+), 47 deletions(-) diff --git a/mix/ContractCallDataEncoder.cpp b/mix/ContractCallDataEncoder.cpp index 07ab8dd73..08e764955 100644 --- a/mix/ContractCallDataEncoder.cpp +++ b/mix/ContractCallDataEncoder.cpp @@ -20,15 +20,19 @@ * Ethereum IDE client. */ -#include +#include #include #include +#include +#include +#include #include #include #include "QVariableDeclaration.h" #include "QVariableDefinition.h" #include "QFunctionDefinition.h" #include "ContractCallDataEncoder.h" +using namespace std; using namespace dev; using namespace dev::solidity; using namespace dev::mix; @@ -38,13 +42,18 @@ bytes ContractCallDataEncoder::encodedData() bytes r(m_encodedData); size_t headerSize = m_encodedData.size() & ~0x1fUL; //ignore any prefix that is not 32-byte aligned //apply offsets - for (auto const& p: m_offsetMap) + for (auto const& p: m_dynamicOffsetMap) + { + vector_ref offsetRef(m_dynamicData.data() + p.first, 32); + toBigEndian>(p.second + headerSize, offsetRef); //add header size minus signature hash + } + for (auto const& p: m_staticOffsetMap) { vector_ref offsetRef(r.data() + p.first, 32); toBigEndian>(p.second + headerSize, offsetRef); //add header size minus signature hash } - - r.insert(r.end(), m_dynamicData.begin(), m_dynamicData.end()); + if (m_dynamicData.size() > 0) + r.insert(r.end(), m_dynamicData.begin(), m_dynamicData.end()); return r; } @@ -54,54 +63,95 @@ void ContractCallDataEncoder::encode(QFunctionDefinition const* _function) m_encodedData.insert(m_encodedData.end(), hash.begin(), hash.end()); } +void ContractCallDataEncoder::encodeArray(QJsonArray const& _array, QList _dim, SolidityType const& _type, bytes& _content) +{ + size_t offsetStart = _content.size(); + if (_dim[0] == -1) + { + bytes count = bytes(32); + toBigEndian((u256)_array.size(), count); + _content += count; //reserved space for count + } + _dim.pop_front(); + + int k = 0; + for (QJsonValue const& c: _array) + { + if (c.isArray()) + { + if (_dim[0] == -1) + { + m_dynamicOffsetMap.push_back(std::make_pair(m_dynamicData.size() + offsetStart + 32 + k * 32, + m_dynamicData.size() + _content.size())); + } + encodeArray(c.toArray(), _dim, _type, _content); + } + else + { + // encode single item + if (c.isDouble()) + encodeSingleItem(QString::number(c.toDouble()), _type, _content); + else if (c.isString()) + encodeSingleItem(c.toString(), _type, _content); + } + k++; + } +} + void ContractCallDataEncoder::encode(QVariant const& _data, SolidityType const& _type) { - u256 count = 1; QStringList strList; + vector dim; if (_type.array) { - if (_data.type() == QVariant::String) - strList = _data.toString().split(",", QString::SkipEmptyParts); //TODO: proper parsing - else - strList = _data.toStringList(); - count = strList.count(); + QList dim = extractDimension(_type. name); + bytes content; + QJsonDocument jsonResponse = QJsonDocument::fromJson(_data.toString().toUtf8()); + QJsonArray jsonObject = jsonResponse.array(); + size_t size = m_encodedData.size(); + if (dim[0] == -1) + { + m_encodedData += bytes(32); // reserve space for offset + m_staticOffsetMap.push_back(std::make_pair(size, m_dynamicData.size())); + } + encodeArray(jsonObject, dim, _type, content); + if (!_type.dynamicSize) + m_encodedData.insert(m_encodedData.end(), content.begin(), content.end()); + else + m_dynamicData.insert(m_dynamicData.end(), content.begin(), content.end()); } - else - strList.append(_data.toString()); - - if (_type.dynamicSize) + else if (_type.dynamicSize && _type.type == SolidityType::Type::Bytes) { bytes empty(32); size_t sizePos = m_dynamicData.size(); m_dynamicData += empty; //reserve space for count - if (_type.type == SolidityType::Type::Bytes) - count = encodeSingleItem(_data.toString(), _type, m_dynamicData); - else - { - count = strList.count(); - for (auto const& item: strList) - encodeSingleItem(item, _type, m_dynamicData); - } + u256 count = encodeSingleItem(_data.toString(), _type, m_dynamicData); vector_ref sizeRef(m_dynamicData.data() + sizePos, 32); toBigEndian(count, sizeRef); - m_offsetMap.push_back(std::make_pair(m_encodedData.size(), sizePos)); + m_staticOffsetMap.push_back(std::make_pair(m_encodedData.size(), sizePos)); m_encodedData += empty; //reserve space for offset } else + encodeSingleItem(_data.toString(), _type, m_encodedData); +} + +QList ContractCallDataEncoder::extractDimension(QString const& _type) +{ + QList dim; + QRegExp dimExtract("(\\[[^\\]]*\\])"); + int pos = dimExtract.indexIn(_type); + while (pos != -1) { - if (_type.array) - count = _type.count; - int c = static_cast(count); - if (strList.size() > c) - strList.erase(strList.begin() + c, strList.end()); + QString d = dimExtract.cap(0); + pos += d.length(); + pos = dimExtract.indexIn(_type, pos); + if (d == "[]") + dim.push_front(-1); else - while (strList.size() < c) - strList.append(QString()); - - for (auto const& item: strList) - encodeSingleItem(item, _type, m_encodedData); + dim.push_front(d.replace("[", "").replace("]", "").toInt()); } + return dim; } unsigned ContractCallDataEncoder::encodeSingleItem(QString const& _data, SolidityType const& _type, bytes& _dest) diff --git a/mix/ContractCallDataEncoder.h b/mix/ContractCallDataEncoder.h index 2707845ae..629e46b0d 100644 --- a/mix/ContractCallDataEncoder.h +++ b/mix/ContractCallDataEncoder.h @@ -69,11 +69,14 @@ private: QString toString(bool _b); QString toString(dev::bytes const& _b); bool asString(dev::bytes const& _b, QString& _str); + QList extractDimension(QString const& _type); + void encodeArray(QJsonArray const& _array, QList _dim, SolidityType const& _type, bytes& _content); private: bytes m_encodedData; bytes m_dynamicData; - std::vector> m_offsetMap; + std::vector> m_dynamicOffsetMap; + std::vector> m_staticOffsetMap; }; } diff --git a/mix/qml/js/InputValidator.js b/mix/qml/js/InputValidator.js index 37185b898..8a8ef1715 100644 --- a/mix/qml/js/InputValidator.js +++ b/mix/qml/js/InputValidator.js @@ -1,27 +1,20 @@ Qt.include("QEtherHelper.js") var nbRegEx = new RegExp('^[0-9]+$'); +var arrayRegEx = new RegExp('\\[[^\\]]*\\]', "g"); +var capturenbRegEx = new RegExp("[0-9]+"); + function validate(model, values) { var inError = []; for (var k in model) { + init() if (values[model[k].name]) { var type = model[k].type.name; - var res; - if (isContractType(type)) - res = validateAddress(type, values[model[k].name]); - else if (type.indexOf("int") !== -1) - res = validateInt(type, values[model[k].name]); - else if (type.indexOf("bytes") !== -1) - res = validateBytes(type, values[model[k].name]); - else if (type.indexOf("bool") !== -1) - res = validateBool(type, values[model[k].name]); - else if (type.indexOf("address") !== -1) - res = validateAddress(type, values[model[k].name]); - else - res.valid = true; + var value = values[model[k].name]; + var res = check(type, value) if (!res.valid) inError.push({ type: type, value: values, message: res.message }); } @@ -29,6 +22,100 @@ function validate(model, values) return inError; } +function init() +{ + nbRegEx = new RegExp('^[0-9]+$'); + arrayRegEx = new RegExp('\\[[^\\]]*\\]', "g"); + capturenbRegEx = new RegExp("[0-9]+"); +} + +function check(type, value) +{ + var res = { valid: true, message : "" } + if (isContractType(type)) + res = validateAddress(type, value); + else if (isArray(type)) + res = validateArray(type, value); + else if (type.indexOf("int") !== -1) + res = validateInt(type, value); + else if (type.indexOf("bytes") !== -1) + res = validateBytes(type, value); + else if (type.indexOf("bool") !== -1) + res = validateBool(type, value); + else if (type.indexOf("address") !== -1) + res = validateAddress(type, value); + else + { + res.valid = true + res.message = "" + } + return res; +} + +function isArray(_type) +{ + if (!arrayRegEx.test(_type)) + return false + else + return (_type.indexOf("int") !== -1 || _type.indexOf("bytes") !== -1 || _type.indexOf("bool") !== -1 || _type.indexOf("adress") !== -1) +} + +function checkArrayRecursively(_type, _dim, _array) +{ + if (_array instanceof Array) + { + if (_dim.length === 0) + return { valid: false, message: "Your input contains too many dimensions" } + var size = -1 + var infinite = false + if (_dim === "[]") + infinite = true + else + size = parseInt(capturenbRegEx.exec(_dim[0])) + if (_array.length > size && !infinite) + return { valid: false, message: "Array size does not correspond. Should be " + _dim[0] } + if (_array.length > 0) + { + var _newdim = _dim.slice(0) + _newdim.splice(0, 1) + for (var i = 0; i < _array.length; i++) + { + var ret = checkArrayRecursively(_type, _newdim, _array[i]) + if (!ret.valid) + return ret + } + } + return { valid: true, message: "" } + } + else + { + if (_dim.length > 0) + return { valid: false, message: "Your input contains too few dimensions" } + if (typeof(_array) === "number") + _array = '' + _array + '' + return check(_type, _array) + } +} + +function validateArray(_type, _value) +{ + try + { + _value = JSON.parse(_value) + } + catch (e) + { + return { valid: false, message: "Input must be JSON formatted like [1,5,3,9] or [[4,9],[4,9],[4,9],[4,9]]" } + } + var dim = _type.match(arrayRegEx) + dim.reverse(); + for (var k = 0; k < dim.length; k++) + { + _type = _type.replace(dim[k], "") + } + return checkArrayRecursively(_type, dim, _value) +} + function isContractType(_type) { for (var k in Object.keys(codeModel.contracts)) From 0ea695c699c21affeca39cf33ae488a120400eda Mon Sep 17 00:00:00 2001 From: yann300 Date: Tue, 16 Jun 2015 13:41:05 +0200 Subject: [PATCH 2/9] fix for #2194 #2145 --- mix/ContractCallDataEncoder.cpp | 13 +++++++++---- mix/ContractCallDataEncoder.h | 1 + mix/qml/js/ProjectModel.js | 9 ++++++--- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/mix/ContractCallDataEncoder.cpp b/mix/ContractCallDataEncoder.cpp index 08e764955..bc3322145 100644 --- a/mix/ContractCallDataEncoder.cpp +++ b/mix/ContractCallDataEncoder.cpp @@ -248,12 +248,15 @@ dev::bytes ContractCallDataEncoder::decodeBytes(dev::bytes const& _rawValue) } QString ContractCallDataEncoder::toString(dev::bytes const& _b) +{ + return QString::fromStdString(dev::toJS(_b)); +} + +QString ContractCallDataEncoder::toChar(dev::bytes const& _b) { QString str; - if (asString(_b, str)) - return "\"" + str + "\" " + QString::fromStdString(dev::toJS(_b)); - else - return QString::fromStdString(dev::toJS(_b)); + asString(_b, str); + return str; } @@ -269,6 +272,8 @@ QVariant ContractCallDataEncoder::decode(SolidityType const& _type, bytes const& return QVariant::fromValue(toString(decodeBool(rawParam))); else if (type == QSolidityType::Type::Bytes || type == QSolidityType::Type::Hash) return QVariant::fromValue(toString(decodeBytes(rawParam))); + else if (type == QSolidityType::Type::String) + return QVariant::fromValue(toChar(decodeBytes(rawParam))); else if (type == QSolidityType::Type::Struct) return QVariant::fromValue(QString("struct")); //TODO else if (type == QSolidityType::Type::Address) diff --git a/mix/ContractCallDataEncoder.h b/mix/ContractCallDataEncoder.h index 629e46b0d..bec048c50 100644 --- a/mix/ContractCallDataEncoder.h +++ b/mix/ContractCallDataEncoder.h @@ -71,6 +71,7 @@ private: bool asString(dev::bytes const& _b, QString& _str); QList extractDimension(QString const& _type); void encodeArray(QJsonArray const& _array, QList _dim, SolidityType const& _type, bytes& _content); + QString toChar(dev::bytes const& _b); private: bytes m_encodedData; diff --git a/mix/qml/js/ProjectModel.js b/mix/qml/js/ProjectModel.js index 6fce7686d..f9c7a611c 100644 --- a/mix/qml/js/ProjectModel.js +++ b/mix/qml/js/ProjectModel.js @@ -252,8 +252,10 @@ function doCreateProject(title, path) { files: [ contractsFile, indexFile ] }; //TODO: copy from template - fileIo.writeFile(dirPath + indexFile, htmlTemplate); - fileIo.writeFile(dirPath + contractsFile, contractTemplate); + if (!fileIo.fileExists(dirPath + indexFile)) + fileIo.writeFile(dirPath + indexFile, htmlTemplate); + if (!fileIo.fileExists(dirPath + contractsFile)) + fileIo.writeFile(dirPath + contractsFile, contractTemplate); newProject(projectData); var json = JSON.stringify(projectData, null, "\t"); fileIo.writeFile(projectFile, json); @@ -340,7 +342,8 @@ function newContract() { function createAndAddFile(name, extension, content) { var fileName = generateFileName(name, extension); var filePath = projectPath + fileName; - fileIo.writeFile(filePath, content); + if (!fileIo.fileExists(filePath)) + fileIo.writeFile(filePath, content); var id = addFile(fileName); saveProjectFile(); documentAdded(id); From 63ad9620416f2d593db6e2b730fd5e2dfabf6355 Mon Sep 17 00:00:00 2001 From: yann300 Date: Tue, 16 Jun 2015 16:35:50 +0200 Subject: [PATCH 3/9] fix #2194 --- mix/ContractCallDataEncoder.cpp | 16 +++++++++++----- mix/qml/QStringTypeView.qml | 15 +++++++++++++-- mix/qml/js/InputValidator.js | 10 +++++++++- 3 files changed, 33 insertions(+), 8 deletions(-) diff --git a/mix/ContractCallDataEncoder.cpp b/mix/ContractCallDataEncoder.cpp index 8f5f3ecc7..e67ce9cea 100644 --- a/mix/ContractCallDataEncoder.cpp +++ b/mix/ContractCallDataEncoder.cpp @@ -80,10 +80,7 @@ void ContractCallDataEncoder::encodeArray(QJsonArray const& _array, QList _ if (c.isArray()) { if (_dim[0] == -1) - { - m_dynamicOffsetMap.push_back(std::make_pair(m_dynamicData.size() + offsetStart + 32 + k * 32, - m_dynamicData.size() + _content.size())); - } + m_dynamicOffsetMap.push_back(std::make_pair(m_dynamicData.size() + offsetStart + 32 + k * 32, m_dynamicData.size() + _content.size())); encodeArray(c.toArray(), _dim, _type, _content); } else @@ -183,6 +180,11 @@ unsigned ContractCallDataEncoder::encodeSingleItem(QString const& _data, Solidit catch (std::exception const&) { // manage input as a string. + QRegExp strExtract("\"(.*)\""); //check if contains both string and hex value, keep the string. + int i = strExtract.indexIn(src); + if (i != -1) + src = strExtract.cap(0); + src = src.replace("\"", ""); result = encodeStringParam(src, alignSize); } } @@ -249,7 +251,11 @@ dev::bytes ContractCallDataEncoder::decodeBytes(dev::bytes const& _rawValue) QString ContractCallDataEncoder::toString(dev::bytes const& _b) { - return QString::fromStdString(dev::toJS(_b)); + QString str; + if (asString(_b, str)) + return "\"" + str + "\" " + QString::fromStdString(dev::toJS(_b)); + else + return QString::fromStdString(dev::toJS(_b)); } QString ContractCallDataEncoder::toChar(dev::bytes const& _b) diff --git a/mix/qml/QStringTypeView.qml b/mix/qml/QStringTypeView.qml index 080c49282..101863421 100644 --- a/mix/qml/QStringTypeView.qml +++ b/mix/qml/QStringTypeView.qml @@ -2,11 +2,15 @@ import QtQuick 2.0 Item { - property alias value: textinput.text + property string value property alias readOnly: textinput.readOnly id: editRoot height: 20 width: readOnly ? textinput.implicitWidth : 150 + onValueChanged: + { + textinput.text = value + } SourceSansProBold { @@ -18,12 +22,19 @@ Item radius: 4 TextInput { id: textinput - text: value clip: true anchors.fill: parent wrapMode: Text.WrapAnywhere font.family: boldFont.name selectByMouse: true + onTextChanged: { + var stringRegEx = new RegExp('"^\\"*', "g") + var str = stringRegEx.exec(text) + if (str && str.length > 0) + value = str[0] + else + value = text + } } } } diff --git a/mix/qml/js/InputValidator.js b/mix/qml/js/InputValidator.js index 8a8ef1715..aa3b90150 100644 --- a/mix/qml/js/InputValidator.js +++ b/mix/qml/js/InputValidator.js @@ -187,7 +187,15 @@ function validateAddress(_type, _value) function validateBytes(_type, _value) { var ret = { valid: true, message: "" } - if (_value.length > parseInt(_type.replace("bytes", "")) ) + if (_value.indexOf("\"") === 0 && _value.indexOf("0x") !== -1) + { + //this is a different fomatting + var stringRegEx = new RegExp('".*"', "g"); + var matches = _value.match(stringRegEx); + if (matches.length === 1) + _value = matches[0] + } + if (_type !== "bytes" && _value.length > parseInt(_type.replace("bytes", "")) ) { ret.valid = false; ret.message = _type + " should not contains more than " + _type.replace("bytes", "") + " characters"; From af4d1b027ca349b8e14e48c3da131ed2600ffc20 Mon Sep 17 00:00:00 2001 From: yann300 Date: Tue, 16 Jun 2015 16:59:02 +0200 Subject: [PATCH 4/9] bux fixes --- mix/qml/js/InputValidator.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mix/qml/js/InputValidator.js b/mix/qml/js/InputValidator.js index aa3b90150..46b23274d 100644 --- a/mix/qml/js/InputValidator.js +++ b/mix/qml/js/InputValidator.js @@ -110,9 +110,8 @@ function validateArray(_type, _value) var dim = _type.match(arrayRegEx) dim.reverse(); for (var k = 0; k < dim.length; k++) - { _type = _type.replace(dim[k], "") - } + _type = _type.replace(/calldata/g, "") return checkArrayRecursively(_type, dim, _value) } From a2e06448e3db239d4dbca9d338fc8b7bd3c451ee Mon Sep 17 00:00:00 2001 From: yann300 Date: Wed, 17 Jun 2015 13:00:44 +0200 Subject: [PATCH 5/9] small changes --- mix/ContractCallDataEncoder.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mix/ContractCallDataEncoder.cpp b/mix/ContractCallDataEncoder.cpp index e67ce9cea..1b713b82b 100644 --- a/mix/ContractCallDataEncoder.cpp +++ b/mix/ContractCallDataEncoder.cpp @@ -45,12 +45,12 @@ bytes ContractCallDataEncoder::encodedData() for (auto const& p: m_dynamicOffsetMap) { vector_ref offsetRef(m_dynamicData.data() + p.first, 32); - toBigEndian>(p.second + headerSize, offsetRef); //add header size minus signature hash + toBigEndian(p.second + headerSize, offsetRef); //add header size minus signature hash } for (auto const& p: m_staticOffsetMap) { vector_ref offsetRef(r.data() + p.first, 32); - toBigEndian>(p.second + headerSize, offsetRef); //add header size minus signature hash + toBigEndian(p.second + headerSize, offsetRef); //add header size minus signature hash } if (m_dynamicData.size() > 0) r.insert(r.end(), m_dynamicData.begin(), m_dynamicData.end()); From 52bc8a6c5ca8e70061feaa060e85269322f30aeb Mon Sep 17 00:00:00 2001 From: yann300 Date: Wed, 17 Jun 2015 14:05:40 +0200 Subject: [PATCH 6/9] small changes --- mix/ContractCallDataEncoder.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/mix/ContractCallDataEncoder.cpp b/mix/ContractCallDataEncoder.cpp index 1b713b82b..a1aae3f79 100644 --- a/mix/ContractCallDataEncoder.cpp +++ b/mix/ContractCallDataEncoder.cpp @@ -98,7 +98,6 @@ void ContractCallDataEncoder::encodeArray(QJsonArray const& _array, QList _ void ContractCallDataEncoder::encode(QVariant const& _data, SolidityType const& _type) { QStringList strList; - vector dim; if (_type.array) { QList dim = extractDimension(_type. name); From cbe3f80d6f28a1af1a8c8fed52a9d2d492153e4e Mon Sep 17 00:00:00 2001 From: yann300 Date: Wed, 17 Jun 2015 16:23:28 +0200 Subject: [PATCH 7/9] - bug fix with encoding bytes and string. - bug fix when using String type QML control - bytes parameter: stop trying to get ASCII value (as String as been reimported) --- mix/ContractCallDataEncoder.cpp | 36 +++++++++++++-------------------- mix/QVariableDeclaration.h | 1 + mix/qml/QIntTypeView.qml | 3 --- mix/qml/QStringTypeView.qml | 33 +++++++++++++----------------- mix/qml/StructView.qml | 2 +- mix/qml/js/InputValidator.js | 19 ++++------------- 6 files changed, 34 insertions(+), 60 deletions(-) diff --git a/mix/ContractCallDataEncoder.cpp b/mix/ContractCallDataEncoder.cpp index a1aae3f79..3c635b8b0 100644 --- a/mix/ContractCallDataEncoder.cpp +++ b/mix/ContractCallDataEncoder.cpp @@ -97,37 +97,34 @@ void ContractCallDataEncoder::encodeArray(QJsonArray const& _array, QList _ void ContractCallDataEncoder::encode(QVariant const& _data, SolidityType const& _type) { - QStringList strList; - if (_type.array) + if (_type.dynamicSize && (_type.type == SolidityType::Type::Bytes || _type.type == SolidityType::Type::String)) + { + bytes empty(32); + size_t sizePos = m_dynamicData.size(); + m_dynamicData += empty; //reserve space for count + u256 count = encodeSingleItem(_data.toString(), _type, m_dynamicData); + vector_ref sizeRef(m_dynamicData.data() + sizePos, 32); + toBigEndian(count, sizeRef); + m_staticOffsetMap.push_back(std::make_pair(m_encodedData.size(), sizePos)); + m_encodedData += empty; //reserve space for offset + } + else if (_type.array) { QList dim = extractDimension(_type. name); bytes content; - QJsonDocument jsonResponse = QJsonDocument::fromJson(_data.toString().toUtf8()); - QJsonArray jsonObject = jsonResponse.array(); size_t size = m_encodedData.size(); - if (dim[0] == -1) + if (dim.front() == -1) { m_encodedData += bytes(32); // reserve space for offset m_staticOffsetMap.push_back(std::make_pair(size, m_dynamicData.size())); } - encodeArray(jsonObject, dim, _type, content); + encodeArray(_data.toJsonArray(), dim, _type, content); if (!_type.dynamicSize) m_encodedData.insert(m_encodedData.end(), content.begin(), content.end()); else m_dynamicData.insert(m_dynamicData.end(), content.begin(), content.end()); } - else if (_type.dynamicSize && _type.type == SolidityType::Type::Bytes) - { - bytes empty(32); - size_t sizePos = m_dynamicData.size(); - m_dynamicData += empty; //reserve space for count - u256 count = encodeSingleItem(_data.toString(), _type, m_dynamicData); - vector_ref sizeRef(m_dynamicData.data() + sizePos, 32); - toBigEndian(count, sizeRef); - m_staticOffsetMap.push_back(std::make_pair(m_encodedData.size(), sizePos)); - m_encodedData += empty; //reserve space for offset - } else encodeSingleItem(_data.toString(), _type, m_encodedData); } @@ -179,11 +176,6 @@ unsigned ContractCallDataEncoder::encodeSingleItem(QString const& _data, Solidit catch (std::exception const&) { // manage input as a string. - QRegExp strExtract("\"(.*)\""); //check if contains both string and hex value, keep the string. - int i = strExtract.indexIn(src); - if (i != -1) - src = strExtract.cap(0); - src = src.replace("\"", ""); result = encodeStringParam(src, alignSize); } } diff --git a/mix/QVariableDeclaration.h b/mix/QVariableDeclaration.h index 85c719987..583847b44 100644 --- a/mix/QVariableDeclaration.h +++ b/mix/QVariableDeclaration.h @@ -60,6 +60,7 @@ public: Bool, Address, Bytes, + String, Enum, Struct }; diff --git a/mix/qml/QIntTypeView.qml b/mix/qml/QIntTypeView.qml index c42e65654..ec54d6409 100644 --- a/mix/qml/QIntTypeView.qml +++ b/mix/qml/QIntTypeView.qml @@ -33,6 +33,3 @@ Item } } } - - - diff --git a/mix/qml/QStringTypeView.qml b/mix/qml/QStringTypeView.qml index 101863421..c42e65654 100644 --- a/mix/qml/QStringTypeView.qml +++ b/mix/qml/QStringTypeView.qml @@ -2,38 +2,33 @@ import QtQuick 2.0 Item { - property string value + property alias value: textinput.text property alias readOnly: textinput.readOnly id: editRoot - height: 20 width: readOnly ? textinput.implicitWidth : 150 - onValueChanged: - { - textinput.text = value - } - SourceSansProBold - { - id: boldFont + DebuggerPaneStyle { + id: dbgStyle } Rectangle { anchors.fill: parent radius: 4 TextInput { + anchors.verticalCenter: parent.verticalCenter id: textinput + font.family: dbgStyle.general.basicFont clip: true - anchors.fill: parent - wrapMode: Text.WrapAnywhere - font.family: boldFont.name selectByMouse: true - onTextChanged: { - var stringRegEx = new RegExp('"^\\"*', "g") - var str = stringRegEx.exec(text) - if (str && str.length > 0) - value = str[0] - else - value = text + text: value + anchors.fill: parent + font.pointSize: dbgStyle.general.basicFontSize + color: dbgStyle.general.basicColor + MouseArea { + id: mouseArea + anchors.fill: parent + hoverEnabled: true + onClicked: textinput.forceActiveFocus() } } } diff --git a/mix/qml/StructView.qml b/mix/qml/StructView.qml index 880f22057..6b9edc755 100644 --- a/mix/qml/StructView.qml +++ b/mix/qml/StructView.qml @@ -72,7 +72,7 @@ Column return Qt.createComponent("qrc:/qml/QIntTypeView.qml"); else if (t === QSolidityType.Bool) return Qt.createComponent("qrc:/qml/QBoolTypeView.qml"); - else if (t === QSolidityType.Bytes) + else if (t === QSolidityType.Bytes || t === QSolidityType.String) return Qt.createComponent("qrc:/qml/QStringTypeView.qml"); else if (t === QSolidityType.Hash) return Qt.createComponent("qrc:/qml/QHashTypeView.qml"); diff --git a/mix/qml/js/InputValidator.js b/mix/qml/js/InputValidator.js index 46b23274d..7190ae291 100644 --- a/mix/qml/js/InputValidator.js +++ b/mix/qml/js/InputValidator.js @@ -1,8 +1,8 @@ Qt.include("QEtherHelper.js") -var nbRegEx = new RegExp('^[0-9]+$'); -var arrayRegEx = new RegExp('\\[[^\\]]*\\]', "g"); -var capturenbRegEx = new RegExp("[0-9]+"); +var nbRegEx; +var arrayRegEx; +var capturenbRegEx; function validate(model, values) { @@ -54,10 +54,7 @@ function check(type, value) function isArray(_type) { - if (!arrayRegEx.test(_type)) - return false - else - return (_type.indexOf("int") !== -1 || _type.indexOf("bytes") !== -1 || _type.indexOf("bool") !== -1 || _type.indexOf("adress") !== -1) + return arrayRegEx.test(_type); } function checkArrayRecursively(_type, _dim, _array) @@ -186,14 +183,6 @@ function validateAddress(_type, _value) function validateBytes(_type, _value) { var ret = { valid: true, message: "" } - if (_value.indexOf("\"") === 0 && _value.indexOf("0x") !== -1) - { - //this is a different fomatting - var stringRegEx = new RegExp('".*"', "g"); - var matches = _value.match(stringRegEx); - if (matches.length === 1) - _value = matches[0] - } if (_type !== "bytes" && _value.length > parseInt(_type.replace("bytes", "")) ) { ret.valid = false; From eac6ed820b573628d715f846cfe725498c8c1bc4 Mon Sep 17 00:00:00 2001 From: yann300 Date: Wed, 17 Jun 2015 16:45:47 +0200 Subject: [PATCH 8/9] hide extractDimension from .h --- mix/ContractCallDataEncoder.cpp | 39 +++++++++++++++++---------------- mix/ContractCallDataEncoder.h | 1 - 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/mix/ContractCallDataEncoder.cpp b/mix/ContractCallDataEncoder.cpp index 3c635b8b0..62cc39a04 100644 --- a/mix/ContractCallDataEncoder.cpp +++ b/mix/ContractCallDataEncoder.cpp @@ -37,6 +37,24 @@ using namespace dev; using namespace dev::solidity; using namespace dev::mix; +static QList extractDimension(QString const& _type) +{ + QList dim; + QRegExp dimExtract("(\\[[^\\]]*\\])"); + int pos = dimExtract.indexIn(_type); + while (pos != -1) + { + QString d = dimExtract.cap(0); + pos += d.length(); + pos = dimExtract.indexIn(_type, pos); + if (d == "[]") + dim.push_front(-1); + else + dim.push_front(d.replace("[", "").replace("]", "").toInt()); + } + return dim; +} + bytes ContractCallDataEncoder::encodedData() { bytes r(m_encodedData); @@ -118,7 +136,8 @@ void ContractCallDataEncoder::encode(QVariant const& _data, SolidityType const& m_encodedData += bytes(32); // reserve space for offset m_staticOffsetMap.push_back(std::make_pair(size, m_dynamicData.size())); } - encodeArray(_data.toJsonArray(), dim, _type, content); + QJsonDocument jsonDoc = QJsonDocument::fromJson(_data.toString().toUtf8()); + encodeArray(jsonDoc.array(), dim, _type, content); if (!_type.dynamicSize) m_encodedData.insert(m_encodedData.end(), content.begin(), content.end()); @@ -129,24 +148,6 @@ void ContractCallDataEncoder::encode(QVariant const& _data, SolidityType const& encodeSingleItem(_data.toString(), _type, m_encodedData); } -QList ContractCallDataEncoder::extractDimension(QString const& _type) -{ - QList dim; - QRegExp dimExtract("(\\[[^\\]]*\\])"); - int pos = dimExtract.indexIn(_type); - while (pos != -1) - { - QString d = dimExtract.cap(0); - pos += d.length(); - pos = dimExtract.indexIn(_type, pos); - if (d == "[]") - dim.push_front(-1); - else - dim.push_front(d.replace("[", "").replace("]", "").toInt()); - } - return dim; -} - unsigned ContractCallDataEncoder::encodeSingleItem(QString const& _data, SolidityType const& _type, bytes& _dest) { if (_type.type == SolidityType::Type::Struct) diff --git a/mix/ContractCallDataEncoder.h b/mix/ContractCallDataEncoder.h index 867e75751..e29cb92e0 100644 --- a/mix/ContractCallDataEncoder.h +++ b/mix/ContractCallDataEncoder.h @@ -71,7 +71,6 @@ private: QString toString(bool _b); QString toString(dev::bytes const& _b); bool asString(dev::bytes const& _b, QString& _str); - QList extractDimension(QString const& _type); void encodeArray(QJsonArray const& _array, QList _dim, SolidityType const& _type, bytes& _content); QString toChar(dev::bytes const& _b); From e1f4c2e0e57ef599798005a04f176bcc2bb562eb Mon Sep 17 00:00:00 2001 From: yann300 Date: Thu, 18 Jun 2015 23:29:44 +0200 Subject: [PATCH 9/9] use baseType to encode arrays --- mix/CodeModel.cpp | 12 +++++++++++- mix/CodeModel.h | 2 ++ mix/ContractCallDataEncoder.cpp | 32 ++++++-------------------------- mix/ContractCallDataEncoder.h | 2 +- mix/SolidityType.h | 1 + 5 files changed, 21 insertions(+), 28 deletions(-) diff --git a/mix/CodeModel.cpp b/mix/CodeModel.cpp index 1ca5d9160..8cb901a39 100644 --- a/mix/CodeModel.cpp +++ b/mix/CodeModel.cpp @@ -493,9 +493,18 @@ dev::bytes const& CodeModel::getStdContractCode(const QString& _contractName, co return m_compiledContracts.at(_contractName); } +void CodeModel::retrieveSubType(SolidityType& _wrapperType, dev::solidity::Type const* _type) +{ + if (_type->getCategory() == Type::Category::Array) + { + ArrayType const* arrayType = dynamic_cast(_type); + _wrapperType.baseType = std::make_shared(nodeType(arrayType->getBaseType().get())); + } +} + SolidityType CodeModel::nodeType(dev::solidity::Type const* _type) { - SolidityType r { SolidityType::Type::UnsignedInteger, 32, 1, false, false, QString::fromStdString(_type->toString()), std::vector(), std::vector() }; + SolidityType r { SolidityType::Type::UnsignedInteger, 32, 1, false, false, QString::fromStdString(_type->toString()), std::vector(), std::vector(), nullptr }; if (!_type) return r; switch (_type->getCategory()) @@ -536,6 +545,7 @@ SolidityType CodeModel::nodeType(dev::solidity::Type const* _type) r.count = static_cast(array->getLength()); r.dynamicSize = _type->isDynamicallySized(); r.array = true; + retrieveSubType(r, _type); } break; case Type::Category::Enum: diff --git a/mix/CodeModel.h b/mix/CodeModel.h index b9d06341d..7841c7142 100644 --- a/mix/CodeModel.h +++ b/mix/CodeModel.h @@ -224,6 +224,8 @@ public: Q_INVOKABLE void unregisterContractSrc(QString const& _documentId); /// Convert solidity type info to mix type static SolidityType nodeType(dev::solidity::Type const* _type); + /// Retrieve subtype + static void retrieveSubType(SolidityType& _wrapperType, dev::solidity::Type const* _type); /// Check if given location belongs to contract or function bool isContractOrFunctionLocation(dev::SourceLocation const& _location); /// Get funciton name by location diff --git a/mix/ContractCallDataEncoder.cpp b/mix/ContractCallDataEncoder.cpp index 62cc39a04..da21869e4 100644 --- a/mix/ContractCallDataEncoder.cpp +++ b/mix/ContractCallDataEncoder.cpp @@ -37,24 +37,6 @@ using namespace dev; using namespace dev::solidity; using namespace dev::mix; -static QList extractDimension(QString const& _type) -{ - QList dim; - QRegExp dimExtract("(\\[[^\\]]*\\])"); - int pos = dimExtract.indexIn(_type); - while (pos != -1) - { - QString d = dimExtract.cap(0); - pos += d.length(); - pos = dimExtract.indexIn(_type, pos); - if (d == "[]") - dim.push_front(-1); - else - dim.push_front(d.replace("[", "").replace("]", "").toInt()); - } - return dim; -} - bytes ContractCallDataEncoder::encodedData() { bytes r(m_encodedData); @@ -81,25 +63,24 @@ void ContractCallDataEncoder::encode(QFunctionDefinition const* _function) m_encodedData.insert(m_encodedData.end(), hash.begin(), hash.end()); } -void ContractCallDataEncoder::encodeArray(QJsonArray const& _array, QList _dim, SolidityType const& _type, bytes& _content) +void ContractCallDataEncoder::encodeArray(QJsonArray const& _array, SolidityType const& _type, bytes& _content) { size_t offsetStart = _content.size(); - if (_dim[0] == -1) + if (_type.dynamicSize) { bytes count = bytes(32); toBigEndian((u256)_array.size(), count); _content += count; //reserved space for count } - _dim.pop_front(); int k = 0; for (QJsonValue const& c: _array) { if (c.isArray()) { - if (_dim[0] == -1) + if (_type.baseType->dynamicSize) m_dynamicOffsetMap.push_back(std::make_pair(m_dynamicData.size() + offsetStart + 32 + k * 32, m_dynamicData.size() + _content.size())); - encodeArray(c.toArray(), _dim, _type, _content); + encodeArray(c.toArray(), *_type.baseType, _content); } else { @@ -128,16 +109,15 @@ void ContractCallDataEncoder::encode(QVariant const& _data, SolidityType const& } else if (_type.array) { - QList dim = extractDimension(_type. name); bytes content; size_t size = m_encodedData.size(); - if (dim.front() == -1) + if (_type.dynamicSize) { m_encodedData += bytes(32); // reserve space for offset m_staticOffsetMap.push_back(std::make_pair(size, m_dynamicData.size())); } QJsonDocument jsonDoc = QJsonDocument::fromJson(_data.toString().toUtf8()); - encodeArray(jsonDoc.array(), dim, _type, content); + encodeArray(jsonDoc.array(), _type, content); if (!_type.dynamicSize) m_encodedData.insert(m_encodedData.end(), content.begin(), content.end()); diff --git a/mix/ContractCallDataEncoder.h b/mix/ContractCallDataEncoder.h index e29cb92e0..be11a5772 100644 --- a/mix/ContractCallDataEncoder.h +++ b/mix/ContractCallDataEncoder.h @@ -71,7 +71,7 @@ private: QString toString(bool _b); QString toString(dev::bytes const& _b); bool asString(dev::bytes const& _b, QString& _str); - void encodeArray(QJsonArray const& _array, QList _dim, SolidityType const& _type, bytes& _content); + void encodeArray(QJsonArray const& _array, SolidityType const& _type, bytes& _content); QString toChar(dev::bytes const& _b); private: diff --git a/mix/SolidityType.h b/mix/SolidityType.h index 75f47e7fa..13f5c2ecd 100644 --- a/mix/SolidityType.h +++ b/mix/SolidityType.h @@ -57,6 +57,7 @@ struct SolidityType QString name; std::vector members; //for struct std::vector enumNames; //for enum + std::shared_ptr baseType; }; struct SolidityDeclaration