mirror of
https://github.com/react/react-native-devtools-frontend.git
synced 2026-10-12 17:45:49 +08:00
Bug: 1006759 Change-Id: I895bd53ff3f5381cc7e159f0813b426f34e8fef7 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/1991638 Commit-Queue: Tim van der Lippe <tvanderlippe@chromium.org> Reviewed-by: Paul Lewis <aerotwist@chromium.org>
186 lines
4.8 KiB
JavaScript
186 lines
4.8 KiB
JavaScript
// Copyright 2016 The Chromium Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
import {ESTreeWalker} from './ESTreeWalker.js';
|
|
|
|
export const RelaxedJSONParser = {
|
|
/**
|
|
* @param {string} content
|
|
* @return {*}
|
|
*/
|
|
parse(content) {
|
|
content = '(' + content + ')';
|
|
|
|
let root;
|
|
try {
|
|
root = acorn.parse(content, {});
|
|
} catch (e) {
|
|
return null;
|
|
}
|
|
|
|
const walker = new ESTreeWalker(beforeVisit, afterVisit);
|
|
|
|
const rootTip = [];
|
|
|
|
/** @type {!Array.<!FormatterWorker.RelaxedJSONParser.Context>} */
|
|
const stack = [];
|
|
|
|
let stackData = /** @type {!FormatterWorker.RelaxedJSONParser.Context} */ (
|
|
{key: 0, tip: rootTip, state: States.ExpectValue, parentIsArray: true});
|
|
|
|
walker.setWalkNulls(true);
|
|
let hasExpression = false;
|
|
|
|
walker.walk(root);
|
|
|
|
if (hasExpression) {
|
|
return null;
|
|
}
|
|
return rootTip.length ? rootTip[0] : null;
|
|
|
|
/**
|
|
* @param {!FormatterWorker.RelaxedJSONParser.Context} newStack
|
|
*/
|
|
function pushStack(newStack) {
|
|
stack.push(stackData);
|
|
stackData = newStack;
|
|
}
|
|
|
|
function popStack() {
|
|
stackData = stack.pop();
|
|
}
|
|
|
|
/**
|
|
* @param {*} value
|
|
*/
|
|
function applyValue(value) {
|
|
stackData.tip[stackData.key] = value;
|
|
if (stackData.parentIsArray) {
|
|
stackData.key++;
|
|
} else {
|
|
stackData.state = null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {!ESTree.Node} node
|
|
* @return {!Object|undefined}
|
|
*/
|
|
function beforeVisit(node) {
|
|
switch (node.type) {
|
|
case 'ObjectExpression': {
|
|
const newTip = {};
|
|
applyValue(newTip);
|
|
|
|
pushStack(/** @type {!FormatterWorker.RelaxedJSONParser.Context} */ (
|
|
{key: null, tip: newTip, state: null, parentIsArray: false}));
|
|
break;
|
|
}
|
|
case 'ArrayExpression': {
|
|
const newTip = [];
|
|
applyValue(newTip);
|
|
|
|
pushStack(/** @type {!FormatterWorker.RelaxedJSONParser.Context} */ (
|
|
{key: 0, tip: newTip, state: States.ExpectValue, parentIsArray: true}));
|
|
break;
|
|
}
|
|
case 'Property':
|
|
stackData.state = States.ExpectKey;
|
|
break;
|
|
case 'Literal':
|
|
if (stackData.state === States.ExpectKey) {
|
|
stackData.key = node.value;
|
|
stackData.state = States.ExpectValue;
|
|
} else if (stackData.state === States.ExpectValue) {
|
|
applyValue(extractValue(node));
|
|
return ESTreeWalker.SkipSubtree;
|
|
}
|
|
break;
|
|
case 'Identifier':
|
|
if (stackData.state === States.ExpectKey) {
|
|
stackData.key = /** @type {string} */ (node.name);
|
|
stackData.state = States.ExpectValue;
|
|
} else if (stackData.state === States.ExpectValue) {
|
|
applyValue(extractValue(node));
|
|
return ESTreeWalker.SkipSubtree;
|
|
}
|
|
break;
|
|
case 'UnaryExpression':
|
|
if (stackData.state === States.ExpectValue) {
|
|
applyValue(extractValue(node));
|
|
return ESTreeWalker.SkipSubtree;
|
|
}
|
|
break;
|
|
case 'Program':
|
|
case 'ExpressionStatement':
|
|
break;
|
|
default:
|
|
if (stackData.state === States.ExpectValue) {
|
|
applyValue(extractValue(node));
|
|
}
|
|
return ESTreeWalker.SkipSubtree;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {!ESTree.Node} node
|
|
*/
|
|
function afterVisit(node) {
|
|
if (node.type === 'ObjectExpression' || node.type === 'ArrayExpression') {
|
|
popStack();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {!ESTree.Node} node
|
|
* @return {*}
|
|
*/
|
|
function extractValue(node) {
|
|
let isNegative = false;
|
|
const originalNode = node;
|
|
let value;
|
|
if (node.type === 'UnaryExpression' && (node.operator === '-' || node.operator === '+')) {
|
|
if (node.operator === '-') {
|
|
isNegative = true;
|
|
}
|
|
node = /** @type {!ESTree.Node} */ (node.argument);
|
|
}
|
|
|
|
if (node.type === 'Literal') {
|
|
value = node.value;
|
|
} else if (node.type === 'Identifier' && Keywords.hasOwnProperty(node.name)) {
|
|
value = Keywords[node.name];
|
|
} else {
|
|
hasExpression = true;
|
|
return content.substring(originalNode.start, originalNode.end);
|
|
}
|
|
|
|
if (isNegative) {
|
|
if (typeof value !== 'number') {
|
|
hasExpression = true;
|
|
return content.substring(originalNode.start, originalNode.end);
|
|
}
|
|
value = -(value);
|
|
}
|
|
return value;
|
|
}
|
|
}
|
|
};
|
|
|
|
/** @enum {string} */
|
|
export const States = {
|
|
ExpectKey: 'ExpectKey',
|
|
ExpectValue: 'ExpectValue'
|
|
};
|
|
|
|
/** @enum {*} */
|
|
export const Keywords = {
|
|
'NaN': NaN,
|
|
'true': true,
|
|
'false': false,
|
|
'Infinity': Infinity,
|
|
'undefined': undefined,
|
|
'null': null
|
|
};
|