@glimmer/compiler
Version:
242 lines (209 loc) • 26 kB
JavaScript
import { generateSyntaxError, isKeyword, KEYWORDS_TYPES } from '@glimmer/syntax';
import { exhausted } from '@glimmer/util';
import { Err } from '../../../shared/result';
class KeywordImpl {
constructor(keyword, type, delegate) {
this.keyword = keyword;
this.delegate = delegate;
let nodes = new Set();
for (let nodeType of KEYWORD_NODES[type]) {
nodes.add(nodeType);
}
this.types = nodes;
}
match(node) {
if (!this.types.has(node.type)) {
return false;
}
let path = getCalleeExpression(node);
if (path !== null && path.type === 'Path' && path.ref.type === 'Free') {
if (path.tail.length > 0) {
if (path.ref.resolution.serialize() === 'Loose') {
// cannot be a keyword reference, keywords do not allow paths (must be
// relying on implicit this fallback)
return false;
}
}
return path.ref.name === this.keyword;
} else {
return false;
}
}
translate(node, state) {
if (this.match(node)) {
let path = getCalleeExpression(node);
if (path !== null && path.type === 'Path' && path.tail.length > 0) {
return Err(generateSyntaxError(`The \`${this.keyword}\` keyword was used incorrectly. It was used as \`${path.loc.asString()}\`, but it cannot be used with additional path segments. \n\nError caused by`, node.loc));
}
let param = this.delegate.assert(node, state);
return param.andThen(param => this.delegate.translate({
node,
state
}, param));
} else {
return null;
}
}
}
export const KEYWORD_NODES = {
Call: ['Call'],
Block: ['InvokeBlock'],
Append: ['AppendContent'],
Modifier: ['ElementModifier']
};
export function keyword(keyword, type, delegate) {
return new KeywordImpl(keyword, type, delegate);
}
function getCalleeExpression(node) {
switch (node.type) {
// This covers the inside of attributes and expressions, as well as the callee
// of call nodes
case 'Path':
return node;
case 'AppendContent':
return getCalleeExpression(node.value);
case 'Call':
case 'InvokeBlock':
case 'ElementModifier':
return node.callee;
default:
return null;
}
}
export class Keywords {
constructor(type) {
this._keywords = [];
this._type = type;
}
kw(name, delegate) {
this._keywords.push(keyword(name, this._type, delegate));
return this;
}
translate(node, state) {
for (let keyword of this._keywords) {
let result = keyword.translate(node, state);
if (result !== null) {
return result;
}
}
let path = getCalleeExpression(node);
if (path && path.type === 'Path' && path.ref.type === 'Free' && isKeyword(path.ref.name)) {
let {
name
} = path.ref;
let usedType = this._type;
let validTypes = KEYWORDS_TYPES[name];
if (validTypes.indexOf(usedType) === -1) {
return Err(generateSyntaxError(`The \`${name}\` keyword was used incorrectly. It was used as ${typesToReadableName[usedType]}, but its valid usages are:\n\n${generateTypesMessage(name, validTypes)}\n\nError caused by`, node.loc));
}
}
return null;
}
}
const typesToReadableName = {
Append: 'an append statement',
Block: 'a block statement',
Call: 'a call expression',
Modifier: 'a modifier'
};
function generateTypesMessage(name, types) {
return types.map(type => {
switch (type) {
case 'Append':
return `- As an append statement, as in: {{${name}}}`;
case 'Block':
return `- As a block statement, as in: {{#${name}}}{{/${name}}}`;
case 'Call':
return `- As an expression, as in: (${name})`;
case 'Modifier':
return `- As a modifier, as in: <div {{${name}}}></div>`;
default:
return exhausted(type);
}
}).join('\n\n');
}
/**
* This function builds keyword definitions for a particular type of AST node (`KeywordType`).
*
* You can build keyword definitions for:
*
* - `Expr`: A `SubExpression` or `PathExpression`
* - `Block`: A `BlockStatement`
* - A `BlockStatement` is a keyword candidate if its head is a
* `PathExpression`
* - `Append`: An `AppendStatement`
*
* A node is a keyword candidate if:
*
* - A `PathExpression` is a keyword candidate if it has no tail, and its
* head expression is a `LocalVarHead` or `FreeVarHead` whose name is
* the keyword's name.
* - A `SubExpression`, `AppendStatement`, or `BlockStatement` is a keyword
* candidate if its head is a keyword candidate.
*
* The keyword infrastructure guarantees that:
*
* - If a node is not a keyword candidate, it is never passed to any keyword's
* `assert` method.
* - If a node is not the `KeywordType` for a particular keyword, it will not
* be passed to the keyword's `assert` method.
*
* `Expr` keywords are used in expression positions and should return HIR
* expressions. `Block` and `Append` keywords are used in statement
* positions and should return HIR statements.
*
* A keyword definition has two parts:
*
* - `match`, which determines whether an AST node matches the keyword, and can
* optionally return some information extracted from the AST node.
* - `translate`, which takes a matching AST node as well as the extracted
* information and returns an appropriate HIR instruction.
*
* # Example
*
* This keyword:
*
* - turns `(hello)` into `"hello"`
* - as long as `hello` is not in scope
* - makes it an error to pass any arguments (such as `(hello world)`)
*
* ```ts
* keywords('SubExpr').kw('hello', {
* assert(node: ExprKeywordNode): Result<void> | false {
* // we don't want to transform `hello` as a `PathExpression`
* if (node.type !== 'SubExpression') {
* return false;
* }
*
* // node.head would be `LocalVarHead` if `hello` was in scope
* if (node.head.type !== 'FreeVarHead') {
* return false;
* }
*
* if (node.params.length || node.hash) {
* return Err(generateSyntaxError(`(hello) does not take any arguments`), node.loc);
* } else {
* return Ok();
* }
* },
*
* translate(node: ASTv2.SubExpression): hir.Expression {
* return ASTv2.builders.literal("hello", node.loc)
* }
* })
* ```
*
* The keyword infrastructure checks to make sure that the node is the right
* type before calling `assert`, so you only need to consider `SubExpression`
* and `PathExpression` here. It also checks to make sure that the node passed
* to `assert` has the keyword name in the right place.
*
* Note the important difference between returning `false` from `assert`,
* which just means that the node didn't match, and returning `Err`, which
* means that the node matched, but there was a keyword-specific syntax
* error.
*/
export function keywords(type) {
return new Keywords(type);
}
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uLy4uL3BhY2thZ2VzL0BnbGltbWVyL2NvbXBpbGVyL2xpYi9wYXNzZXMvMS1ub3JtYWxpemF0aW9uL2tleXdvcmRzL2ltcGwudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsU0FFRSxtQkFGRixFQUdFLFNBSEYsRUFJRSxjQUpGLFFBTU8saUJBTlA7QUFPQSxTQUFTLFNBQVQsUUFBMEIsZUFBMUI7QUFFQSxTQUFTLEdBQVQsUUFBNEIsd0JBQTVCOztBQWdCQSxNQUFNLFdBQU4sQ0FBaUI7QUFRZixFQUFBLFdBQUEsQ0FDWSxPQURaLEVBRUUsSUFGRixFQUdVLFFBSFYsRUFHa0U7QUFGdEQsU0FBQSxPQUFBLEdBQUEsT0FBQTtBQUVGLFNBQUEsUUFBQSxHQUFBLFFBQUE7QUFFUixRQUFJLEtBQUssR0FBRyxJQUFJLEdBQUosRUFBWjs7QUFDQSxTQUFLLElBQUksUUFBVCxJQUFxQixhQUFhLENBQUMsSUFBRCxDQUFsQyxFQUEwQztBQUN4QyxNQUFBLEtBQUssQ0FBQyxHQUFOLENBQVUsUUFBVjtBQUNEOztBQUVELFNBQUssS0FBTCxHQUFhLEtBQWI7QUFDRDs7QUFFUyxFQUFBLEtBQUssQ0FBQyxJQUFELEVBQTJCO0FBQ3hDLFFBQUksQ0FBQyxLQUFLLEtBQUwsQ0FBVyxHQUFYLENBQWUsSUFBSSxDQUFDLElBQXBCLENBQUwsRUFBZ0M7QUFDOUIsYUFBTyxLQUFQO0FBQ0Q7O0FBRUQsUUFBSSxJQUFJLEdBQUcsbUJBQW1CLENBQUMsSUFBRCxDQUE5Qjs7QUFFQSxRQUFJLElBQUksS0FBSyxJQUFULElBQWlCLElBQUksQ0FBQyxJQUFMLEtBQWMsTUFBL0IsSUFBeUMsSUFBSSxDQUFDLEdBQUwsQ0FBUyxJQUFULEtBQWtCLE1BQS9ELEVBQXVFO0FBQ3JFLFVBQUksSUFBSSxDQUFDLElBQUwsQ0FBVSxNQUFWLEdBQW1CLENBQXZCLEVBQTBCO0FBQ3hCLFlBQUksSUFBSSxDQUFDLEdBQUwsQ0FBUyxVQUFULENBQW9CLFNB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NUQ7O0FBQ0YsV0FBSyxNQUFMO0FBQ0UsZUFBTywrQkFBK0IsSUFBSSxHQUExQzs7QUFDRixXQUFLLFVBQUw7QUFDRSxlQUFPLGtDQUFrQyxJQUFJLFdBQTdDOztBQUNGO0FBQ0UsZUFBTyxTQUFTLENBQUMsSUFBRCxDQUFoQjtBQVZKO0FBWUQsR0FkSSxFQWVKLElBZkksQ0FlQyxNQWZELENBQVA7QUFnQkQ7QUFFRDs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFpRkEsT0FBTSxTQUFVLFFBQVYsQ0FBMEMsSUFBMUMsRUFBaUQ7QUFDckQsU0FBTyxJQUFJLFFBQUosQ0FBYSxJQUFiLENBQVA7QUFDRCIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7XG4gIEFTVHYyLFxuICBnZW5lcmF0ZVN5bnRheEVycm9yLFxuICBpc0tleXdvcmQsXG4gIEtFWVdPUkRTX1RZUEVTLFxuICBLZXl3b3JkVHlwZSxcbn0gZnJvbSAnQGdsaW1tZXIvc3ludGF4JztcbmltcG9ydCB7IGV4aGF1c3RlZCB9IGZyb20gJ0BnbGltbWVyL3V0aWwnO1xuXG5pbXBvcnQgeyBFcnIsIFJlc3VsdCB9IGZyb20gJy4uLy4uLy4uL3NoYXJlZC9yZXN1bHQnO1xuaW1wb3J0IHsgTm9ybWFsaXphdGlvblN0YXRlIH0gZnJvbSAnLi4vY29udGV4dCc7XG5cbmV4cG9ydCBpbnRlcmZhY2UgS2V5d29yZERlbGVnYXRlPE1hdGNoIGV4dGVuZHMgS2V5d29yZE1hdGNoLCBWLCBPdXQ+IHtcbiAgYXNzZXJ0KG9wdGlvbnM6IE1hdGNoLCBzdGF0ZTogTm9ybWFsaXphdGlvblN0YXRlKTogUmVzdWx0PFY+O1xuICB0cmFuc2xhdGUob3B0aW9uczogeyBub2RlOiBNYXRjaDsgc3RhdGU6IE5vcm1hbGl6YXRpb25TdGF0ZSB9LCBwYXJhbTogVik6IFJlc3VsdDxPdXQ+O1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIEtleXdvcmQ8SyBleHRlbmRzIEtleXdvcmRUeXBlID0gS2V5d29yZFR5cGUsIE91dCA9IHVua25vd24+IHtcbiAgdHJhbnNsYXRlKG5vZGU6IEtleXdvcmRDYW5kaWRhdGVzW0tdLCBzdGF0ZTogTm9ybWFsaXphdGlvblN0YXRlKTogUmVzdWx0PE91dD4gfCBudWxsO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIEJsb2NrS2V5d29yZDxPdXQgPSB1bmtub3duPiB7XG4gIHRyYW5zbGF0ZShub2RlOiBBU1R2Mi5JbnZva2VCbG9jaywgc3RhdGU6IE5vcm1hbGl6YXRpb25TdGF0ZSk6IFJlc3VsdDxPdXQ+IHwgbnVsbDtcbn1cblxuY2xhc3MgS2V5d29yZEltcGw8XG4gIEsgZXh0ZW5kcyBLZXl3b3JkVHlwZSxcbiAgUyBleHRlbmRzIHN0cmluZyA9IHN0cmluZyxcbiAgUGFyYW0gPSB1bmtub3duLFxuICBPdXQgPSB1bmtub3duXG4+IHtcbiAgcHJvdGVjdGVkIHR5cGVzOiBTZXQ8S2V5d29yZENhbmRpZGF0ZXNbS11bJ3R5cGUnXT47XG5cbiAgY29uc3RydWN0b3IoXG4gICAgcHJvdGVjdGVkIGtleXdvcmQ6IFMsXG4gICAgdHlwZTogS2V5d29yZFR5cGUsXG4gICAgcHJpdmF0ZSBkZWxlZ2F0ZTogS2V5d29yZERlbGVnYXRlPEtleXdvcmRNYXRjaGVzW0tdLCBQYXJhbSwgT3V0PlxuICApIHtcbiAgICBsZXQgbm9kZXMgPSBuZXcgU2V0PEtleXdvcmROb2RlWyd0eXBlJ10+KCk7XG4gICAgZm9yIChsZXQgbm9kZVR5cGUgb2YgS0VZV09SRF9OT0RFU1t0eXBlXSkge1xuICAgICAgbm9kZXMuYWRkKG5vZGVUeXBlKTtcbiAgICB9XG5cbiAgICB0aGlzLnR5cGVzID0gbm9kZXM7XG4gIH1cblxuICBwcm90ZWN0ZWQgbWF0Y2gobm9kZTogS2V5d29yZENhbmRpZGF0ZXNbS10pOiBub2RlIGlzIEtleXdvcmRNYXRjaGVzW0tdIHtcbiAgICBpZiAoIXRoaXMudHlwZXMuaGFzKG5vZGUudHlwZSkpIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG5cbiAgICBsZXQgcGF0aCA9IGdldENhbGxlZUV4cHJlc3Npb24obm9kZSk7XG5cbiAgICBpZiAocGF0aCAhPT0gbnVsbCAmJiBwYXRoLnR5cGUgPT09ICdQYXRoJyAmJiBwYXRoLnJlZi50eXBlID09PSAnRnJlZScpIHtcbiAgICAgIGlmIChwYXRoLnRhaWwubGVuZ3RoID4gMCkge1xuICAgICAgICBpZiAocGF0aC5yZWYucmVzb2x1dGlvbi5zZXJpYWxpemUoKSA9PT0gJ0xvb3NlJykge1xuICAgICAgICAgIC8vIGNhbm5vdCBiZSBhIGtleXdvcmQgcmVmZXJlbmNlLCBrZXl3b3JkcyBkbyBub3QgYWxsb3cgcGF0aHMgKG11c3QgYmVcbiAgICAgICAgICAvLyByZWx5aW5nIG9uIGltcGxpY2l0IHRoaXMgZmFsbGJhY2spXG4gICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIHJldHVybiBwYXRoLnJlZi5uYW1lID09PSB0aGlzLmtleXdvcmQ7XG4gICAgfSBlbHNlIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gIH1cblxuICB0cmFuc2xhdGUobm9kZTogS2V5d29yZE1hdGNoZXNbS10sIHN0YXRlOiBOb3JtYWxpemF0aW9uU3RhdGUpOiBSZXN1bHQ8T3V0PiB8IG51bGwge1xuICAgIGlmICh0aGlzLm1hdGNoKG5vZGUpKSB7XG4gICAgICBsZXQgcGF0aCA9IGdldENhbGxlZUV4cHJlc3Npb24obm9kZSk7XG5cbiAgICAgIGlmIChwYXRoICE9PSBudWxsICYmIHBhdGgudHlwZSA9PT0gJ1BhdGgnICYmIHBhdGgudGFpbC5sZW5ndGggPiAwKSB7XG4gICAgICAgIHJldHVybiBFcnIoXG4gICAgICAgICAgZ2VuZXJhdGVTeW50YXhFcnJvcihcbiAgICAgICAgICAgIGBUaGUgXFxgJHtcbiAgICAgICAgICAgICAgdGhpcy5rZXl3b3JkXG4gICAgICAgICAgICB9XFxgIGtleXdvcmQgd2FzIHVzZWQgaW5jb3JyZWN0bHkuIEl0IHdhcyB1c2VkIGFzIFxcYCR7cGF0aC5sb2MuYXNTdHJpbmcoKX1cXGAsIGJ1dCBpdCBjYW5ub3QgYmUgdXNlZCB3aXRoIGFkZGl0aW9uYWwgcGF0aCBzZWdtZW50cy4gXFxuXFxuRXJyb3IgY2F1c2VkIGJ5YCxcbiAgICAgICAgICAgIG5vZGUubG9jXG4gICAgICAgICAgKVxuICAgICAgICApO1xuICAgICAgfVxuXG4gICAgICBsZXQgcGFyYW0gPSB0aGlzLmRlbGVnYXRlLmFzc2VydChub2RlLCBzdGF0ZSk7XG4gICAgICByZXR1cm4gcGFyYW0uYW5kVGhlbigocGFyYW0pID0+IHRoaXMuZGVsZWdhdGUudHJhbnNsYXRlKHsgbm9kZSwgc3RhdGUgfSwgcGFyYW0pKTtcbiAgICB9IGVsc2Uge1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuICB9XG59XG5cbmV4cG9ydCB0eXBlIFBvc3NpYmxlTm9kZSA9XG4gIHwgQVNUdjIuUGF0aEV4cHJlc3Npb25cbiAgfCBBU1R2Mi5BcHBlbmRDb250ZW50XG4gIHwgQVNUdjIuQ2FsbEV4cHJlc3Npb25cbiAgfCBBU1R2Mi5JbnZva2VCbG9jaztcblxuZXhwb3J0IGNvbnN0IEtFWVdPUkRfTk9ERVMgPSB7XG4gIENhbGw6IFsnQ2FsbCddLFxuICBCbG9jazogWydJbnZva2VCbG9jayddLFxuICBBcHBlbmQ6IFsnQXBwZW5kQ29udGVudCddLFxuICBNb2RpZmllcjogWydFbGVtZW50TW9kaWZpZXInXSxcbn0gYXMgY29uc3Q7XG5cbmV4cG9ydCBpbnRlcmZhY2UgS2V5d29yZENhbmRpZGF0ZXMge1xuICBDYWxsOiBBU1R2Mi5FeHByZXNzaW9uTm9kZTtcbiAgQmxvY2s6IEFTVHYyLkludm9rZUJsb2NrO1xuICBBcHBlbmQ6IEFTVHYyLkFwcGVuZENvbnRlbnQ7XG4gIE1vZGlmaWVyOiBBU1R2Mi5FbGVtZW50TW9kaWZpZXI7XG59XG5cbmV4cG9ydCB0eXBlIEtleXdvcmRDYW5kaWRhdGUgPSBLZXl3b3JkQ2FuZGlkYXRlc1trZXlvZiBLZXl3b3JkQ2FuZGlkYXRlc107XG5cbmV4cG9ydCBpbnRlcmZhY2UgS2V5d29yZE1hdGNoZXMge1xuICBDYWxsOiBBU1R2Mi5DYWxsRXhwcmVzc2lvbjtcbiAgQmxvY2s6IEFTVHYyLkludm9rZUJsb2NrO1xuICBBcHBlbmQ6IEFTVHYyLkFwcGVuZENvbnRlbnQ7XG4gIE1vZGlmaWVyOiBBU1R2Mi5FbGVtZW50TW9kaWZpZXI7XG59XG5cbmV4cG9ydCB0eXBlIEtleXdvcmRNYXRjaCA9IEtleXdvcmRNYXRjaGVzW2tleW9mIEtleXdvcmRNYXRjaGVzXTtcblxuLyoqXG4gKiBBIFwiZ2VuZXJpY1wiIGtleXdvcmQgaXMgc29tZXRoaW5nIGxpa2UgYGhhcy1ibG9ja2AsIHdoaWNoIG1ha2VzIHNlbnNlIGluIHRoZSBjb250ZXh0XG4gKiBvZiBzdWItZXhwcmVzc2lvbiBvciBhcHBlbmRcbiAqL1xuZXhwb3J0IHR5cGUgR2VuZXJpY0tleXdvcmROb2RlID0gQVNUdjIuQXBwZW5kQ29udGVudCB8IEFTVHYyLkNhbGxFeHByZXNzaW9uO1xuXG5leHBvcnQgdHlwZSBLZXl3b3JkTm9kZSA9XG4gIHwgR2VuZXJpY0tleXdvcmROb2RlXG4gIHwgQVNUdjIuQ2FsbEV4cHJlc3Npb25cbiAgfCBBU1R2Mi5JbnZva2VCbG9ja1xuICB8IEFTVHYyLkVsZW1lbnRNb2RpZmllcjtcblxuZXhwb3J0IGZ1bmN0aW9uIGtleXdvcmQ8XG4gIEsgZXh0ZW5kcyBLZXl3b3JkVHlwZSxcbiAgRCBleHRlbmRzIEtleXdvcmREZWxlZ2F0ZTxLZXl3b3JkTWF0Y2hlc1tLXSwgdW5rbm93biwgT3V0PixcbiAgT3V0ID0gdW5rbm93blxuPihrZXl3b3JkOiBzdHJpbmcsIHR5cGU6IEssIGRlbGVnYXRlOiBEKTogS2V5d29yZDxLLCBPdXQ+IHtcbiAgcmV0dXJuIG5ldyBLZXl3b3JkSW1wbChrZXl3b3JkLCB0eXBlLCBkZWxlZ2F0ZSBhcyBLZXl3b3JkRGVsZWdhdGU8S2V5d29yZE1hdGNoLCB1bmtub3duLCBPdXQ+KTtcbn1cblxuZXhwb3J0IHR5cGUgUG9zc2libGVLZXl3b3JkID0gS2V5d29yZE5vZGU7XG50eXBlIE91dEZvcjxLIGV4dGVuZHMgS2V5d29yZCB8IEJsb2NrS2V5d29yZD4gPSBLIGV4dGVuZHMgQmxvY2tLZXl3b3JkPGluZmVyIE91dD5cbiAgPyBPdXRcbiAgOiBLIGV4dGVuZHMgS2V5d29yZDxLZXl3b3JkVHlwZSwgaW5mZXIgT3V0PlxuICA/IE91dFxuICA6IG5ldmVyO1xuXG5mdW5jdGlvbiBnZXRDYWxsZWVFeHByZXNzaW9uKFxuICBub2RlOiBLZXl3b3JkTm9kZSB8IEFTVHYyLkV4cHJlc3Npb25Ob2RlXG4pOiBBU1R2Mi5FeHByZXNzaW9uTm9kZSB8IG51bGwge1xuICBzd2l0Y2ggKG5vZGUudHlwZSkge1xuICAgIC8vIFRoaXMgY292ZXJzIHRoZSBpbnNpZGUgb2YgYXR0cmlidXRlcyBhbmQgZXhwcmVzc2lvbnMsIGFzIHdlbGwgYXMgdGhlIGNhbGxlZVxuICAgIC8vIG9mIGNhbGwgbm9kZXNcbiAgICBjYXNlICdQYXRoJzpcbiAgICAgIHJldHVybiBub2RlO1xuICAgIGNhc2UgJ0FwcGVuZENvbnRlbnQnOlxuICAgICAgcmV0dXJuIGdldENhbGxlZUV4cHJlc3Npb24obm9kZS52YWx1ZSk7XG4gICAgY2FzZSAnQ2FsbCc6XG4gICAgY2FzZSAnSW52b2tlQmxvY2snOlxuICAgIGNhc2UgJ0VsZW1lbnRNb2RpZmllcic6XG4gICAgICByZXR1cm4gbm9kZS5jYWxsZWU7XG4gICAgZGVmYXVsdDpcbiAgICAgIHJldHVybiBudWxsO1xuICB9XG59XG5cbmV4cG9ydCBjbGFzcyBLZXl3b3JkczxLIGV4dGVuZHMgS2V5d29yZFR5cGUsIEtleXdvcmRMaXN0IGV4dGVuZHMgS2V5d29yZDxLPiA9IG5ldmVyPlxuICBpbXBsZW1lbnRzIEtleXdvcmQ8SywgT3V0Rm9yPEtleXdvcmRMaXN0Pj4ge1xuICBfa2V5d29yZHM6IEtleXdvcmRbXSA9IFtdO1xuICBfdHlwZTogSztcblxuICBjb25zdHJ1Y3Rvcih0eXBlOiBLKSB7XG4gICAgdGhpcy5fdHlwZSA9IHR5cGU7XG4gIH1cblxuICBrdzxTIGV4dGVuZHMgc3RyaW5nID0gc3RyaW5nLCBPdXQgPSB1bmtub3duPihcbiAgICBuYW1lOiBTLFxuICAgIGRlbGVnYXRlOiBLZXl3b3JkRGVsZWdhdGU8S2V5d29yZE1hdGNoZXNbS10sIHVua25vd24sIE91dD5cbiAgKTogS2V5d29yZHM8SywgS2V5d29yZExpc3QgfCBLZXl3b3JkPEssIE91dD4+IHtcbiAgICB0aGlzLl9rZXl3b3Jkcy5wdXNoKGtleXdvcmQobmFtZSwgdGhpcy5fdHlwZSwgZGVsZWdhdGUpKTtcblxuICAgIHJldHVybiB0aGlzO1xuICB9XG5cbiAgdHJhbnNsYXRlKFxuICAgIG5vZGU6IEtleXdvcmRDYW5kaWRhdGVzW0tdLFxuICAgIHN0YXRlOiBOb3JtYWxpemF0aW9uU3RhdGVcbiAgKTogUmVzdWx0PE91dEZvcjxLZXl3b3JkTGlzdD4+IHwgbnVsbCB7XG4gICAgZm9yIChsZXQga2V5d29yZCBvZiB0aGlzLl9rZXl3b3Jkcykge1xuICAgICAgbGV0IHJlc3VsdCA9IGtleXdvcmQudHJhbnNsYXRlKG5vZGUsIHN0YXRlKSBhcyBSZXN1bHQ8T3V0Rm9yPEtleXdvcmRMaXN0Pj47XG4gICAgICBpZiAocmVzdWx0ICE9PSBudWxsKSB7XG4gICAgICAgIHJldHVybiByZXN1bHQ7XG4gICAgICB9XG4gICAgfVxuXG4gICAgbGV0IHBhdGggPSBnZXRDYWxsZWVFeHByZXNzaW9uKG5vZGUpO1xuXG4gICAgaWYgKHBhdGggJiYgcGF0aC50eXBlID09PSAnUGF0aCcgJiYgcGF0aC5yZWYudHlwZSA9PT0gJ0ZyZWUnICYmIGlzS2V5d29yZChwYXRoLnJlZi5uYW1lKSkge1xuICAgICAgbGV0IHsgbmFtZSB9ID0gcGF0aC5yZWY7XG5cbiAgICAgIGxldCB1c2VkVHlwZSA9IHRoaXMuX3R5cGU7XG4gICAgICBsZXQgdmFsaWRUeXBlcyA9IEtFWVdPUkRTX1RZUEVTW25hbWVdO1xuXG4gICAgICBpZiAodmFsaWRUeXBlcy5pbmRleE9mKHVzZWRUeXBlKSA9PT0gLTEpIHtcbiAgICAgICAgcmV0dXJuIEVycihcbiAgICAgICAgICBnZW5lcmF0ZVN5bnRheEVycm9yKFxuICAgICAgICAgICAgYFRoZSBcXGAke25hbWV9XFxgIGtleXdvcmQgd2FzIHVzZWQgaW5jb3JyZWN0bHkuIEl0IHdhcyB1c2VkIGFzICR7XG4gICAgICAgICAgICAgIHR5cGVzVG9SZWFkYWJsZU5hbWVbdXNlZFR5cGVdXG4gICAgICAgICAgICB9LCBidXQgaXRzIHZhbGlkIHVzYWdlcyBhcmU6XFxuXFxuJHtnZW5lcmF0ZVR5cGVzTWVzc2FnZShcbiAgICAgICAgICAgICAgbmFtZSxcbiAgICAgICAgICAgICAgdmFsaWRUeXBlc1xuICAgICAgICAgICAgKX1cXG5cXG5FcnJvciBjYXVzZWQgYnlgLFxuICAgICAgICAgICAgbm9kZS5sb2NcbiAgICAgICAgICApXG4gICAgICAgICk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIG51bGw7XG4gIH1cbn1cblxuY29uc3QgdHlwZXNUb1JlYWRhYmxlTmFtZSA9IHtcbiAgQXBwZW5kOiAnYW4gYXBwZW5kIHN0YXRlbWVudCcsXG4gIEJsb2NrOiAnYSBibG9jayBzdGF0ZW1lbnQnLFxuICBDYWxsOiAnYSBjYWxsIGV4cHJlc3Npb24nLFxuICBNb2RpZmllcjogJ2EgbW9kaWZpZXInLFxufTtcblxuZnVuY3Rpb24gZ2VuZXJhdGVUeXBlc01lc3NhZ2UobmFtZTogc3RyaW5nLCB0eXBlczogS2V5d29yZFR5cGVbXSk6IHN0cmluZyB7XG4gIHJldHVybiB0eXBlc1xuICAgIC5tYXAoKHR5cGUpID0+IHtcbiAgICAgIHN3aXRjaCAodHlwZSkge1xuICAgICAgICBjYXNlICdBcHBlbmQnOlxuICAgICAgICAgIHJldHVybiBgLSBBcyBhbiBhcHBlbmQgc3RhdGVtZW50LCBhcyBpbjoge3ske25hbWV9fX1gO1xuICAgICAgICBjYXNlICdCbG9jayc6XG4gICAgICAgICAgcmV0dXJuIGAtIEFzIGEgYmxvY2sgc3RhdGVtZW50LCBhcyBpbjoge3sjJHtuYW1lfX19e3svJHtuYW1lfX19YDtcbiAgICAgICAgY2FzZSAnQ2FsbCc6XG4gICAgICAgICAgcmV0dXJuIGAtIEFzIGFuIGV4cHJlc3Npb24sIGFzIGluOiAoJHtuYW1lfSlgO1xuICAgICAgICBjYXNlICdNb2RpZmllcic6XG4gICAgICAgICAgcmV0dXJuIGAtIEFzIGEgbW9kaWZpZXIsIGFzIGluOiA8ZGl2IHt7JHtuYW1lfX19PjwvZGl2PmA7XG4gICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgcmV0dXJuIGV4aGF1c3RlZCh0eXBlKTtcbiAgICAgIH1cbiAgICB9KVxuICAgIC5qb2luKCdcXG5cXG4nKTtcbn1cblxuLyoqXG4gKiBUaGlzIGZ1bmN0aW9uIGJ1aWxkcyBrZXl3b3JkIGRlZmluaXRpb25zIGZvciBhIHBhcnRpY3VsYXIgdHlwZSBvZiBBU1Qgbm9kZSAoYEtleXdvcmRUeXBlYCkuXG4gKlxuICogWW91IGNhbiBidWlsZCBrZXl3b3JkIGRlZmluaXRpb25zIGZvcjpcbiAqXG4gKiAtIGBFeHByYDogQSBgU3ViRXhwcmVzc2lvbmAgb3IgYFBhdGhFeHByZXNzaW9uYFxuICogLSBgQmxvY2tgOiBBIGBCbG9ja1N0YXRlbWVudGBcbiAqICAgLSBBIGBCbG9ja1N0YXRlbWVudGAgaXMgYSBrZXl3b3JkIGNhbmRpZGF0ZSBpZiBpdHMgaGVhZCBpcyBhXG4gKiAgICAgYFBhdGhFeHByZXNzaW9uYFxuICogLSBgQXBwZW5kYDogQW4gYEFwcGVuZFN0YXRlbWVudGBcbiAqXG4gKiBBIG5vZGUgaXMgYSBrZXl3b3JkIGNhbmRpZGF0ZSBpZjpcbiAqXG4gKiAtIEEgYFBhdGhFeHByZXNzaW9uYCBpcyBhIGtleXdvcmQgY2FuZGlkYXRlIGlmIGl0IGhhcyBubyB0YWlsLCBhbmQgaXRzXG4gKiAgIGhlYWQgZXhwcmVzc2lvbiBpcyBhIGBMb2NhbFZhckhlYWRgIG9yIGBGcmVlVmFySGVhZGAgd2hvc2UgbmFtZSBpc1xuICogICB0aGUga2V5d29yZCdzIG5hbWUuXG4gKiAtIEEgYFN1YkV4cHJlc3Npb25gLCBgQXBwZW5kU3RhdGVtZW50YCwgb3IgYEJsb2NrU3RhdGVtZW50YCBpcyBhIGtleXdvcmRcbiAqICAgY2FuZGlkYXRlIGlmIGl0cyBoZWFkIGlzIGEga2V5d29yZCBjYW5kaWRhdGUuXG4gKlxuICogVGhlIGtleXdvcmQgaW5mcmFzdHJ1Y3R1cmUgZ3VhcmFudGVlcyB0aGF0OlxuICpcbiAqIC0gSWYgYSBub2RlIGlzIG5vdCBhIGtleXdvcmQgY2FuZGlkYXRlLCBpdCBpcyBuZXZlciBwYXNzZWQgdG8gYW55IGtleXdvcmQnc1xuICogICBgYXNzZXJ0YCBtZXRob2QuXG4gKiAtIElmIGEgbm9kZSBpcyBub3QgdGhlIGBLZXl3b3JkVHlwZWAgZm9yIGEgcGFydGljdWxhciBrZXl3b3JkLCBpdCB3aWxsIG5vdFxuICogICBiZSBwYXNzZWQgdG8gdGhlIGtleXdvcmQncyBgYXNzZXJ0YCBtZXRob2QuXG4gKlxuICogYEV4cHJgIGtleXdvcmRzIGFyZSB1c2VkIGluIGV4cHJlc3Npb24gcG9zaXRpb25zIGFuZCBzaG91bGQgcmV0dXJuIEhJUlxuICogZXhwcmVzc2lvbnMuIGBCbG9ja2AgYW5kIGBBcHBlbmRgIGtleXdvcmRzIGFyZSB1c2VkIGluIHN0YXRlbWVudFxuICogcG9zaXRpb25zIGFuZCBzaG91bGQgcmV0dXJuIEhJUiBzdGF0ZW1lbnRzLlxuICpcbiAqIEEga2V5d29yZCBkZWZpbml0aW9uIGhhcyB0d28gcGFydHM6XG4gKlxuICogLSBgbWF0Y2hgLCB3aGljaCBkZXRlcm1pbmVzIHdoZXRoZXIgYW4gQVNUIG5vZGUgbWF0Y2hlcyB0aGUga2V5d29yZCwgYW5kIGNhblxuICogICBvcHRpb25hbGx5IHJldHVybiBzb21lIGluZm9ybWF0aW9uIGV4dHJhY3RlZCBmcm9tIHRoZSBBU1Qgbm9kZS5cbiAqIC0gYHRyYW5zbGF0ZWAsIHdoaWNoIHRha2VzIGEgbWF0Y2hpbmcgQVNUIG5vZGUgYXMgd2VsbCBhcyB0aGUgZXh0cmFjdGVkXG4gKiAgIGluZm9ybWF0aW9uIGFuZCByZXR1cm5zIGFuIGFwcHJvcHJpYXRlIEhJUiBpbnN0cnVjdGlvbi5cbiAqXG4gKiAjIEV4YW1wbGVcbiAqXG4gKiBUaGlzIGtleXdvcmQ6XG4gKlxuICogLSB0dXJucyBgKGhlbGxvKWAgaW50byBgXCJoZWxsb1wiYFxuICogICAtIGFzIGxvbmcgYXMgYGhlbGxvYCBpcyBub3QgaW4gc2NvcGVcbiAqIC0gbWFrZXMgaXQgYW4gZXJyb3IgdG8gcGFzcyBhbnkgYXJndW1lbnRzIChzdWNoIGFzIGAoaGVsbG8gd29ybGQpYClcbiAqXG4gKiBgYGB0c1xuICoga2V5d29yZHMoJ1N1YkV4cHInKS5rdygnaGVsbG8nLCB7XG4gKiAgIGFzc2VydChub2RlOiBFeHByS2V5d29yZE5vZGUpOiBSZXN1bHQ8dm9pZD4gfCBmYWxzZSB7XG4gKiAgICAgLy8gd2UgZG9uJ3Qgd2FudCB0byB0cmFuc2Zvcm0gYGhlbGxvYCBhcyBhIGBQYXRoRXhwcmVzc2lvbmBcbiAqICAgICBpZiAobm9kZS50eXBlICE9PSAnU3ViRXhwcmVzc2lvbicpIHtcbiAqICAgICAgIHJldHVybiBmYWxzZTtcbiAqICAgICB9XG4gKlxuICogICAgIC8vIG5vZGUuaGVhZCB3b3VsZCBiZSBgTG9jYWxWYXJIZWFkYCBpZiBgaGVsbG9gIHdhcyBpbiBzY29wZVxuICogICAgIGlmIChub2RlLmhlYWQudHlwZSAhPT0gJ0ZyZWVWYXJIZWFkJykge1xuICogICAgICAgcmV0dXJuIGZhbHNlO1xuICogICAgIH1cbiAqXG4gKiAgICAgaWYgKG5vZGUucGFyYW1zLmxlbmd0aCB8fCBub2RlLmhhc2gpIHtcbiAqICAgICAgIHJldHVybiBFcnIoZ2VuZXJhdGVTeW50YXhFcnJvcihgKGhlbGxvKSBkb2VzIG5vdCB0YWtlIGFueSBhcmd1bWVudHNgKSwgbm9kZS5sb2MpO1xuICogICAgIH0gZWxzZSB7XG4gKiAgICAgICByZXR1cm4gT2soKTtcbiAqICAgICB9XG4gKiAgIH0sXG4gKlxuICogICB0cmFuc2xhdGUobm9kZTogQVNUdjIuU3ViRXhwcmVzc2lvbik6IGhpci5FeHByZXNzaW9uIHtcbiAqICAgICByZXR1cm4gQVNUdjIuYnVpbGRlcnMubGl0ZXJhbChcImhlbGxvXCIsIG5vZGUubG9jKVxuICogICB9XG4gKiB9KVxuICogYGBgXG4gKlxuICogVGhlIGtleXdvcmQgaW5mcmFzdHJ1Y3R1cmUgY2hlY2tzIHRvIG1ha2Ugc3VyZSB0aGF0IHRoZSBub2RlIGlzIHRoZSByaWdodFxuICogdHlwZSBiZWZvcmUgY2FsbGluZyBgYXNzZXJ0YCwgc28geW91IG9ubHkgbmVlZCB0byBjb25zaWRlciBgU3ViRXhwcmVzc2lvbmBcbiAqIGFuZCBgUGF0aEV4cHJlc3Npb25gIGhlcmUuIEl0IGFsc28gY2hlY2tzIHRvIG1ha2Ugc3VyZSB0aGF0IHRoZSBub2RlIHBhc3NlZFxuICogdG8gYGFzc2VydGAgaGFzIHRoZSBrZXl3b3JkIG5hbWUgaW4gdGhlIHJpZ2h0IHBsYWNlLlxuICpcbiAqIE5vdGUgdGhlIGltcG9ydGFudCBkaWZmZXJlbmNlIGJldHdlZW4gcmV0dXJuaW5nIGBmYWxzZWAgZnJvbSBgYXNzZXJ0YCxcbiAqIHdoaWNoIGp1c3QgbWVhbnMgdGhhdCB0aGUgbm9kZSBkaWRuJ3QgbWF0Y2gsIGFuZCByZXR1cm5pbmcgYEVycmAsIHdoaWNoXG4gKiBtZWFucyB0aGF0IHRoZSBub2RlIG1hdGNoZWQsIGJ1dCB0aGVyZSB3YXMgYSBrZXl3b3JkLXNwZWNpZmljIHN5bnRheFxuICogZXJyb3IuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBrZXl3b3JkczxLIGV4dGVuZHMgS2V5d29yZFR5cGU+KHR5cGU6IEspOiBLZXl3b3JkczxLPiB7XG4gIHJldHVybiBuZXcgS2V5d29yZHModHlwZSk7XG59XG4iXSwic291cmNlUm9vdCI6IiJ9