import * as Blockly from "blockly/core" /** * @fileoverview Helper functions for generating Solidity for blocks. * @author jeanmarc.leroux@google.com (Jean-Marc Le Roux) * @author rekmarks@icloud.com (Erik Marks) */ // 'use strict'; // goog.provide('Blockly.Solidity'); // goog.require('Blockly.Generator'); /** * Solidity code generator. * @type {!Blockly.Generator} */ Blockly.Solidity = new Blockly.Generator('Solidity'); Blockly.Solidity.LABEL_GROUP_STATE = "state"; Blockly.Solidity.LABEL_GROUP_PARAMETER = "parameter"; Blockly.Solidity.LABEL_GROUP_VARIABLE = "variable"; Blockly.Solidity.LABEL_GROUP_METHOD = "method"; Blockly.Solidity.UNDEFINED_NAME = "__UNDEFINED__"; /** * List of illegal variable names. * This is not intended to be a security feature. Blockly is 100% client-side, * so bypassing this list is trivial. This is intended to prevent users from * accidentally clobbering a built-in object or function. * @private */ Blockly.Solidity.addReservedWords( 'Blockly,' + // In case JS is evaled in the current window. 'abstract, after, case, catch, default, final, in, inline, let, match,' + 'null, of, relocatable, static, switch, try, type, typeof' + "function,true,false,bool,int8,int,uint8,", "uint,address,bytes1,contract,constructor,uint8,", "override,virtual,indexed,anonymous,immutable,constant,payable,view,pure,", "public,private,external,internal,abi,bytes,block,gasleft,msg,tx,assert,require,", "revert,blockhash,keccak256,sha256,ripemd160,ecrecover,addmod,mulmod,this,", "super,selfdestruct,type" ); /** * Order of operation ENUMs. * https://solidity.readthedocs.io/en/develop/miscellaneous.html#order * TODO: can this be further minimized? */ Blockly.Solidity.ORDER_ATOMIC = 0; // 0 "" ... // TODO: postfix increment/decrement // https://stackoverflow.com/questions/7031326/what-is-the-difference-between-prefix-and-postfix-operators#7031409 Blockly.Solidity.ORDER_NEW = 1.1; // new Blockly.Solidity.ORDER_MEMBER = 1.2; // . [] Blockly.Solidity.ORDER_FUNCTION_CALL = 1.3; // () Blockly.Solidity.ORDER_PARENTHESES = 1.4; // () // TODO: "Parentheses" ought to be 1.X Blockly.Solidity.ORDER_INCREMENT = 2.1; // ++ (prefix) Blockly.Solidity.ORDER_DECREMENT = 2.2; // -- (prefix) Blockly.Solidity.ORDER_UNARY_PLUS = 2.3; // + Blockly.Solidity.ORDER_UNARY_NEGATION = 2.4; // - Blockly.Solidity.ORDER_TYPEOF = 2.5; // typeof Blockly.Solidity.ORDER_VOID = 2.6; // void Blockly.Solidity.ORDER_DELETE = 2.7; // delete Blockly.Solidity.ORDER_LOGICAL_NOT = 2.8; // ! Blockly.Solidity.ORDER_BITWISE_NOT = 2.9; // ~ Blockly.Solidity.ORDER_EXPONENTATION = 3; // ** Blockly.Solidity.ORDER_MULTIPLICATION = 4.1; // * Blockly.Solidity.ORDER_DIVISION = 4.2; // / Blockly.Solidity.ORDER_MODULUS = 4.3; // % Blockly.Solidity.ORDER_SUBTRACTION = 5.1; // - Blockly.Solidity.ORDER_ADDITION = 5.2; // + Blockly.Solidity.ORDER_BITWISE_SHIFT = 6; // << >> >>> Blockly.Solidity.ORDER_BITWISE_AND = 7; // & Blockly.Solidity.ORDER_BITWISE_XOR = 8; // ^ Blockly.Solidity.ORDER_BITWISE_OR = 9; // | Blockly.Solidity.ORDER_RELATIONAL = 10 // < <= > >= Blockly.Solidity.ORDER_EQUALITY = 11; // == != === !== Blockly.Solidity.ORDER_LOGICAL_AND = 12; // && Blockly.Solidity.ORDER_LOGICAL_OR = 13; // || Blockly.Solidity.ORDER_CONDITIONAL = 14; // ?: Blockly.Solidity.ORDER_ASSIGNMENT = 15; // = += -= *= /= %= <<= >>= ... Blockly.Solidity.ORDER_COMMA = 16; // , Blockly.Solidity.ORDER_NONE = 99; // (...) /** * List of outer-inner pairings that do NOT require parentheses. * @type {!Array.<!Array.<number>>} */ Blockly.Solidity.ORDER_OVERRIDES = [ // (foo()).bar -> foo().bar // (foo())[0] -> foo()[0] [Blockly.Solidity.ORDER_FUNCTION_CALL, Blockly.Solidity.ORDER_MEMBER], // (foo())() -> foo()() [Blockly.Solidity.ORDER_FUNCTION_CALL, Blockly.Solidity.ORDER_FUNCTION_CALL], // (foo.bar).baz -> foo.bar.baz // (foo.bar)[0] -> foo.bar[0] // (foo[0]).bar -> foo[0].bar // (foo[0])[1] -> foo[0][1] [Blockly.Solidity.ORDER_MEMBER, Blockly.Solidity.ORDER_MEMBER], // (foo.bar)() -> foo.bar() // (foo[0])() -> foo[0]() [Blockly.Solidity.ORDER_MEMBER, Blockly.Solidity.ORDER_FUNCTION_CALL], // !(!foo) -> !!foo [Blockly.Solidity.ORDER_LOGICAL_NOT, Blockly.Solidity.ORDER_LOGICAL_NOT], // a * (b * c) -> a * b * c [Blockly.Solidity.ORDER_MULTIPLICATION, Blockly.Solidity.ORDER_MULTIPLICATION], // a + (b + c) -> a + b + c [Blockly.Solidity.ORDER_ADDITION, Blockly.Solidity.ORDER_ADDITION], // a && (b && c) -> a && b && c [Blockly.Solidity.ORDER_LOGICAL_AND, Blockly.Solidity.ORDER_LOGICAL_AND], // a || (b || c) -> a || b || c [Blockly.Solidity.ORDER_LOGICAL_OR, Blockly.Solidity.ORDER_LOGICAL_OR], // ((foo[0])) -> foo[0] // ((foo.bar)) -> foo.bar [Blockly.Solidity.ORDER_MEMBER, Blockly.Solidity.ORDER_PARENTHESES], // (foo()) -> foo() [Blockly.Solidity.ORDER_FUNCTION_CALL, Blockly.Solidity.ORDER_PARENTHESES] ]; /** * Initialise the database of variable names. * @param {!Blockly.Workspace} workspace Workspace to generate code from. */ Blockly.Solidity.init = function (workspace) { // Create a dictionary of definitions to be printed before the code. Blockly.Solidity.definitions_ = Object.create(null); // Create a dictionary mapping desired function names in definitions_ // to actual function names (to avoid collisions with user functions). Blockly.Solidity.functionNames_ = Object.create(null); if (!Blockly.Solidity.variableDB_) { Blockly.Solidity.variableDB_ = new Blockly.Names(Blockly.Solidity.RESERVED_WORDS_); } else { Blockly.Solidity.variableDB_.reset(); } // var defvars = []; // var variables = workspace.getAllVariables(); // if (variables.length) { // for (var i = 0; i < variables.length; i++) { // defvars[i] = Blockly.Solidity.variableDB_.getName(variables[i].name, // Blockly.Variables.NAME_TYPE); // } // Blockly.Solidity.definitions_['variables'] = // 'int ' + defvars.join(', ') + ';'; // } }; /** * Prepend the generated code with the variable definitions. * @param {string} code Generated code. * @return {string} Completed code. */ Blockly.Solidity.finish = function (code) { // Convert the definitions dictionary into a list. var definitions = []; for (var name in Blockly.Solidity.definitions_) { definitions.push(Blockly.Solidity.definitions_[name]); } // Clean up temporary data. delete Blockly.Solidity.definitions_; delete Blockly.Solidity.functionNames_; Blockly.Solidity.variableDB_.reset(); return definitions.join('\n\n') + '\n\n\n' + code; }; /** * Naked values are top-level blocks with outputs that aren't plugged into * anything. A trailing semicolon is needed to make this legal. * @param {string} line Line of generated code. * @return {string} Legal line of code. */ Blockly.Solidity.scrubNakedValue = function (line) { return line + '\n'; }; /** * Encode a string as a properly escaped Solidity string, complete with * quotes. * @param {string} string Text to encode. * @return {string} Solidity string. * @private */ Blockly.Solidity.quote_ = function (string) { // Can't use goog.string.quote since Google's style guide recommends // JS string literals use single quotes. string = string.replace(/\\/g, '\\\\') .replace(/\n/g, '\\\n') .replace(/'/g, '\\\''); return '\'' + string + '\''; }; /** * Common tasks for generating Solidity from blocks. * Handles comments for the specified block and any connected value blocks. * Calls any statements following this block. * @param {!Blockly.Block} block The current block. * @param {string} code The Solidity code created for this block. * @return {string} Solidity code with comments and subsequent blocks added. * @private */ Blockly.Solidity.scrub_ = function (block, code) { var commentCode = ''; // Only collect comments for blocks that aren't inline. if (!block.outputConnection || !block.outputConnection.targetConnection) { // Collect comment for this block. var comment = block.getCommentText(); comment = Blockly.utils.wrap ? Blockly.utils.wrap(comment, Blockly.Solidity.COMMENT_WRAP - 3) : ''; if (comment) { if (block.getProcedureDef) { // Use a comment block for function comments. commentCode += '/**\n' + Blockly.Solidity.prefixLines(comment + '\n', ' * ') + ' */\n'; } else { commentCode += Blockly.Solidity.prefixLines(comment + '\n', '// '); } } // Collect comments for all value arguments. // Don't collect comments for nested statements. for (var i = 0; i < block.inputList.length; i++) { if (block.inputList[i].type == Blockly.INPUT_VALUE) { var childBlock = block.inputList[i].connection.targetBlock(); if (childBlock) { var comment = Blockly.Solidity.allNestedComments(childBlock); if (comment) { commentCode += Blockly.Solidity.prefixLines(comment, '// '); } } } } } var nextBlock = block.nextConnection && block.nextConnection.targetBlock(); var nextCode = Blockly.Solidity.blockToCode(nextBlock); return commentCode + code + nextCode; }; /** * Gets a property and adjusts the value while taking into account indexing. * @param {!Blockly.Block} block The block. * @param {string} atId The property ID of the element to get. * @param {number=} opt_delta Value to add. * @param {boolean=} opt_negate Whether to negate the value. * @param {number=} opt_order The highest order acting on this value. * @return {string|number} */ Blockly.Solidity.getAdjusted = function (block, atId, opt_delta, opt_negate, opt_order) { var delta = opt_delta || 0; var order = opt_order || Blockly.Solidity.ORDER_NONE; if (block.workspace.options.oneBasedIndex) { delta--; } var defaultAtIndex = block.workspace.options.oneBasedIndex ? '1' : '0'; if (delta > 0) { var at = Blockly.Solidity.valueToCode(block, atId, Blockly.Solidity.ORDER_ADDITION) || defaultAtIndex; } else if (delta < 0) { var at = Blockly.Solidity.valueToCode(block, atId, Blockly.Solidity.ORDER_SUBTRACTION) || defaultAtIndex; } else if (opt_negate) { var at = Blockly.Solidity.valueToCode(block, atId, Blockly.Solidity.ORDER_UNARY_NEGATION) || defaultAtIndex; } else { var at = Blockly.Solidity.valueToCode(block, atId, order) || defaultAtIndex; } if (Blockly.isNumber(at)) { // If the index is a naked number, adjust it right now. at = parseFloat(at) + delta; if (opt_negate) { at = -at; } } else { // If the index is dynamic, adjust it in code. if (delta > 0) { at = at + ' + ' + delta; var innerOrder = Blockly.Solidity.ORDER_ADDITION; } else if (delta < 0) { at = at + ' - ' + -delta; var innerOrder = Blockly.Solidity.ORDER_SUBTRACTION; } if (opt_negate) { if (delta) { at = '-(' + at + ')'; } else { at = '-' + at; } var innerOrder = Blockly.Solidity.ORDER_UNARY_NEGATION; } innerOrder = Math.floor(innerOrder); order = Math.floor(order); if (innerOrder && order >= innerOrder) { at = '(' + at + ')'; } } return at; }; Blockly.Solidity.updateWorkspaceNameFields = function (workspace) { var blocks = workspace.getAllBlocks(); for (var i = 0; i < blocks.length; ++i) { var nameField = blocks[i].getVariableNameSelectField ? blocks[i].getVariableNameSelectField() : null; var group = blocks[i].getVariableLabelGroup ? blocks[i].getVariableLabelGroup() : null; if (!!nameField && !!group) { var vars = Blockly.Solidity.getVariablesInScope(blocks[i], group); var options = vars.map(function (v) { return [Blockly.Solidity.getVariableName(v), v.id_]; }); var selectedOption = nameField.getValue(); console.log("selected option => ", selectedOption); if (options.length != 0) { var wasUndefined = nameField.menuGenerator_[0][1] == Blockly.Solidity.UNDEFINED_NAME; nameField.menuGenerator_ = options; if (wasUndefined) { nameField.setValue(options[0][1]); } else { nameField.setValue(selectedOption); // The text input does not redraw/update itself after we call "setValue", // so we set the text manually. // nameField.setText( // options.filter(function (o) { return o[1] == selectedOption })[0][0] // ); } } } } }; Blockly.Solidity.updateWorkspaceTypes = function (workspace, nameFieldName, valueFieldName) { var blocks = workspace.getAllBlocks(); var vars = workspace.getAllVariables(); for (var i = 0; i < blocks.length; ++i) { var stateNameField = blocks[i].getField(nameFieldName); if (!stateNameField) { continue; } var variableId = blocks[i].getFieldValue(nameFieldName); var variable = workspace.getVariableById(variableId); if (!variable) { return; } if (blocks[i].inputList[0] && blocks[i].inputList[0].name == valueFieldName) { switch (variable.type) { case 'TYPE_BOOL': blocks[i].inputList[0].setCheck("Boolean"); break; case 'TYPE_INT': blocks[i].inputList[0].setCheck("Number"); break; case 'TYPE_UINT': blocks[i].inputList[0].setCheck("Number"); break; case 'TYPE_STRING': blocks[i].inputList[0].setCheck("String"); break; case 'TYPE_ADDRESS': blocks[i].inputList[0].setCheck("String"); break; default: } } // TODO: update the output type } }; Blockly.Solidity.updateWorkspaceStateTypes = function (workspace) { Blockly.Solidity.updateWorkspaceTypes( workspace, 'STATE_NAME', 'STATE_VALUE' ); }; Blockly.Solidity.updateWorkspaceParameterTypes = function (workspace) { Blockly.Solidity.updateWorkspaceTypes( workspace, 'PARAM_NAME', 'PARAM_VALUE' ); }; Blockly.Solidity.createVariable = function (workspace, group, type, name, scope, id) { var variable = workspace.createVariable(name, type, id); variable.group = group; variable.scope = scope; Blockly.Solidity.setVariableName(variable, name); return variable; }; Blockly.Solidity.getVariableById = function (workspace, id) { return workspace.getVariableById(id); }; Blockly.Solidity.getVariableByName = function (workspace, name) { return Blockly.Solidity.getAllVariables(workspace) .filter(function (v) { return Blockly.Solidity.getVariableName(v) == name; }) [0]; }; Blockly.Solidity.getVariableByNameAndScope = function (name, scope, group = null) { return Blockly.Solidity.getVariablesInScope(scope, group) .filter(function (v) { return Blockly.Solidity.getVariableName(v) == name; }) [0]; }; Blockly.Solidity.deleteVariableById = function (workspace, id) { Blockly.Solidity.deleteVariableByName( workspace, Blockly.Solidity.getVariableById(workspace, id).name ); } Blockly.Solidity.deleteVariableByName = function (workspace, name) { return workspace.deleteVariable(name); }; Blockly.Solidity.variableIsInScope = function (variable, scope) { while (!!scope && scope.id != variable.scope.id) { var type = scope.type; do { scope = scope.getParent(); } while (scope && type == scope.type) } return !!scope; }; Blockly.Solidity.setVariableName = function (variable, name) { variable.name = '_scope("' + variable.scope.id + '")_' + name; } Blockly.Solidity.getVariableName = function (variable) { return variable.name.replace('_scope("' + variable.scope.id + '")_', ''); } Blockly.Solidity.getAllVariables = function (workspace) { return workspace.getAllVariables(); }; Blockly.Solidity.getVariablesInScope = function (block, group = null) { return Blockly.Solidity.getAllVariables(block.workspace) .filter(function (v) { return Blockly.Solidity.variableIsInScope(v, block); }) .filter(function (v) { return !group || v.group == group }); }; Blockly.Extensions.register("declare_typed_variable", function () { var block = this; if (!this.getVariableNameField) { throw "missing getVariableNameField method"; } if (!this.getVariableType) { throw "missing getVariableType method"; } if (!this.getVariableGroup) { throw "missing getVariableGroup method"; } if (!this.getVariableScope) { throw "missing getVariableScope method"; } this.declareOrUpdateVariable = function (name, force = false) { var oldName = this.getVariableNameField().getValue(); if (!this.getParent()) { return oldName; } if (!force && (!this.getParent() || oldName == name)) { return oldName; } var group = this.getVariableGroup(); var scope = this.getVariableScope(); var type = this.getVariableType(); if (!Blockly.Solidity.getVariableByNameAndScope(name, scope, group)) { newName = name; } else { var count = 2; var newName = name + count; while ( Blockly.Solidity.getVariableByNameAndScope(newName, scope, group) ) { count++; newName = name + count; } } var variable = Blockly.Solidity.getVariableById(this.workspace, this.id); if (!variable) { Blockly.Solidity.createVariable( this.workspace, group, type, newName, scope, this.id ); } else { variable.name = newName; } if (force) { this.getVariableNameField().setValue(newName); } Blockly.Solidity.updateWorkspaceNameFields(this.workspace); return newName; }; this.getVariableNameField().setValidator(function (name) { return block.declareOrUpdateVariable(name); }); var onchange = null; // if (goog.isFunction(this.onchange)) { // onchange = this.onchange; // } this.setOnChange(function (event) { Blockly.Solidity.updateWorkspaceNameFields(this.workspace); Blockly.Solidity.updateWorkspaceStateTypes(this.workspace); Blockly.Solidity.updateWorkspaceParameterTypes(this.workspace); if (event.blockId != this.id) { return; } if (event.type == "move" && !!event.oldParentId) { if (!!Blockly.Solidity.getVariableById(this.workspace, this.id)) { this.workspace.deleteVariableById(this.id) } } if (event.type == "move" && !!event.newParentId) { if (!this.workspace.getVariableById(this.id)) { this.declareOrUpdateVariable( this.getVariableNameField().getValue(), true ); } } if (event.element == "field" && event.name == "TYPE") { var variable = this.workspace.getVariableById(this.id); variable.type = this.getVariableType(); Blockly.Solidity.updateWorkspaceStateTypes(this.workspace); } if (!!onchange) { onchange.call(block, event); } }); }); Blockly.defineBlocksWithJsonArray([ { type: "contract", message0: "smart contract %1", args0: [ { type: "field_input", name: "NAME", check: "String", text: "MyContract", }, ], message1: "states %1", args1: [ { type: "input_statement", name: "STATES", check: ["contract_state"], align: "RIGHT", }, ], message2: "constructor %1", args2: [ { type: "input_statement", name: "CTOR", check: ["contract_ctor"], align: "RIGHT", }, ], message3: "methods %1", args3: [ { type: "input_statement", name: "METHODS", check: ["contract_method"], align: "RIGHT", }, ], colour: 160, tooltip: "Declares a new smart contract.", }, ]); Blockly.Blocks["contract_state"] = { init: function () { var nameField = new Blockly.FieldTextInput("s"); this.appendDummyInput() .appendField( new Blockly.FieldDropdown([ ["bool", "TYPE_BOOL"], ["int", "TYPE_INT"], ["uint", "TYPE_UINT"], ["string", "TYPE_STRING"], ["address", "TYPE_ADDRESS"], ]), "TYPE" ) .appendField(nameField, "NAME"); this.setPreviousStatement(true, "contract_state"); this.setNextStatement(true, "contract_state"); this.setColour(195); this.contextMenu = false; this._stateNameInitialized = false; this.getVariableNameField = function () { return nameField; }; this.getVariableType = function () { return this.getFieldValue("TYPE"); }; this.getVariableGroup = function () { return Blockly.Solidity.LABEL_GROUP_STATE; }; this.getVariableScope = function () { var scope = this.getParent(); while (!!scope && scope.type != "contract") { scope = scope.getParent(); } return scope; }; Blockly.Extensions.apply("declare_typed_variable", this, false); }, }; Blockly.Blocks["contract_state_get"] = { init: function () { this.appendDummyInput().appendField( new Blockly.FieldDropdown([ ["select state...", Blockly.Solidity.UNDEFINED_NAME], ]), "STATE_NAME" ); this.setOutput(true, null); this.setColour(195); this.getVariableNameSelectField = function () { return this.getField("STATE_NAME"); }; this.getVariableLabelGroup = function () { return Blockly.Solidity.LABEL_GROUP_STATE; }; }, }; Blockly.Blocks["contract_state_set"] = { init: function () { this.appendValueInput("STATE_VALUE") .appendField("set") .appendField( new Blockly.FieldDropdown( [["select state...", Blockly.Solidity.UNDEFINED_NAME]], this.validate ), "STATE_NAME" ) .appendField("to"); this.setPreviousStatement(true, null); this.setNextStatement(true, null); this.setColour(195); this.getVariableNameSelectField = function () { return this.getField("STATE_NAME"); }; this.getVariableLabelGroup = function () { return Blockly.Solidity.LABEL_GROUP_STATE; }; }, validate: function (stateNameVariableId) { var workspace = this.sourceBlock_.workspace; // FIXME: dirty hack to make sure updateWorkspaceStateTypes is called right after validate setTimeout(function () { Blockly.Solidity.updateWorkspaceStateTypes(workspace); }, 1); return stateNameVariableId; }, }; Blockly.Blocks["contract_method_parameter"] = { init: function () { var nameField = new Blockly.FieldTextInput("p"); this.appendDummyInput() .appendField( new Blockly.FieldDropdown([ ["bool", "TYPE_BOOL"], ["int", "TYPE_INT"], ["uint", "TYPE_UINT"], ["string", "TYPE_STRING"], ["address", "TYPE_ADDRESS"], ]), "TYPE" ) .appendField(nameField, "NAME"); this.setPreviousStatement(true, "contract_method_parameter"); this.setNextStatement(true, "contract_method_parameter"); this.setColour(320); this.contextMenu = false; this.getVariableNameField = function () { return nameField; }; this.getVariableType = function () { return this.getFieldValue("TYPE"); }; this.getVariableGroup = function () { return Blockly.Solidity.LABEL_GROUP_PARAMETER; }; this.getVariableScope = function () { var scope = this.getParent(); while ( !!scope && scope.type != "contract_method" && scope.type != "contract_ctor" ) { scope = scope.getParent(); } return scope; }; Blockly.Extensions.apply("declare_typed_variable", this, false); }, }; Blockly.Blocks["contract_method_parameter_get"] = { init: function () { this.appendDummyInput().appendField( new Blockly.FieldDropdown([ ["select param...", Blockly.Solidity.UNDEFINED_NAME], ]), "PARAM_NAME" ); this.setOutput(true, null); this.setColour(320); this.getVariableNameSelectField = function () { return this.getField("PARAM_NAME"); }; this.getVariableLabelGroup = function () { return Blockly.Solidity.LABEL_GROUP_PARAMETER; }; }, }; Blockly.Blocks["contract_method"] = { init: function () { this.jsonInit({ message0: "method %1", args0: [ { type: "field_input", name: "NAME", text: "myMethod", }, ], message1: "parameters %1", args1: [ { type: "input_statement", name: "PARAMS", check: ["contract_method_parameter"], align: "RIGHT", }, ], message2: "code %1", args2: [ { type: "input_statement", name: "STACK", align: "RIGHT", }, ], previousStatement: "contract_method", nextStatement: "contract_method", colour: 290, tooltip: "", helpUrl: "", }); this.getVariableNameField = function () { return this.getField("NAME"); }; this.getVariableType = function () { return "void"; }; this.getVariableGroup = function () { return Blockly.Solidity.LABEL_GROUP_METHOD; }; this.getVariableScope = function () { var scope = this.getParent(); while (!!scope && scope.type != "contract") { scope = scope.getParent(); } return scope; }; Blockly.Extensions.apply("declare_typed_variable", this, false); }, }; Blockly.Blocks['contract_method_call'] = { init: function () { this.appendDummyInput() .appendField('call method') .appendField( new Blockly.FieldDropdown( [["select method...", Blockly.Solidity.UNDEFINED_NAME]] ), "METHOD_NAME" ); this.setPreviousStatement(true, null); this.setNextStatement(true, null); // this.setOutput(true, null); this.setColour(320); this.getVariableNameSelectField = function () { return this.getField('METHOD_NAME'); }; this.getVariableLabelGroup = function () { return Blockly.Solidity.LABEL_GROUP_METHOD }; this.setOnChange(function (event) { if (event.blockId != this.id) { return; } if (event.element == 'field' && event.name == 'METHOD_NAME') { var methodId = this.getFieldValue('METHOD_NAME'); var methodBlock = this.workspace.getBlockById(methodId); var block = methodBlock; // Remove the old input (so that you don't have inputs stack repeatedly) for (let i = 0; i < this.params_.length; i++) { if (this.getInput(`${i}`)) { this.removeInput(`${i}`); } } this.params_ = [] do { block = block.getChildren() .filter(function (c) { return c.type == 'contract_method_parameter' })[0]; if (block) { this.params_.push(block); } } while (block) this.updateShape_() // FIXME: add/remove inputs according to the method params } }); }, params_: [], saveExtraState: function () { return { 'params': this.params_, }; }, loadExtraState: function (state) { this.params_ = state['params']; // This is a helper function which adds or removes inputs from the block. this.updateShape_(); }, updateShape_: function () { for (let i = 0; i < this.params_.length; i++) { this.appendValueInput(`${i}`) .appendField(`${this.params_[i].getVariableType()} ${this.params_[i].getVariableNameField().value_}`) // FIXME: Modify the code in this loop so that we pass the name of the each parameter in order // instead of set each time. } } }; /** ERROR HANDLING */ // Assert Blockly.Blocks["assert"] = { init: function () { this.appendValueInput("NAME").setCheck(null).appendField("Assert") this.setInputsInline(false) this.setOutput(true, null) this.setColour(185) this.setTooltip("") this.setHelpUrl("") }, } // Require Blockly.Blocks["require"] = { init: function () { this.appendValueInput("require").setCheck(null).appendField("require") this.setInputsInline(false) this.setOutput(true, null) this.setColour(285) this.setTooltip("") this.setHelpUrl("") }, } // Revert Blockly.Blocks["revert"] = { init: function () { this.appendValueInput("revert").setCheck(null).appendField("revert") this.setInputsInline(false) this.setOutput(true, null) this.setColour(210) this.setTooltip("") this.setHelpUrl("") }, } Blockly.defineBlocksWithJsonArray([ { type: "contract_ctor", message0: "constructor", message1: "parameters %1", args1: [ { type: "input_statement", name: "PARAMS", check: "contract_method_parameter", align: "RIGHT", }, ], message2: "code %1", args2: [ { type: "input_statement", name: "STACK", align: "RIGHT", }, ], previousStatement: ["contract_ctor"], colour: 290, tooltip: "", helpUrl: "", }, ]); Blockly.defineBlocksWithJsonArray([ { type: "contract_intrinsic_sha3", message0: "sha3 %1", args0: [ { type: "input_value", name: "VALUE", }, ], output: null, colour: 60, tooltip: "", helpUrl: "", }, ]); Blockly.Blocks["controls_for"] = { init: function () { this.jsonInit({ message0: "%{BKY_CONTROLS_FOR_TITLE}", args0: [ { type: "field_input", name: "VAR", text: "i", }, { type: "input_value", name: "FROM", check: "Number", align: "RIGHT", }, { type: "input_value", name: "TO", check: "Number", align: "RIGHT", }, { type: "input_value", name: "BY", check: "Number", align: "RIGHT", }, ], message1: "%{BKY_CONTROLS_REPEAT_INPUT_DO} %1", args1: [ { type: "input_statement", name: "DO", }, ], inputsInline: true, previousStatement: null, nextStatement: null, colour: "%{BKY_LOOPS_HUE}", helpUrl: "%{BKY_CONTROLS_FOR_HELPURL}", }); this.getVariableNameField = function () { return this.getField("VAR"); }; this.getVariableType = function () { return "TYPE_UINT"; }; this.getVariableGroup = function () { return Blockly.Solidity.LABEL_GROUP_VARIABLE; }; this.getVariableScope = function () { return this; }; Blockly.Extensions.apply("declare_typed_variable", this, false); }, };