summaryrefslogtreecommitdiffstats
path: root/devtools/client/debugger/src/workers/parser/utils/helpers.js
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/client/debugger/src/workers/parser/utils/helpers.js')
-rw-r--r--devtools/client/debugger/src/workers/parser/utils/helpers.js232
1 files changed, 232 insertions, 0 deletions
diff --git a/devtools/client/debugger/src/workers/parser/utils/helpers.js b/devtools/client/debugger/src/workers/parser/utils/helpers.js
new file mode 100644
index 0000000000..0045b54136
--- /dev/null
+++ b/devtools/client/debugger/src/workers/parser/utils/helpers.js
@@ -0,0 +1,232 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
+
+import * as t from "@babel/types";
+import generate from "@babel/generator";
+
+export function isFunction(node) {
+ return (
+ t.isFunction(node) ||
+ t.isArrowFunctionExpression(node) ||
+ t.isObjectMethod(node) ||
+ t.isClassMethod(node)
+ );
+}
+
+export function isAwaitExpression(path) {
+ const { node, parent } = path;
+ return (
+ t.isAwaitExpression(node) ||
+ t.isAwaitExpression(parent.init) ||
+ t.isAwaitExpression(parent)
+ );
+}
+
+export function isYieldExpression(path) {
+ const { node, parent } = path;
+ return (
+ t.isYieldExpression(node) ||
+ t.isYieldExpression(parent.init) ||
+ t.isYieldExpression(parent)
+ );
+}
+
+export function isObjectShorthand(parent) {
+ if (!t.isObjectProperty(parent)) {
+ return false;
+ }
+
+ if (parent.value && parent.value.left) {
+ return (
+ parent.value.type === "AssignmentPattern" &&
+ parent.value.left.type === "Identifier"
+ );
+ }
+
+ return (
+ parent.value &&
+ parent.key.start == parent.value.start &&
+ parent.key.loc.identifierName === parent.value.loc.identifierName
+ );
+}
+
+export function getObjectExpressionValue(node) {
+ const { value } = node;
+
+ if (t.isIdentifier(value)) {
+ return value.name;
+ }
+
+ if (t.isCallExpression(value) || t.isFunctionExpression(value)) {
+ return "";
+ }
+ const code = generate(value).code;
+
+ const shouldWrap = t.isObjectExpression(value);
+ return shouldWrap ? `(${code})` : code;
+}
+
+export function getCode(node) {
+ return generate(node).code;
+}
+
+export function getComments(ast) {
+ if (!ast || !ast.comments) {
+ return [];
+ }
+ return ast.comments.map(comment => ({
+ name: comment.location,
+ location: comment.loc,
+ }));
+}
+
+export function isComputedExpression(expression) {
+ return /^\[/m.test(expression);
+}
+
+export function getMemberExpression(root) {
+ function _getMemberExpression(node, expr) {
+ if (t.isMemberExpression(node)) {
+ expr = [node.property.name].concat(expr);
+ return _getMemberExpression(node.object, expr);
+ }
+
+ if (t.isCallExpression(node)) {
+ return [];
+ }
+
+ if (t.isThisExpression(node)) {
+ return ["this"].concat(expr);
+ }
+
+ return [node.name].concat(expr);
+ }
+
+ const expr = _getMemberExpression(root, []);
+ return expr.join(".");
+}
+
+export function getVariables(dec) {
+ if (!dec.id) {
+ return [];
+ }
+
+ if (t.isArrayPattern(dec.id)) {
+ if (!dec.id.elements) {
+ return [];
+ }
+
+ // NOTE: it's possible that an element is empty or has several variables
+ // e.g. const [, a] = arr
+ // e.g. const [{a, b }] = 2
+ return dec.id.elements
+ .filter(Boolean)
+ .map(element => ({
+ name: t.isAssignmentPattern(element)
+ ? element.left.name
+ : element.name || element.argument?.name,
+ location: element.loc,
+ }))
+ .filter(({ name }) => name);
+ }
+
+ return [
+ {
+ name: dec.id.name,
+ location: dec.loc,
+ },
+ ];
+}
+
+/**
+ * Add the identifiers for a given object pattern.
+ *
+ * @param {Array.<Object>} identifiers
+ * the current list of identifiers where to push the new identifiers
+ * related to this path.
+ * @param {Set<String>} identifiersKeys
+ * List of currently registered identifier location key.
+ * @param {Object} pattern
+ */
+export function addPatternIdentifiers(identifiers, identifiersKeys, pattern) {
+ let items;
+ if (t.isObjectPattern(pattern)) {
+ items = pattern.properties.map(({ value }) => value);
+ }
+
+ if (t.isArrayPattern(pattern)) {
+ items = pattern.elements;
+ }
+
+ if (items) {
+ addIdentifiers(identifiers, identifiersKeys, items);
+ }
+}
+
+function addIdentifiers(identifiers, identifiersKeys, items) {
+ for (const item of items) {
+ if (t.isObjectPattern(item) || t.isArrayPattern(item)) {
+ addPatternIdentifiers(identifiers, identifiersKeys, item);
+ } else if (t.isIdentifier(item)) {
+ if (!identifiersKeys.has(nodeLocationKey(item.loc))) {
+ identifiers.push({
+ name: item.name,
+ expression: item.name,
+ location: item.loc,
+ });
+ }
+ }
+ }
+}
+
+// Top Level checks the number of "body" nodes in the ancestor chain
+// if the node is top-level, then it shoul only have one body.
+export function isTopLevel(ancestors) {
+ return ancestors.filter(ancestor => ancestor.key == "body").length == 1;
+}
+
+export function nodeLocationKey({ start, end }) {
+ return `${start.line}:${start.column}:${end.line}:${end.column}`;
+}
+
+export function getFunctionParameterNames(path) {
+ if (path.node.params != null) {
+ return path.node.params.map(param => {
+ if (param.type !== "AssignmentPattern") {
+ return param.name;
+ }
+
+ // Parameter with default value
+ if (
+ param.left.type === "Identifier" &&
+ param.right.type === "Identifier"
+ ) {
+ return `${param.left.name} = ${param.right.name}`;
+ } else if (
+ param.left.type === "Identifier" &&
+ param.right.type === "StringLiteral"
+ ) {
+ return `${param.left.name} = ${param.right.value}`;
+ } else if (
+ param.left.type === "Identifier" &&
+ param.right.type === "ObjectExpression"
+ ) {
+ return `${param.left.name} = {}`;
+ } else if (
+ param.left.type === "Identifier" &&
+ param.right.type === "ArrayExpression"
+ ) {
+ return `${param.left.name} = []`;
+ } else if (
+ param.left.type === "Identifier" &&
+ param.right.type === "NullLiteral"
+ ) {
+ return `${param.left.name} = null`;
+ }
+
+ return null;
+ });
+ }
+ return [];
+}