polymer-analyzer
Version:
Static analysis for Web Components
208 lines (206 loc) • 6.34 kB
JavaScript
"use strict";
/**
* @license
* Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
* This code may only be used under the BSD style license found at
* http://polymer.github.io/LICENSE.txt
* The complete set of authors may be found at
* http://polymer.github.io/AUTHORS.txt
* The complete set of contributors may be found at
* http://polymer.github.io/CONTRIBUTORS.txt
* Code distributed by Google as part of the polymer project is also
* subject to an additional IP rights grant found at
* http://polymer.github.io/PATENTS.txt
*/
Object.defineProperty(exports, "__esModule", { value: true });
const jsdoc = require("./jsdoc");
/**
* Converts an ast literal to its underlying valie.
*/
function literalToValue(literal) {
return literal.value;
}
/**
* Early evaluates a unary expression.
*/
function unaryToValue(unary) {
const operand = expressionToValue(unary.argument);
switch (unary.operator) {
case '!':
return !operand;
case '-':
return -operand;
case '+':
return +operand;
case '~':
return ~operand;
case 'typeof':
return typeof operand;
case 'void':
return void operand;
case 'delete':
return undefined;
default:
const never = unary.operator;
throw new Error(`Unknown unary operator found: ${never}`);
}
}
/**
* Try to evaluate function bodies.
*/
function functionDeclarationToValue(fn) {
if (fn.body.type === 'BlockStatement') {
return blockStatementToValue(fn.body);
}
}
function functionExpressionToValue(fn) {
if (fn.body.type === 'BlockStatement') {
return blockStatementToValue(fn.body);
}
}
function arrowFunctionExpressionToValue(fn) {
if (fn.body.type === 'BlockStatement') {
return blockStatementToValue(fn.body);
}
else {
return expressionToValue(fn.body);
}
}
/**
* Block statement: find last return statement, and return its value
*/
function blockStatementToValue(block) {
for (let i = block.body.length - 1; i >= 0; i--) {
const body = block.body[i];
if (body.type === 'ReturnStatement') {
return returnStatementToValue(body);
}
}
}
/**
* Evaluates return's argument
*/
function returnStatementToValue(ret) {
return expressionToValue(ret.argument || { type: 'Literal', value: null, raw: 'null' });
}
/**
* Evaluate array expression
*/
function arrayExpressionToValue(arry) {
const value = [];
for (let i = 0; i < arry.elements.length; i++) {
const v = expressionToValue(arry.elements[i]);
if (v === undefined) {
continue;
}
value.push(v);
}
return value;
}
/**
* Evaluate object expression
*/
function objectExpressionToValue(obj) {
const evaluatedObjectExpression = {};
for (const prop of obj.properties) {
if (prop.key.type !== 'Literal') {
return;
}
const evaluatedKey = '' + literalToValue(prop.key);
const evaluatedValue = expressionToValue(prop.value);
if (evaluatedValue === undefined) {
return;
}
evaluatedObjectExpression[evaluatedKey] = evaluatedValue;
}
return evaluatedObjectExpression;
}
/**
* Binary expressions, like 5 + 5
*/
function binaryExpressionToValue(member) {
const left = expressionToValue(member.left);
const right = expressionToValue(member.right);
if (left == null || right == null) {
return;
}
if (member.operator === '+') {
// We need to cast to `any` here because, while it's usually not the right
// thing to do to use '+' on two values of a mix of types because it's
// unpredictable, that is what the original code we're evaluating does.
return left + right;
}
return;
}
/**
* Tries to get the value of an expression. Returns undefined on failure.
*/
function expressionToValue(valueExpression) {
switch (valueExpression.type) {
case 'Literal':
return literalToValue(valueExpression);
case 'UnaryExpression':
return unaryToValue(valueExpression);
case 'FunctionDeclaration':
return functionDeclarationToValue(valueExpression);
case 'FunctionExpression':
return functionExpressionToValue(valueExpression);
case 'ArrowFunctionExpression':
return arrowFunctionExpressionToValue(valueExpression);
case 'ArrayExpression':
return arrayExpressionToValue(valueExpression);
case 'ObjectExpression':
return objectExpressionToValue(valueExpression);
case 'BinaryExpression':
return binaryExpressionToValue(valueExpression);
default:
return;
}
}
exports.expressionToValue = expressionToValue;
/**
* Extracts the name of the identifier or `.` separated chain of identifiers.
*
* Returns undefined if the given node isn't a simple identifier or chain of
* simple identifiers.
*/
function getIdentifierName(node) {
if (node.type === 'Identifier') {
return node.name;
}
if (node.type === 'MemberExpression') {
const object = getIdentifierName(node.object);
let property;
if (node.computed) {
property = expressionToValue(node.property);
}
else {
property = getIdentifierName(node.property);
}
if (object != null && property != null) {
return `${object}.${property}`;
}
}
}
exports.getIdentifierName = getIdentifierName;
/**
* Formats the given identifier name under a namespace, if one is mentioned in
* the commentedNode's comment. Otherwise, name is returned.
*/
function getNamespacedIdentifier(name, docs) {
if (!docs) {
return name;
}
const memberofTag = jsdoc.getTag(docs, 'memberof');
const namespace = memberofTag && memberofTag.description;
if (namespace) {
const rightMostIdentifierName = name.substring(name.lastIndexOf('.') + 1);
return namespace + '.' + rightMostIdentifierName;
}
else {
return name;
}
}
exports.getNamespacedIdentifier = getNamespacedIdentifier;
exports.CANT_CONVERT = 'UNKNOWN';
//# sourceMappingURL=ast-value.js.map