Arquivos
deepforge/src/plugins/GenerateExecFile/GenerateExecFile.js
T
Brian Broll 806780bd1f Added single execution file generation. Fixes #138 (#219)
WIP #138 Added basic code generation for each op.

WIP #138 merged operation code blocks in topo ordering

WIP #138 Removed unused file and updated plugin name

WIP #138 Added support for references

WIP #138. Updated plugin for executions

WIP #138 Fixed ptr code generation

WIP #138 Updated pipeline seed
2016-06-06 09:56:59 -05:00

345 linhas
11 KiB
JavaScript

/*globals define, _*/
/*jshint node:true, browser:true*/
/**
* Generated by PluginGenerator 1.7.0 from webgme on Sat Jun 04 2016 18:01:54 GMT-0500 (CDT).
* A plugin that inherits from the PluginBase. To see source code documentation about available
* properties and methods visit %host%/docs/source/PluginBase.html.
*/
define([
'text!./metadata.json',
'plugin/PluginBase',
'deepforge/plugin/PtrCodeGen',
'q'
], function (
pluginMetadata,
PluginBase,
PtrCodeGen,
Q
) {
'use strict';
pluginMetadata = JSON.parse(pluginMetadata);
var HEADER_LENGTH = 60;
/**
* Initializes a new instance of GenerateExecFile.
* @class
* @augments {PluginBase}
* @classdesc This class represents the plugin GenerateExecFile.
* @constructor
*/
var GenerateExecFile = function () {
// Call base class' constructor.
PluginBase.call(this);
this.pluginMetadata = pluginMetadata;
this._srcIdFor = {}; // input path -> output data node path
this._nameFor = {}; // input path -> opname
this._dataNameFor = {};
this._opNames = {};
// topo sort stuff
this._nextOps = {};
this._incomingCnts = {};
this._operations = {};
this.activeNodeId = null;
this.activeNodeDepth = null;
};
/**
* Metadata associated with the plugin. Contains id, name, version, description, icon, configStructue etc.
* This is also available at the instance at this.pluginMetadata.
* @type {object}
*/
GenerateExecFile.metadata = pluginMetadata;
// Prototypical inheritance from PluginBase.
GenerateExecFile.prototype = Object.create(PluginBase.prototype);
GenerateExecFile.prototype.constructor = GenerateExecFile;
/**
* Main function for the plugin to execute. This will perform the execution.
* Notes:
* - Always log with the provided logger.[error,warning,info,debug].
* - Do NOT put any user interaction logic UI, etc. inside this method.
* - callback always has to be called even if error happened.
*
* @param {function(string, plugin.PluginResult)} callback - the result callback
*/
GenerateExecFile.prototype.main = function (callback) {
// Get all the children and call generate exec file
this.activeNodeId = this.core.getPath(this.activeNode);
this.activeNodeDepth = this.activeNodeId.split('/').length + 1;
if (this.isMetaTypeOf(this.activeNode, this.META.Execution)) {
this.activeNodeDepth++;
}
return this.core.loadChildren(this.activeNode)
.then(nodes => this.createExecFile(nodes))
.then(code => this.blobClient.putFile('init.lua', code))
.then(hash => {
this.result.addArtifact(hash);
this.result.setSuccess(true);
callback(null, this.result);
})
.fail(err => callback(err));
};
GenerateExecFile.prototype.createExecFile = function (children) {
// Convert opNodes' jobs to the nested operations
var opNodes,
nodes;
return this.unpackJobs(children)
.then(_nodes => {
nodes = _nodes;
opNodes = nodes
.filter(node => this.isMetaTypeOf(node, this.META.Operation));
return Q.all(nodes.map(node => this.registerNameAndData(node)));
})
.then(() => Q.all(opNodes.map(node => this.createOperation(node))))
.then(operations => {
var nextIds = opNodes.map(n => this.core.getPath(n))
.filter(id => !this._incomingCnts[id]);
operations.forEach(op => this._operations[op.id] = op);
// Toposort and concat!
return this.combineOpNodes(nextIds);
})
.fail(err => this.logger.error(err));
};
GenerateExecFile.prototype.unpackJobs = function (nodes) {
return Q.all(
nodes.map(node => {
if (!this.isMetaTypeOf(node, this.META.Job)) {
return node;
}
return this.core.loadChildren(node)
.then(children =>
children.find(c => this.isMetaTypeOf(c, this.META.Operation))
);
})
);
};
GenerateExecFile.prototype.combineOpNodes = function (opIds) {
var nextIds = [],
dstIds,
code,
id;
// Combine all nodes with incoming cnts of 0
code = opIds.map(id => this._operations[id].code).join('\n');
// Decrement all next ops
dstIds = opIds.map(id => this._nextOps[id])
.reduce((l1, l2) => l1.concat(l2), []);
for (var i = dstIds.length; i--;) {
id = dstIds[i];
if (--this._incomingCnts[id] === 0) {
nextIds.push(id);
}
}
// append
return [
code,
nextIds.length ? this.combineOpNodes(nextIds) : ''
].join('\n');
};
GenerateExecFile.prototype.registerNameAndData = function (node) {
var name = this.core.getAttribute(node, 'name'),
id = this.core.getPath(node),
basename = name,
i = 2;
if (this.isMetaTypeOf(node, this.META.Operation)) {
// Get a unique operation name
while (this._opNames[name]) {
name = basename + '_' + i;
i++;
}
// register the unique name
this._opNames[name] = true;
this._nameFor[id] = name;
// For operations, register all output data node names by path
return this.core.loadChildren(node)
.then(cntrs => {
var cntr = cntrs.find(n => this.isMetaTypeOf(n, this.META.Outputs));
return this.core.loadChildren(cntr);
})
.then(outputs => {
outputs.forEach(output => {
var dataId = this.core.getPath(output);
name = this.core.getAttribute(output, 'name');
this._dataNameFor[dataId] = name;
});
});
// For each input data node, register the associated output id
} else if (this.isMetaTypeOf(node, this.META.Transporter)) {
var outputData = this.core.getPointerPath(node, 'src'),
inputData = this.core.getPointerPath(node, 'dst'),
srcOpId = this.getOpIdFor(outputData),
dstOpId = this.getOpIdFor(inputData);
this._srcIdFor[inputData] = outputData;
// Store the next operation ids for the op id
if (!this._nextOps[srcOpId]) {
this._nextOps[srcOpId] = [];
}
this._nextOps[srcOpId].push(dstOpId);
// Increment the incoming counts for each dst op
this._incomingCnts[dstOpId] = this._incomingCnts[dstOpId] || 0;
this._incomingCnts[dstOpId]++;
}
};
GenerateExecFile.prototype.getOpIdFor = function (dataId) {
var ids = dataId.split('/'),
depth = ids.length;
ids.splice(this.activeNodeDepth - depth);
return ids.join('/');
};
// For each operation...
// - unpack the inputs from prev ops
// - add the attributes table (if used)
// - check for '\<attributes\>' in code
// - add the references
// - generate the code
// - replace the `return <thing>` w/ `<ref-name> = <thing>`
GenerateExecFile.prototype.createOperation = function (node) {
var id = this.core.getPath(node),
operation = {};
operation.name = this._nameFor[id];
operation.id = id;
operation.code = this.core.getAttribute(node, 'code');
// Update the 'code' attribute
// Change the last return statement to assign the results to a table
operation.code = this.assignResultToVar(operation.code,
`${operation.name}_results`);
// Get all the input names (and sources)
return this.core.loadChildren(node)
.then(containers => {
var inputs;
inputs = containers
.find(cntr => this.isMetaTypeOf(cntr, this.META.Inputs));
this.logger.info(`${name} has ${containers.length} cntrs`);
return this.core.loadChildren(inputs);
})
.then(data => {
// Get the input names and sources
var inputNames = data.map(d => this.core.getAttribute(d, 'name')),
ids = data.map(d => this.core.getPath(d)),
srcIds = ids.map(id => this._srcIdFor[id]);
operation.inputs = inputNames.map((name, i) => {
var id = srcIds[i],
srcDataName = this._dataNameFor[id],
srcOpId = this.getOpIdFor(id),
srcOpName = this._nameFor[srcOpId];
return `local ${name} = ${srcOpName}_results.${srcDataName}`;
});
return operation;
})
.then(operation => {
// For each reference, run the plugin and retrieve the generated code
operation.refNames = this.core.getPointerNames(node)
.filter(name => name !== 'base');
var refs = operation.refNames
.map(ref => [ref, this.core.getPointerPath(node, ref)]);
return Q.all(
refs.map(pair => this.genPtrSnippet.apply(this, pair))
);
})
.then(codeFiles => {
operation.refs = codeFiles;
this.genOperationCode(operation);
return operation;
});
};
GenerateExecFile.prototype.genPtrSnippet = function (ptrName, pId) {
return this.getPtrCodeHash(pId)
.then(hash => this.blobClient.getObjectAsString(hash))
.then(code => this.createHeader(`creating ${ptrName}`, 40) + '\n' +
this.assignResultToVar(code, ptrName));
};
GenerateExecFile.prototype.createHeader = function (title, length) {
var len;
title = ` ${title} `;
length = length || HEADER_LENGTH;
len = Math.max(
Math.floor((length - title.length)/2),
2
);
return [
'',
title,
''
].join(new Array(len+1).join('-')) + '\n';
};
GenerateExecFile.prototype.genOperationCode = function (operation) {
var header = this.createHeader(`"${operation.name}" Operation`),
codeParts = [];
codeParts.push(header);
if (operation.inputs.length) {
codeParts.push(operation.inputs.join('\n'));
}
if (operation.refs.length) {
codeParts.push(operation.refs.join('\n'));
}
codeParts.push(operation.code);
codeParts.push('');
operation.code = codeParts.join('\n');
return operation;
};
GenerateExecFile.prototype.assignResultToVar = function (code, name) {
var i = code.lastIndexOf('return');
return code.substring(0, i) +
code.substring(i)
.replace('return', `local ${name} = `);
};
_.extend(GenerateExecFile.prototype, PtrCodeGen.prototype);
return GenerateExecFile;
});