From 65f90025a07c3ae51e0429f985d89d0341e44ca2 Mon Sep 17 00:00:00 2001 From: M Starch Date: Wed, 5 Feb 2025 18:44:00 -0800 Subject: [PATCH 1/3] Fixes nasa/fprime#3174 by fixing interanl and JSON large number handling --- src/fprime_gds/common/data_types/cmd_data.py | 4 +- src/fprime_gds/flask/commands.py | 2 +- .../static/addons/commanding/arguments.js | 70 +++-- .../addons/commanding/command-history.js | 25 +- .../static/addons/commanding/command-input.js | 7 +- .../commanding/command-string-template.js | 3 +- .../addons/commanding/command-string.js | 6 +- .../flask/static/addons/sequencer/addon.js | 2 + src/fprime_gds/flask/static/js/json.js | 275 ++++++++++++++++++ src/fprime_gds/flask/static/js/loader.js | 117 +------- 10 files changed, 349 insertions(+), 162 deletions(-) create mode 100644 src/fprime_gds/flask/static/js/json.js diff --git a/src/fprime_gds/common/data_types/cmd_data.py b/src/fprime_gds/common/data_types/cmd_data.py index b12c285d..ea985833 100644 --- a/src/fprime_gds/common/data_types/cmd_data.py +++ b/src/fprime_gds/common/data_types/cmd_data.py @@ -148,13 +148,13 @@ def process_args(self, input_values): args = [] for val, arg_tuple in zip(input_values, self.template.arguments): try: - _, _, arg_type = arg_tuple + arg_name, _, arg_type = arg_tuple arg_value = arg_type() self.convert_arg_value(val, arg_value) args.append(arg_value) errors.append("") except Exception as exc: - errors.append(str(exc)) + errors.append(f"{arg_name}[{arg_type.__name__}]: {exc}") return args, errors @staticmethod diff --git a/src/fprime_gds/flask/commands.py b/src/fprime_gds/flask/commands.py index 1e8a67e7..3ce20aa7 100644 --- a/src/fprime_gds/flask/commands.py +++ b/src/fprime_gds/flask/commands.py @@ -49,7 +49,7 @@ class CommandArgumentsInvalidException(werkzeug.exceptions.BadRequest): """Command arguments failed to validate properly""" def __init__(self, errors): - super().__init__("Failed to validate all arguments") + super().__init__(f"Failed to validate all arguments: {', '.join(errors)}") self.args = errors diff --git a/src/fprime_gds/flask/static/addons/commanding/arguments.js b/src/fprime_gds/flask/static/addons/commanding/arguments.js index 7dc71f03..ce8e9d00 100644 --- a/src/fprime_gds/flask/static/addons/commanding/arguments.js +++ b/src/fprime_gds/flask/static/addons/commanding/arguments.js @@ -74,7 +74,7 @@ export function command_argument_assignment_helper(argument, squashed_argument_v command_argument_array_serializable_assignment_helper(argument, squashed_argument_value); } else { let is_not_string = typeof(argument.type.MAX_LENGTH) === "undefined"; - argument.value = (is_not_string && (squashed_argument_value === FILL_NEEDED)) ? null : squashed_argument_value; + argument.value = (is_not_string && (squashed_argument_value === FILL_NEEDED)) ? null : squashed_argument_value.toString(); } } @@ -119,7 +119,21 @@ export function squashify_argument(argument) { let field = argument.type.MEMBER_LIST[i][0]; value[field] = squashify_argument(argument.value[field]); } - } else if (["U64Type", "U32Type", "U16Type", "U8Type"].indexOf(argument.type.name) != -1) { + } else if (["U64Type"].indexOf(argument.type.name) !== -1) { + if (argument.value.startsWith("0x")) { + // Hexadecimal + value = BigInt(argument.value, 16); + } else if (argument.value.startsWith("0b")) { + // Binary + value = BigInt(argument.value.slice(2), 2); + } else if (argument.value.startsWith("0o")) { + // Octal + value = BigInt(argument.value.slice(2), 8); + } else { + // Decimal + value = BigInt(argument.value, 10); + } + } else if (["U32Type", "U16Type", "U8Type"].indexOf(argument.type.name) !== -1) { if (argument.value.startsWith("0x")) { // Hexadecimal value = parseInt(argument.value, 16); @@ -134,10 +148,13 @@ export function squashify_argument(argument) { value = parseInt(argument.value, 10); } } - else if (["I64Type", "I32Type", "I16Type", "I8Type"].indexOf(argument.type.name) != -1) { + else if (["I64Type"].indexOf(argument.type.name) !== -1) { + value = BigInt(argument.value, 10); + } + else if (["I32Type", "I16Type", "I8Type"].indexOf(argument.type.name) !== -1) { value = parseInt(argument.value, 10); } - else if (["F64Type", "F32Type"].indexOf(argument.type.name) != -1) { + else if (["F64Type", "F32Type"].indexOf(argument.type.name) !== -1) { value = parseFloat(argument.value); } else if (argument.type.name == "BoolType") { @@ -160,22 +177,35 @@ export function squashify_argument(argument) { * @returns: string to display */ export function argument_display_string(argument) { - // Base assignment of the value - let string = `${(argument.value == null || argument.value === "") ? FILL_NEEDED: argument.value}`; - - if (argument.type.LENGTH) { - string = `[${argument.value.map((argument) => argument_display_string(argument)).join(", ")}]`; - } else if (argument.type.MEMBER_LIST) { - let fields = []; - for (let i = 0; i < argument.type.MEMBER_LIST.length; i++) { - let field = argument.type.MEMBER_LIST[i][0]; - fields.push(`${field}: ${argument_display_string(argument.value[field])}`); + let string = FILL_NEEDED; + try { + // Check for array + if (argument.type.LENGTH) { + string = `[${argument.value.map((argument) => argument_display_string(argument)).join(", ")}]`; + } + // Serializable + else if (argument.type.MEMBER_LIST) { + let fields = []; + for (let i = 0; i < argument.type.MEMBER_LIST.length; i++) { + let field = argument.type.MEMBER_LIST[i][0]; + fields.push(`${field}: ${argument_display_string(argument.value[field])}`); + } + string = `{${fields.join(", ")}}` + } + // String type + else if (argument.type.MAX_LENGTH) { + let value = (argument.value == null) ? "" : argument.value; + value = value.replace(/"/g, '\\\"'); + string = `"${value}"` + } + // Unassigned values + else if (argument.value == null || argument.value === "") { + string = FILL_NEEDED; + } else { + string = squashify_argument(argument); } - string = `{${fields.join(", ")}}` - } else if (argument.type.MAX_LENGTH) { - let value = (argument.value == null) ? "" : argument.value; - value = value.replace(/"/g, '\\\"'); - string = `"${value}"` + } catch (e) { + string = FILL_NEEDED; } return string; } @@ -291,7 +321,7 @@ Vue.component("command-scalar-argument", { return ["text", "0[bB][01]+|0[oO][0-7]+|0[xX][0-9a-fA-F]+|[1-9]\\d*|0", ""]; } else if (["I64Type", "I32Type", "I16Type", "I8Type"].indexOf(this.argument.type.name) != -1) { - return ["number", null, "1"]; + return ["text", "-?[1-9]\\d*|0", ""]; } else if (["F64Type", "F32Type"].indexOf(this.argument.type.name) != -1) { return ["number", null, "any"]; diff --git a/src/fprime_gds/flask/static/addons/commanding/command-history.js b/src/fprime_gds/flask/static/addons/commanding/command-history.js index 0807eee6..ac5edcdd 100644 --- a/src/fprime_gds/flask/static/addons/commanding/command-history.js +++ b/src/fprime_gds/flask/static/addons/commanding/command-history.js @@ -10,7 +10,8 @@ import {command_argument_assignment_helper} from "./arguments.js"; import {listExistsAndItemNameNotInList, timeToString} from "../../js/vue-support/utils.js"; import {command_history_template} from "./command-history-template.js"; import {command_display_string} from "./command-string.js"; - +import { SaferParser } from "../../js/json.js"; +SaferParser.register(); /** * command-history: @@ -103,27 +104,7 @@ Vue.component("command-history", { // Can only set command if it is a child of a command input if (this.$parent.selectCmd) { // command-input expects an array of strings as arguments - this.$parent.selectCmd(cmd.full_name, this.preprocess_args(cmd.args)); - } - }, - /** - * Process the arguments for a command. If the argument is (or contains) a number, it - * is converted to a string. Other types that should be pre-processed can be added here. - * - * @param {*} args - * @returns args processed for command input (numbers converted to strings) - */ - preprocess_args(args) { - if (Array.isArray(args)) { - return args.map(el => this.preprocess_args(el)); - } else if (typeof args === 'object' && args !== null) { - return Object.fromEntries( - Object.entries(args).map(([key, value]) => [key, this.preprocess_args(value)]) - ); - } else if (typeof args === 'number') { - return args.toString(); - } else { - return args; + this.$parent.selectCmd(cmd.full_name, cmd.args); } } } diff --git a/src/fprime_gds/flask/static/addons/commanding/command-input.js b/src/fprime_gds/flask/static/addons/commanding/command-input.js index aa97950c..0d07381b 100644 --- a/src/fprime_gds/flask/static/addons/commanding/command-input.js +++ b/src/fprime_gds/flask/static/addons/commanding/command-input.js @@ -15,6 +15,8 @@ import { } from "../../addons/commanding/arguments.js"; import {_settings} from "../../js/settings.js"; import {command_input_template} from "./command-input-template.js"; +import { SaferParser } from "../../js/json.js"; +SaferParser.register(); /** * This helper will help assign command and values in a safe manner by searching the command store, finding a reference, @@ -38,6 +40,7 @@ function command_assignment_helper(desired_command_name, desired_command_args, p return null; } let selected = _datastore.commands[command_name]; + // Set arguments here for (let i = 0; i < selected.args.length; i++) { let assign_value = (desired_command_args.length > i)? desired_command_args[i] : null; @@ -147,6 +150,7 @@ Vue.component("command-input", { * command reaches the ground system. */ sendCommand() { + // Validate the command before sending anything if (!this.validate()) { return; @@ -158,8 +162,9 @@ Vue.component("command-input", { let _self = this; _self.active = true; let command = this.selected; + let squashed_args = command.args.map(serialize_arg); this.loader.load("/commands/" + command.full_name, "PUT", - {"key":0xfeedcafe, "arguments": command.args.map(serialize_arg)}) + {"key":0xfeedcafe, "arguments": squashed_args}) .then(function() { _self.active = false; // Clear errors, as there is not a problem further diff --git a/src/fprime_gds/flask/static/addons/commanding/command-string-template.js b/src/fprime_gds/flask/static/addons/commanding/command-string-template.js index 9529c83a..b06c84ca 100644 --- a/src/fprime_gds/flask/static/addons/commanding/command-string-template.js +++ b/src/fprime_gds/flask/static/addons/commanding/command-string-template.js @@ -3,7 +3,8 @@ * * Contains the templates used to render the command string input box. */ -export let COMMAND_FORMAT_SPEC = "FULL_COMMAND_NAME[[[, ARG1], ARG2], ...]"; +export let COMMAND_FORMAT_SPEC = "FULL_COMMAND_NAME[[[, ARG1], ARG2], ...] " + + "where ARGN is a decimal number, quoted string, or an enumerated constant"; export let command_string_template = `
diff --git a/src/fprime_gds/flask/static/addons/commanding/command-string.js b/src/fprime_gds/flask/static/addons/commanding/command-string.js index 9a8b5a3f..a79da524 100644 --- a/src/fprime_gds/flask/static/addons/commanding/command-string.js +++ b/src/fprime_gds/flask/static/addons/commanding/command-string.js @@ -10,6 +10,8 @@ import { command_string_template } from "./command-string-template.js"; import {argument_display_string, FILL_NEEDED} from "./arguments.js" +import {SaferParser} from "../../js/json.js"; +SaferParser.register(); let STRING_PREPROCESSOR = new RegExp(`(?:"((?:[^\"]|\\\")*)")|([a-zA-Z_][a-zA-Z_0-9.]*)|(${FILL_NEEDED})`, "g"); @@ -61,7 +63,9 @@ Vue.component("command-text", { } catch (e) { // JSON parsing exceptions if (e instanceof SyntaxError) { - this.error = `Expected command string of the form: ${COMMAND_FORMAT_SPEC}`; + this.error = `Expected command string of the form: ${COMMAND_FORMAT_SPEC}.`; + } else { + throw e; } } } diff --git a/src/fprime_gds/flask/static/addons/sequencer/addon.js b/src/fprime_gds/flask/static/addons/sequencer/addon.js index a5c85745..97171dec 100644 --- a/src/fprime_gds/flask/static/addons/sequencer/addon.js +++ b/src/fprime_gds/flask/static/addons/sequencer/addon.js @@ -16,6 +16,8 @@ import {_datastore} from "../../js/datastore.js"; import {basicSetup, EditorState, EditorView, linter} from "./third/code-mirror.es.js" import {sequenceLanguageSupport} from "./autocomplete.js" import {processResponse} from "./lint.js"; +import { SaferParser } from "../../js/json.js"; +SaferParser.register(); /** * Sequence sender function used to uplink the sequence and return a promise of how to handle the server's return. diff --git a/src/fprime_gds/flask/static/js/json.js b/src/fprime_gds/flask/static/js/json.js new file mode 100644 index 00000000..bf797518 --- /dev/null +++ b/src/fprime_gds/flask/static/js/json.js @@ -0,0 +1,275 @@ +/** + * json.js: + * + * Contains specialized JSON parser to handle non-standard JSON values from the JavaScript perspective. These values + * are legal in Python and scala, but not in JavaScript. This parser will safely handle these values. + * + * @author mstarch + */ + + +/** + * Helper to determine if value is a string + * @param value: value to check. + * @return {boolean}: true if string, false otherwise + */ +function isString(value) { + return value instanceof String || typeof value === 'string'; +} + +/** + * Helper to determine if value is a function + * @param value: value to check + * @return {boolean}: true if function, false otherwise + */ +function isFunction(value) { + return value instanceof Function || typeof value == "function"; +} + +/** + * Conversion function for converting from string to BigInt or Number depending on size + * @param {*} string_value: value to convert + * @returns: Number for small values and BigInt for large values + */ +function convertInt(string_value) { + string_value = string_value.trim(); + let number_value = Number.parseInt(string_value); + // When the big and normal numbers match, then return the normal number + if (string_value == number_value.toString()) { + return number_value; + } + return BigInt(string_value); +} + +/** + * Parser to safely handle potential JSON object from Python. Python can produce some non-standard values (infinities, + * NaNs, etc.) These values then break on the JS Javascript parser. To localize these faults, they are replaced before + * processing with strings and then formally set during parsing. + * + * This is done by looking for tokens in unquoted text and replacing them with string representations. + * + * This parser will handle: + * - -Infinity + * - Infinity + * - NaN + * - null + * - BigInt + */ +export class SaferParser { + /** + * States representing QUOTED or UNQUOTED text + * @type {{QUOTED: number, UNQUOTED: number}} + */ + static STATES = { + UNQUOTED: 0, + QUOTED: 1 + }; + /** + * List of mapping tuples for clean parsing: string match, replacement type, and real (post parse) type + */ + static MAPPINGS = [ + [/(-Infinity)/, -Infinity], + [/(Infinity)/, Infinity], + [/(NaN)/, NaN], + [/(null)/, null], + [/( -?\d{10,})/, "bigint", convertInt] + ]; + + + // Store the language variants the first time + static language_parse = JSON.parse; + static language_stringify = JSON.stringify; + + /** + * @brief safely process F Prime JSON syntax + * + * Parse method that will replace JSON.parse. This method pre-processes the string data incoming (to be transformed + * into JavaScript objects) for detection of entities not expressible in JavaScript's JSON implementation. This will + * replace those entities with a JSON flag object. + * + * Then the data is processed by the JavaScript built-in JSON parser (now done safely). The reviver function will + * safely revive the flag objects into JavaScript representations of those object. + * + * Handles: + * 1. BigInts + * 2. Inf/-Inf + * 3. NaN + * 4. null + * + * @param json_string: JSON string data containing potentially bad values + * @param reviver: reviver function to be combined with our reviver + * @return {{}}: Javascript Object representation of data safely represented in JavaScript types + */ + static parse(json_string, reviver) { + let converted_data = SaferParser.processUnquoted(json_string, SaferParser.replaceFromString); + // Set up a composite reviver of the one passed in and ours + let input_reviver = reviver || ((key, value) => value); + let full_reviver = (key, value) => input_reviver(key, SaferParser.reviver(key, value)); + try { + let language_parsed = SaferParser.language_parse(converted_data, full_reviver); + return language_parsed; + } catch (e) { + let message = e.toString(); + const matcher = /line (\d+) column (\d+)/ + + // Process the match + let snippet = ""; + let match = message.match(matcher); + if (match != null) { + let lines = converted_data.split("\n"); + let line = lines[Number.parseInt(match[1]) - 1] + snippet = line.substring(Number.parseInt(match[2]) - 6, Number.parseInt(match[2]) + 5); + message += ". Offending snippet: " + snippet; + throw new SyntaxError(message); + } + throw e; + } + } + + /** + * @brief safely write the F Prime JSON syntax + * + * Stringify method that will replace JSON.stringify. This method post-processes the string data outgoing from + * JavaScript's built-in stringify method to replace flag-objects with the correct F Prime representation in + * JavaScript. + * + * This uses the javascript stringify handler method to pre-convert unsupported types into a flag object. This flag + * object is post-converted into a normal string after JSON.stringify has done its best. + * + * Handles: + * 1. BigInts + * 2. Inf/-Inf + * 3. NaN + * 4. null + * + * @param data: data object to stringify + * @param replacer: replacer Array or Function + * @param space: space for passing into JSON.stringify + * @return {{}}: JSON string using JSON support for big-ints Int/-Inf, NaN and null. + */ + static stringify(data, replacer, space) { + let full_replacer = (key, value) => { + // Handle array case for excluded field + if (Array.isArray(replacer) && replacer.indexOf(key) === -1) { + return undefined; + } + // Run input replacer first + else if (isFunction(replacer)) { + value = replacer(key, value); + } + // Then run our safe replacer + let replaced = SaferParser.replaceFromObject(key, value); + return replaced; + }; + // Stringify JSON using built-in JSON parser and the special replacer + let json_string = SaferParser.language_stringify(data, full_replacer, space); + // Post-process JSON string to rework JSON into the wider specification + let post_replace = SaferParser.postReplacer(json_string); + return post_replace + } + + /** + * Get replacement object from a JavaScript type + * @param value: value to replace + */ + static replaceFromObject(_, value) { + for (let i = 0; i < SaferParser.MAPPINGS.length; i++) { + let mapper_type = SaferParser.MAPPINGS[i][1]; + let mapper_is_string = isString(mapper_type); + // Check if the mapping matches the value, if so substitute a replacement object + if ((!mapper_is_string && value == mapper_type) || (mapper_is_string && typeof value == mapper_type)) { + return {"fprime{replacement": (value == null) ? "null" : value.toString()}; + } + } + return value; + } + + /** + * Replace JSON notation for fprime-replcement objects with the wider JSON specification + * + * Replace {"fprime-replacement: "some value"} with restoring the full JSON specification for items not + * supported by JavaScript. + * + * @param json_string: JSON string to rework + * @return reworked JSON string + */ + static postReplacer(json_string) { + return json_string.replace(/\{\s*"fprime\{replacement"\s*:\s*"([^"]+)"\s*\}/sg, "$1"); + } + + /** + * Replace string occurrences of our gnarly types with a mapping equivalent + * @param string_value: value to replace + */ + static replaceFromString(string_value) { + for (let i = 0; i < SaferParser.MAPPINGS.length; i++) { + let mapper = SaferParser.MAPPINGS[i]; + string_value = string_value.replace(mapper[0], "{\"fprime{replacement\": \"$1\"}"); + } + return string_value; + } + + /** + * Apply process function to raw json string only for data that is not qu + * @param json_string + * @param process_function + * @return {string} + */ + static processUnquoted(json_string, process_function) { + // The initial state of any JSON string is unquoted + let state = SaferParser.STATES.UNQUOTED; + let unprocessed = json_string; + let transformed_data = ""; + + while (unprocessed.length > 0) { + let next_quote = unprocessed.indexOf("\""); + let section = (next_quote !== -1) ? unprocessed.substring(0, next_quote + 1) : unprocessed.substring(0); + unprocessed = unprocessed.substring(section.length); + transformed_data += (state === SaferParser.STATES.QUOTED) ? section : process_function(section); + state = (state === SaferParser.STATES.QUOTED) ? SaferParser.STATES.UNQUOTED : SaferParser.STATES.QUOTED; + } + return transformed_data; + } + + /** + * Inverse of convert removing string and replacing back invalid JSON tokens. + * @param key: JSON key + * @param value: JSON value search for the converted value. + * @return {*}: reverted value or value + */ + static reviver(key, value) { + // Look for fprime-replacement and quickly abort if not there + let string_value = value["fprime{replacement"]; + if (typeof string_value === "undefined") { + return value; + } + // Run the mappings looking for a match + for (let i = 0; i < SaferParser.MAPPINGS.length; i++) { + let mapper = SaferParser.MAPPINGS[i]; + if (mapper[0].test(string_value)) { + // Run the conversion function if it exists, otherwise return the mapped constant value + return (mapper.length >= 3) ? mapper[2](string_value) : mapper[1]; + } + } + return value; + } + + /** + * @brief force all calls to JSON.parse and JSON.stringify to use the SafeParser + */ + static register() { + // Override the singleton + JSON.parse = SaferParser.parse; + JSON.stringify = SaferParser.stringify; + } + + /** + * @brief remove the JSON.parse safe override + */ + static deregister() { + JSON.parse = SaferParser.language_parse; + JSON.stringify = SaferParser.language_stringify; + } +} +// Take over all JSON.parse and JSON.stringify calls +SaferParser.register(); \ No newline at end of file diff --git a/src/fprime_gds/flask/static/js/loader.js b/src/fprime_gds/flask/static/js/loader.js index 1bece886..d32960d6 100644 --- a/src/fprime_gds/flask/static/js/loader.js +++ b/src/fprime_gds/flask/static/js/loader.js @@ -13,7 +13,8 @@ */ import {config} from "./config.js"; import {_settings} from "./settings.js"; -import {_validator} from "./validate.js"; +import {SaferParser} from "./json.js"; +SaferParser.register(); /** * Function allowing for the saving of some data to a downloadable file. @@ -50,118 +51,6 @@ export function loadTextFileInputData(event) { }); } -/** - * Parser to safely handle potential JSON object from Python. Python can produce some non-standard values (infinities, - * NaNs, etc.) These values then break on the JS Javascript parser. To localize these faults, they are replaced before - * processing with strings and then formally set during parsing. - * - * This is done by looking for tokens in unquoted text and replacing them with string representations. - * - */ -class SaferParser { - /** - * Set up the parser - */ - constructor() { - this.STATES = { - UNQUOTED: 0, - QUOTED: 1 - }; - this.FLAG = "-_-您好"; // Extended character usage make collisions less-likely - this.MAPPINGS = [ - ["-Infinity", this.FLAG + "-inf", -Infinity], - ["Infinity", this.FLAG + "inf", Infinity], - ["NaN", this.FLAG + "nan", NaN], - ["null", this.FLAG + "null", null] - ]; - this.state = this.STATES.UNQUOTED; - } - - /** - * Parse method that will replace JSON.parse. This handles known bad cases and also prints better error messages - * including the working snippets of text. - * @param rawData: string data - * @return {{}|any}: Javascript Object representation of data. - */ - parse(rawData) { - let converted_data = this.convert(rawData); - try { - return JSON.parse(converted_data, this.revert.bind(this)); - } catch (e) { - let message = e.toString(); - const matcher = /line (\d+) column (\d+)/ - - // Process the match - let snippet = ""; - let match = message.match(matcher); - if (match != null) { - let lines = converted_data.split("\n"); - let line = lines[Number.parseInt(match[1]) - 1] - snippet = line.substring(Number.parseInt(match[2]) - 6, Number.parseInt(match[2]) + 5); - message += ". Offending snippet: " + snippet; - } - _validator.updateErrors([message]); - } - return {}; - } - - /** - * Convert data from invalid form to strings. - * @param rawData: raw data including potentially invalid data - * @return {string}: string data in correct JSON format - */ - convert(rawData) { - let unprocessed = rawData; - let transformed_data = ""; - - while (unprocessed.length > 0) { - let next_quote = unprocessed.indexOf("\""); - let section = (next_quote !== -1) ? unprocessed.substring(0, next_quote + 1) : unprocessed.substring(0); - unprocessed = unprocessed.substring(section.length); - transformed_data += this.processChunk(section); - this.state = (this.state === this.STATES.QUOTED) ? this.STATES.UNQUOTED : this.STATES.QUOTED; - } - return transformed_data; - } - - /** - * Inverse of convert removing string and replacing back invalid JSON tokens. - * @param key: JSON key - * @param value: JSON value search for the converted value. - * @return {*}: reverted value or value - */ - revert(key, value) { - for (let i = 0; i < this.MAPPINGS.length; i++) { - if ((this.MAPPINGS[i][1]) === value) { - return this.MAPPINGS[i][2]; - } - } - return value; - } - - /** - * Process a section of the JSON string looking for values to convert. This is intended to handle a section of - * quoted or unquoted text but should never handle quoted and unquoted data in one call. - * @param section: section of the data - * @return {*}: converted data - */ - processChunk(section) { - // Replaces all the above mappings with a flagged value - let replace_all = (section) => { - for (let i = 0; i < this.MAPPINGS.length; i++) { - section = section.replace(this.MAPPINGS[i][0], "\"" + this.MAPPINGS[i][1] + "\""); - } - return section; - } - - // When out of quoted space, - if (this.state === this.STATES.UNQUOTED) { - return replace_all(section); - } - return section; - } -} - /** * Loader: * @@ -308,7 +197,7 @@ class Loader { if (this.readyState === 4 && this.status === 200 && raw) { resolve(this.responseText); } else if (this.readyState === 4 && this.status === 200) { - let dataObj = new SaferParser().parse(this.responseText); + let dataObj = JSON.parse(this.responseText); resolve(dataObj); } else if(this.readyState === 4) { reject(this.responseText); From 41380c74d2776b0cabce7d9776610e4ea0a37e19 Mon Sep 17 00:00:00 2001 From: M Starch Date: Wed, 5 Feb 2025 20:20:00 -0800 Subject: [PATCH 2/3] sp 1 --- src/fprime_gds/flask/static/js/json.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/fprime_gds/flask/static/js/json.js b/src/fprime_gds/flask/static/js/json.js index bf797518..e92c3044 100644 --- a/src/fprime_gds/flask/static/js/json.js +++ b/src/fprime_gds/flask/static/js/json.js @@ -185,7 +185,7 @@ export class SaferParser { } /** - * Replace JSON notation for fprime-replcement objects with the wider JSON specification + * Replace JSON notation for fprime-replacement objects with the wider JSON specification * * Replace {"fprime-replacement: "some value"} with restoring the full JSON specification for items not * supported by JavaScript. @@ -272,4 +272,4 @@ export class SaferParser { } } // Take over all JSON.parse and JSON.stringify calls -SaferParser.register(); \ No newline at end of file +SaferParser.register(); From 8cc12438dad7ed74fbbcaf6875a54601d5be1757 Mon Sep 17 00:00:00 2001 From: M Starch Date: Wed, 5 Feb 2025 20:20:33 -0800 Subject: [PATCH 3/3] sp 2 --- .github/actions/spelling/expect.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt index 786365d4..59343039 100644 --- a/.github/actions/spelling/expect.txt +++ b/.github/actions/spelling/expect.txt @@ -11,6 +11,7 @@ ANamespace Anps argcomplete argdesc +ARGN argname argtype autoapi