Extracted renderer code into a child of compiler

pull/1/head
Dave Hoover 9 years ago
parent 0a37e6a3fb
commit fe1bc22117
  1. 210
      src/app.js
  2. 22
      src/app/compiler.js
  3. 4
      src/app/editor.js
  4. 230
      src/app/renderer.js

@ -1,5 +1,4 @@
var $ = require('jquery');
var UniversalDApp = require('./universal-dapp.js');
var web3 = require('./web3-adapter.js');
var utils = require('./app/utils');
@ -427,6 +426,7 @@ var run = function() {
if (!hidingRHP) compiler.compile();
});
function getHidingRHP() { return hidingRHP; }
// ----------------- editor resize ---------------
@ -442,215 +442,9 @@ var run = function() {
// ----------------- compiler output renderer ----------------------
var detailsOpen = {};
var renderError = function(message) {
var type = utils.errortype(message);
var $pre = $("<pre />").text(message);
var $error = $('<div class="sol ' + type + '"><div class="close"><i class="fa fa-close"></i></div></div>').prepend($pre);
$('#output').append( $error );
var err = message.match(/^([^:]*):([0-9]*):(([0-9]*):)? /);
if (err) {
var errFile = err[1];
var errLine = parseInt(err[2], 10) - 1;
var errCol = err[4] ? parseInt(err[4], 10) : 0;
if (errFile == '' || errFile == editor.getCacheFile()) {
compiler.addAnnotation({
row: errLine,
column: errCol,
text: message,
type: type
});
}
$error.click(function(ev){
if (errFile != '' && errFile != editor.getCacheFile() && editor.getFiles().indexOf(utils.fileKey(errFile)) !== -1) {
// Switch to file
editor.setCacheFile(errFile);
updateFiles();
//@TODO could show some error icon in files with errors
}
editor.handleErrorClick(errLine, errCol);
});
$error.find('.close').click(function(ev){
ev.preventDefault();
$error.remove();
return false;
});
}
};
var gethDeploy = function(contractName, jsonInterface, bytecode){
var code = "";
var funABI = getConstructorInterface($.parseJSON(jsonInterface));
$.each(funABI.inputs, function(i, inp) {
code += "var " + inp.name + " = /* var of type " + inp.type + " here */ ;\n";
});
code += "var " + contractName + "Contract = web3.eth.contract(" + jsonInterface.replace("\n","") + ");"
+"\nvar " + contractName + " = " + contractName + "Contract.new(";
$.each(funABI.inputs, function(i, inp) {
code += "\n " + inp.name + ",";
});
code += "\n {"+
"\n from: web3.eth.accounts[0], "+
"\n data: '"+bytecode+"', "+
"\n gas: 3000000"+
"\n }, function(e, contract){"+
"\n console.log(e, contract);"+
"\n if (typeof contract.address != 'undefined') {"+
"\n console.log('Contract mined! address: ' + contract.address + ' transactionHash: ' + contract.transactionHash);" +
"\n }" +
"\n })";
return code;
};
var combined = function(contractName, jsonInterface, bytecode){
return JSON.stringify([{name: contractName, interface: jsonInterface, bytecode: bytecode}]);
};
var renderContracts = function(data, source) {
var udappContracts = [];
for (var contractName in data.contracts) {
var contract = data.contracts[contractName];
udappContracts.push({
name: contractName,
interface: contract['interface'],
bytecode: contract.bytecode
});
}
var dapp = new UniversalDApp(udappContracts, {
vm: executionContext === 'vm',
removable: false,
getAddress: function(){ return $('#txorigin').val(); },
getValue: function(){
var comp = $('#value').val().split(' ');
return web3.toWei(comp[0], comp.slice(1).join(' '));
},
removable_instances: true,
renderOutputModifier: function(contractName, $contractOutput) {
var contract = data.contracts[contractName];
return $contractOutput
.append(textRow('Bytecode', contract.bytecode))
.append(textRow('Interface', contract['interface']))
.append(textRow('Web3 deploy', gethDeploy(contractName.toLowerCase(),contract['interface'],contract.bytecode), 'deploy'))
.append(textRow('uDApp', combined(contractName,contract['interface'],contract.bytecode), 'deploy'))
.append(getDetails(contract, source, contractName));
}});
var $contractOutput = dapp.render();
$txOrigin = $('#txorigin');
function renderAccounts(err, accounts) {
if (err)
renderError(err.message);
if (accounts && accounts[0]){
$txOrigin.empty();
for( var a in accounts) { $txOrigin.append($('<option />').val(accounts[a]).text(accounts[a])); }
$txOrigin.val(accounts[0]);
} else $txOrigin.val('unknown');
}
dapp.getAccounts(renderAccounts);
$contractOutput.find('.title').click(function(ev){ $(this).closest('.contract').toggleClass('hide'); });
$('#output').append( $contractOutput );
$('.col2 input,textarea').click(function() { this.select(); });
};
var tableRowItems = function(first, second, cls) {
return $('<div class="crow"/>')
.addClass(cls)
.append($('<div class="col1">').append(first))
.append($('<div class="col2">').append(second));
};
var tableRow = function(description, data) {
return tableRowItems(
$('<span/>').text(description),
$('<input readonly="readonly"/>').val(data));
};
var textRow = function(description, data, cls) {
return tableRowItems(
$('<strong/>').text(description),
$('<textarea readonly="readonly" class="gethDeployText"/>').val(data),
cls);
};
var getDetails = function(contract, source, contractName) {
var button = $('<button>Toggle Details</button>');
var details = $('<div style="display: none;"/>')
.append(tableRow('Solidity Interface', contract.solidity_interface))
.append(tableRow('Opcodes', contract.opcodes));
var funHashes = '';
for (var fun in contract.functionHashes)
funHashes += contract.functionHashes[fun] + ' ' + fun + '\n';
details.append($('<span class="col1">Functions</span>'));
details.append($('<pre/>').text(funHashes));
details.append($('<span class="col1">Gas Estimates</span>'));
details.append($('<pre/>').text(formatGasEstimates(contract.gasEstimates)));
if (contract.runtimeBytecode && contract.runtimeBytecode.length > 0)
details.append(tableRow('Runtime Bytecode', contract.runtimeBytecode));
if (contract.assembly !== null)
{
details.append($('<span class="col1">Assembly</span>'));
var assembly = $('<pre/>').text(formatAssemblyText(contract.assembly, '', source));
details.append(assembly);
}
button.click(function() { detailsOpen[contractName] = !detailsOpen[contractName]; details.toggle(); });
if (detailsOpen[contractName])
details.show();
return $('<div class="contractDetails"/>').append(button).append(details);
};
var formatGasEstimates = function(data) {
var gasToText = function(g) { return g === null ? 'unknown' : g; };
var text = '';
if ('creation' in data)
text += 'Creation: ' + gasToText(data.creation[0]) + ' + ' + gasToText(data.creation[1]) + '\n';
text += 'External:\n';
for (var fun in data.external)
text += ' ' + fun + ': ' + gasToText(data.external[fun]) + '\n';
text += 'Internal:\n';
for (var fun in data.internal)
text += ' ' + fun + ': ' + gasToText(data.internal[fun]) + '\n';
return text;
};
var formatAssemblyText = function(asm, prefix, source) {
if (typeof(asm) == typeof('') || asm === null || asm === undefined)
return prefix + asm + '\n';
var text = prefix + '.code\n';
$.each(asm['.code'], function(i, item) {
var v = item.value === undefined ? '' : item.value;
var src = '';
if (item.begin !== undefined && item.end != undefined)
src = source.slice(item.begin, item.end).replace('\n', '\\n', 'g');
if (src.length > 30)
src = src.slice(0, 30) + '...';
if (item.name != 'tag')
text += ' ';
text += prefix + item.name + ' ' + v + '\t\t\t' + src + '\n';
});
text += prefix + '.data\n';
if (asm['.data'])
$.each(asm['.data'], function(i, item) {
text += ' ' + prefix + '' + i + ':\n';
text += formatAssemblyText(item, prefix + ' ', source);
});
return text;
};
$('.asmOutput button').click(function() {$(this).parent().find('pre').toggle(); });
var getConstructorInterface = function(abi) {
var funABI = {'name':'','inputs':[],'type':'constructor','outputs':[]};
for (var i = 0; i < abi.length; i++)
if (abi[i].type == 'constructor') {
funABI.inputs = abi[i].inputs || [];
break;
}
return funABI;
};
// ----------------- compiler ----------------------
@ -659,7 +453,7 @@ var run = function() {
return $.getJSON('https://api.github.com/repos/' + root + '/contents/' + path, cb);
}
var compiler = new Compiler(editor, renderContracts, renderError, handleGithubCall, $('#output'), function() { return hidingRHP; });
var compiler = new Compiler(editor, handleGithubCall, $('#output'), getHidingRHP, updateFiles);
function setVersionText(text) {
$('#version').text(text);

@ -1,9 +1,11 @@
var queryParams = require('./query-params');
var utils = require('./utils');
var Renderer = require('./renderer');
var Base64 = require('js-base64').Base64;
function Compiler(editor, renderContracts, renderError, handleGithubCall, outputField, hidingRHP) {
function Compiler(editor, handleGithubCall, outputField, hidingRHP, updateFiles) {
var renderer = new Renderer(editor, this, updateFiles);
var compileJSON;
var compilerAcceptsMultipleFiles;
@ -19,7 +21,7 @@ function Compiler(editor, renderContracts, renderError, handleGithubCall, output
function onChange() {
var input = editor.getValue();
if (input === "") {
window.localStorage.setItem(editor.getCacheFile(), '');
window.localStorage.setItem(editor.getRawCacheFile(), '');
return;
}
if (input === previousInput)
@ -36,14 +38,14 @@ function Compiler(editor, renderContracts, renderError, handleGithubCall, output
sourceAnnotations = [];
outputField.empty();
var input = editor.getValue();
window.localStorage.setItem(editor.getCacheFile(), input);
window.localStorage.setItem(editor.getRawCacheFile(), input);
var files = {};
files[utils.fileNameFromKey(editor.getCacheFile())] = input;
files[editor.getCacheFile()] = input;
gatherImports(files, missingInputs, function(input, error) {
outputField.empty();
if (input === null) {
renderError(error);
render.error(error);
} else {
var optimize = queryParams.get().optimize;
compileJSON(input, optimize ? 1 : 0);
@ -104,17 +106,17 @@ function Compiler(editor, renderContracts, renderError, handleGithubCall, output
try {
data = JSON.parse(result);
} catch (exception) {
renderError('Invalid JSON output from the compiler: ' + exception);
renderer.error('Invalid JSON output from the compiler: ' + exception);
return;
}
if (data['error'] !== undefined) {
renderError(data['error']);
renderer.error(data['error']);
if (utils.errortype(data['error']) !== 'warning') noFatalErrors = false;
}
if (data['errors'] != undefined) {
data['errors'].forEach(function(err) {
renderError(err);
renderer.error(err);
if (utils.errortype(err) !== 'warning') noFatalErrors = false;
});
}
@ -122,7 +124,7 @@ function Compiler(editor, renderContracts, renderError, handleGithubCall, output
if (missingInputs !== undefined && missingInputs.length > 0)
this.compile(missingInputs);
else if (noFatalErrors && !hidingRHP())
renderContracts(data, editor.getValue());
renderer.contracts(data, editor.getValue());
}
this.initializeWorker = function(version, setVersionText) {
@ -154,7 +156,7 @@ function Compiler(editor, renderContracts, renderError, handleGithubCall, output
importHints = importHints || [];
if (!compilerAcceptsMultipleFiles)
{
cb(files[utils.fileNameFromKey(editor.getCacheFile())]);
cb(files[editor.getCacheFile()]);
return;
}
var importRegex = /^\s*import\s*[\'\"]([^\'\"]+)[\'\"];/g;

@ -18,6 +18,10 @@ function Editor(loadingFromGist) {
SOL_CACHE_FILE = utils.fileKey(cacheFile);
};
this.getRawCacheFile = function() {
return SOL_CACHE_FILE;
};
this.getCacheFile = function() {
return utils.fileNameFromKey(SOL_CACHE_FILE);
};

@ -0,0 +1,230 @@
var $ = require('jquery');
var UniversalDApp = require('../universal-dapp.js');
var utils = require('./utils');
function Renderer(editor, compiler, updateFiles) {
var detailsOpen = {};
function renderError(message) {
var type = utils.errortype(message);
var $pre = $("<pre />").text(message);
var $error = $('<div class="sol ' + type + '"><div class="close"><i class="fa fa-close"></i></div></div>').prepend($pre);
$('#output').append( $error );
var err = message.match(/^([^:]*):([0-9]*):(([0-9]*):)? /);
if (err) {
var errFile = err[1];
var errLine = parseInt(err[2], 10) - 1;
var errCol = err[4] ? parseInt(err[4], 10) : 0;
if (errFile == '' || errFile == editor.getCacheFile()) {
compiler.addAnnotation({
row: errLine,
column: errCol,
text: message,
type: type
});
}
$error.click(function(ev){
if (errFile != '' && errFile != editor.getCacheFile() && editor.getFiles().indexOf(utils.fileKey(errFile)) !== -1) {
// Switch to file
editor.setCacheFile(errFile);
updateFiles();
//@TODO could show some error icon in files with errors
}
editor.handleErrorClick(errLine, errCol);
});
$error.find('.close').click(function(ev){
ev.preventDefault();
$error.remove();
return false;
});
}
};
this.error = renderError;
var combined = function(contractName, interface, bytecode){
return JSON.stringify([{name: contractName, interface: interface, bytecode: bytecode}]);
};
function renderContracts(data, source) {
var udappContracts = [];
for (var contractName in data.contracts) {
var contract = data.contracts[contractName];
udappContracts.push({
name: contractName,
interface: contract['interface'],
bytecode: contract.bytecode
});
}
var dapp = new UniversalDApp(udappContracts, {
vm: executionContext === 'vm',
removable: false,
getAddress: function(){ return $('#txorigin').val(); },
getValue: function(){
var comp = $('#value').val().split(' ');
return web3.toWei(comp[0], comp.slice(1).join(' '));
},
removable_instances: true,
renderOutputModifier: function(contractName, $contractOutput) {
var contract = data.contracts[contractName];
return $contractOutput
.append(textRow('Bytecode', contract.bytecode))
.append(textRow('Interface', contract['interface']))
.append(textRow('Web3 deploy', gethDeploy(contractName.toLowerCase(),contract['interface'],contract.bytecode), 'deploy'))
.append(textRow('uDApp', combined(contractName,contract['interface'],contract.bytecode), 'deploy'))
.append(getDetails(contract, source, contractName));
}
});
var $contractOutput = dapp.render();
$txOrigin = $('#txorigin');
function renderAccounts(err, accounts) {
if (err)
renderError(err.message);
if (accounts && accounts[0]){
$txOrigin.empty();
for( var a in accounts) { $txOrigin.append($('<option />').val(accounts[a]).text(accounts[a])); }
$txOrigin.val(accounts[0]);
} else $txOrigin.val('unknown');
}
dapp.getAccounts(renderAccounts);
$contractOutput.find('.title').click(function(ev){ $(this).closest('.contract').toggleClass('hide'); });
$('#output').append( $contractOutput );
$('.col2 input,textarea').click(function() { this.select(); });
};
this.contracts = renderContracts;
var tableRowItems = function(first, second, cls) {
return $('<div class="crow"/>')
.addClass(cls)
.append($('<div class="col1">').append(first))
.append($('<div class="col2">').append(second));
};
var tableRow = function(description, data) {
return tableRowItems(
$('<span/>').text(description),
$('<input readonly="readonly"/>').val(data));
};
var textRow = function(description, data, cls) {
return tableRowItems(
$('<strong/>').text(description),
$('<textarea readonly="readonly" class="gethDeployText"/>').val(data),
cls);
};
var getDetails = function(contract, source, contractName) {
var button = $('<button>Toggle Details</button>');
var details = $('<div style="display: none;"/>')
.append(tableRow('Solidity Interface', contract.solidity_interface))
.append(tableRow('Opcodes', contract.opcodes));
var funHashes = '';
for (var fun in contract.functionHashes)
funHashes += contract.functionHashes[fun] + ' ' + fun + '\n';
details.append($('<span class="col1">Functions</span>'));
details.append($('<pre/>').text(funHashes));
details.append($('<span class="col1">Gas Estimates</span>'));
details.append($('<pre/>').text(formatGasEstimates(contract.gasEstimates)));
if (contract.runtimeBytecode && contract.runtimeBytecode.length > 0)
details.append(tableRow('Runtime Bytecode', contract.runtimeBytecode));
if (contract.assembly !== null)
{
details.append($('<span class="col1">Assembly</span>'));
var assembly = $('<pre/>').text(formatAssemblyText(contract.assembly, '', source));
details.append(assembly);
}
button.click(function() { detailsOpen[contractName] = !detailsOpen[contractName]; details.toggle(); });
if (detailsOpen[contractName])
details.show();
return $('<div class="contractDetails"/>').append(button).append(details);
};
var formatGasEstimates = function(data) {
var gasToText = function(g) { return g === null ? 'unknown' : g; }
var text = '';
if ('creation' in data)
text += 'Creation: ' + gasToText(data.creation[0]) + ' + ' + gasToText(data.creation[1]) + '\n';
text += 'External:\n';
for (var fun in data.external)
text += ' ' + fun + ': ' + gasToText(data.external[fun]) + '\n';
text += 'Internal:\n';
for (var fun in data.internal)
text += ' ' + fun + ': ' + gasToText(data.internal[fun]) + '\n';
return text;
};
var formatAssemblyText = function(asm, prefix, source) {
if (typeof(asm) == typeof('') || asm === null || asm === undefined)
return prefix + asm + '\n';
var text = prefix + '.code\n';
$.each(asm['.code'], function(i, item) {
var v = item.value === undefined ? '' : item.value;
var src = '';
if (item.begin !== undefined && item.end != undefined)
src = source.slice(item.begin, item.end).replace('\n', '\\n', 'g');
if (src.length > 30)
src = src.slice(0, 30) + '...';
if (item.name != 'tag')
text += ' ';
text += prefix + item.name + ' ' + v + '\t\t\t' + src + '\n';
});
text += prefix + '.data\n';
if (asm['.data'])
$.each(asm['.data'], function(i, item) {
text += ' ' + prefix + '' + i + ':\n';
text += formatAssemblyText(item, prefix + ' ', source);
});
return text;
};
function gethDeploy(contractName, interface, bytecode){
var code = "";
var funABI = getConstructorInterface(JSON.parse(interface));
funABI.inputs.forEach(function(inp) {
code += "var " + inp.name + " = /* var of type " + inp.type + " here */ ;\n";
});
code += "var " + contractName + "Contract = web3.eth.contract(" + interface.replace("\n","") + ");"
+"\nvar " + contractName + " = " + contractName + "Contract.new(";
funABI.inputs.forEach(function(inp) {
code += "\n " + inp.name + ",";
});
code += "\n {"+
"\n from: web3.eth.accounts[0], "+
"\n data: '"+bytecode+"', "+
"\n gas: 3000000"+
"\n }, function(e, contract){"+
"\n console.log(e, contract);"+
"\n if (typeof contract.address != 'undefined') {"+
"\n console.log('Contract mined! address: ' + contract.address + ' transactionHash: ' + contract.transactionHash);" +
"\n }" +
"\n })";
return code;
}
function getConstructorInterface(abi) {
var funABI = {'name':'','inputs':[],'type':'constructor','outputs':[]};
for (var i = 0; i < abi.length; i++)
if (abi[i].type == 'constructor') {
funABI.inputs = abi[i].inputs || [];
break;
}
return funABI;
}
}
module.exports = Renderer;
Loading…
Cancel
Save