summaryrefslogtreecommitdiffstats
path: root/devtools/shared/sprintfjs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 01:47:29 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 01:47:29 +0000
commit0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d (patch)
treea31f07c9bcca9d56ce61e9a1ffd30ef350d513aa /devtools/shared/sprintfjs
parentInitial commit. (diff)
downloadfirefox-esr-upstream/115.8.0esr.tar.xz
firefox-esr-upstream/115.8.0esr.zip
Adding upstream version 115.8.0esr.upstream/115.8.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--devtools/shared/sprintfjs/UPGRADING.md17
-rw-r--r--devtools/shared/sprintfjs/moz.build9
-rw-r--r--devtools/shared/sprintfjs/sprintf.js283
3 files changed, 309 insertions, 0 deletions
diff --git a/devtools/shared/sprintfjs/UPGRADING.md b/devtools/shared/sprintfjs/UPGRADING.md
new file mode 100644
index 0000000000..e0db44df67
--- /dev/null
+++ b/devtools/shared/sprintfjs/UPGRADING.md
@@ -0,0 +1,17 @@
+SPRINTF JS UPGRADING
+
+Original library at https://github.com/alexei/sprintf.js
+
+This library should no longer be upgraded from upstream. We added performance improvements
+in https://bugzilla.mozilla.org/show_bug.cgi?id=1406311. Most importantly removing the
+usage of the get_type() method as well as prioritizing the %S use case.
+
+If for some reason, updating from upstream becomes necessary, please refer to the bug
+mentioned above to reimplement the performance fixes in the new version.
+
+By default the library only supports string placeholders using %s (lowercase) while we use
+%S (uppercase). The library also has to be manually patched in order to support it.
+
+- grab the unminified version at https://github.com/alexei/sprintf.js/blob/master/src/sprintf.js
+- update the re.placeholder regexp to allow "S" as well as "s"
+- update the switch statement in the format() method to make case "S" equivalent to case "s"
diff --git a/devtools/shared/sprintfjs/moz.build b/devtools/shared/sprintfjs/moz.build
new file mode 100644
index 0000000000..6f4f1f8734
--- /dev/null
+++ b/devtools/shared/sprintfjs/moz.build
@@ -0,0 +1,9 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+DevToolsModules(
+ 'sprintf.js'
+)
diff --git a/devtools/shared/sprintfjs/sprintf.js b/devtools/shared/sprintfjs/sprintf.js
new file mode 100644
index 0000000000..fd53cd3ce9
--- /dev/null
+++ b/devtools/shared/sprintfjs/sprintf.js
@@ -0,0 +1,283 @@
+/**
+ * Copyright (c) 2007-2016, Alexandru Marasteanu <hello [at) alexei (dot] ro>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of this software nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+/* eslint-disable */
+/* globals window, exports, define */
+
+(function(window) {
+ 'use strict'
+
+ var re = {
+ not_string: /[^s]/,
+ not_bool: /[^t]/,
+ not_type: /[^T]/,
+ not_primitive: /[^v]/,
+ number: /[diefg]/,
+ numeric_arg: /bcdiefguxX/,
+ json: /[j]/,
+ not_json: /[^j]/,
+ text: /^[^\x25]+/,
+ modulo: /^\x25{2}/,
+ placeholder: /^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-gijosStTuvxX])/,
+ key: /^([a-z_][a-z_\d]*)/i,
+ key_access: /^\.([a-z_][a-z_\d]*)/i,
+ index_access: /^\[(\d+)\]/,
+ sign: /^[\+\-]/
+ }
+
+ function sprintf() {
+ var key = arguments[0], cache = sprintf.cache
+ if (!(cache[key] && cache.hasOwnProperty(key))) {
+ cache[key] = sprintf.parse(key)
+ }
+ return sprintf.format.call(null, cache[key], arguments)
+ }
+
+ sprintf.format = function(parse_tree, argv) {
+ var cursor = 1, tree_length = parse_tree.length, node_type = '', arg, output = [], i, k, match, pad, pad_character, pad_length, is_positive = true, sign = ''
+ for (i = 0; i < tree_length; i++) {
+ node_type = typeof parse_tree[i]
+ // The items of parse tree are either strings or results of a match() call.
+ if (node_type === 'string') {
+ // this is not a placeholder, this is just a string.
+ output[output.length] = parse_tree[i]
+ }
+ else {
+ // this is a placeholder, need to identify its type, options and replace
+ // it with the appropriate argument.
+ match = parse_tree[i] // convenience purposes only
+ if (match[2]) { // keyword argument
+ arg = argv[cursor]
+ for (k = 0; k < match[2].length; k++) {
+ if (!arg.hasOwnProperty(match[2][k])) {
+ throw new Error(sprintf('[sprintf] property "%s" does not exist', match[2][k]))
+ }
+ arg = arg[match[2][k]]
+ }
+ }
+ else if (match[1]) { // positional argument (explicit)
+ arg = argv[match[1]]
+ }
+ else { // positional argument (implicit)
+ arg = argv[cursor++]
+ }
+
+ // The most commonly used placeholder in DevTools is the string (%S or %s).
+ // We check it first to avoid unnecessary verifications.
+ let hasPadding = match[6];
+ let patternType = match[8];
+ if (!hasPadding && (patternType === "S" || patternType === "s")) {
+ if (typeof arg === "function") {
+ arg = arg();
+ }
+ if (typeof arg !== "string") {
+ arg = String(arg);
+ }
+ output[output.length] = match[7] ? arg.substring(0, match[7]) : arg;
+ continue;
+ }
+
+ if (re.not_type.test(match[8]) && re.not_primitive.test(match[8]) && typeof arg == 'function') {
+ arg = arg()
+ }
+
+ if (re.numeric_arg.test(match[8]) && (typeof arg != 'number' && isNaN(arg))) {
+ throw new TypeError(sprintf("[sprintf] expecting number but found %s", typeof arg))
+ }
+
+ if (re.number.test(match[8])) {
+ is_positive = arg >= 0
+ }
+
+ switch (match[8]) {
+ case 'b':
+ arg = parseInt(arg, 10).toString(2)
+ break
+ case 'c':
+ arg = String.fromCharCode(parseInt(arg, 10))
+ break
+ case 'd':
+ case 'i':
+ arg = parseInt(arg, 10)
+ break
+ case 'j':
+ arg = JSON.stringify(arg, null, match[6] ? parseInt(match[6]) : 0)
+ break
+ case 'e':
+ arg = match[7] ? parseFloat(arg).toExponential(match[7]) : parseFloat(arg).toExponential()
+ break
+ case 'f':
+ arg = match[7] ? parseFloat(arg).toFixed(match[7]) : parseFloat(arg)
+ break
+ case 'g':
+ arg = match[7] ? parseFloat(arg).toPrecision(match[7]) : parseFloat(arg)
+ break
+ case 'o':
+ arg = arg.toString(8)
+ break
+ case 's':
+ case 'S':
+ arg = String(arg)
+ arg = (match[7] ? arg.substring(0, match[7]) : arg)
+ break
+ case 't':
+ arg = String(!!arg)
+ arg = (match[7] ? arg.substring(0, match[7]) : arg)
+ break
+ case 'T':
+ arg = Object.prototype.toString.call(arg).slice(8, -1).toLowerCase()
+ arg = (match[7] ? arg.substring(0, match[7]) : arg)
+ break
+ case 'u':
+ arg = parseInt(arg, 10) >>> 0
+ break
+ case 'v':
+ arg = arg.valueOf()
+ arg = (match[7] ? arg.substring(0, match[7]) : arg)
+ break
+ case 'x':
+ arg = parseInt(arg, 10).toString(16)
+ break
+ case 'X':
+ arg = parseInt(arg, 10).toString(16).toUpperCase()
+ break
+ }
+ if (re.json.test(match[8])) {
+ output[output.length] = arg
+ }
+ else {
+ if (re.number.test(match[8]) && (!is_positive || match[3])) {
+ sign = is_positive ? '+' : '-'
+ arg = arg.toString().replace(re.sign, '')
+ }
+ else {
+ sign = ''
+ }
+ pad_character = match[4] ? match[4] === '0' ? '0' : match[4].charAt(1) : ' '
+ pad_length = match[6] - (sign + arg).length
+ pad = match[6] ? (pad_length > 0 ? str_repeat(pad_character, pad_length) : '') : ''
+ output[output.length] = match[5] ? sign + arg + pad : (pad_character === '0' ? sign + pad + arg : pad + sign + arg)
+ }
+ }
+ }
+ return output.join('')
+ }
+
+ sprintf.cache = {}
+
+ sprintf.parse = function(fmt) {
+ var _fmt = fmt, match = [], parse_tree = [], arg_names = 0
+ while (_fmt) {
+ if ((match = re.text.exec(_fmt)) !== null) {
+ parse_tree[parse_tree.length] = match[0]
+ }
+ else if ((match = re.modulo.exec(_fmt)) !== null) {
+ parse_tree[parse_tree.length] = '%'
+ }
+ else if ((match = re.placeholder.exec(_fmt)) !== null) {
+ if (match[2]) {
+ arg_names |= 1
+ var field_list = [], replacement_field = match[2], field_match = []
+ if ((field_match = re.key.exec(replacement_field)) !== null) {
+ field_list[field_list.length] = field_match[1]
+ while ((replacement_field = replacement_field.substring(field_match[0].length)) !== '') {
+ if ((field_match = re.key_access.exec(replacement_field)) !== null) {
+ field_list[field_list.length] = field_match[1]
+ }
+ else if ((field_match = re.index_access.exec(replacement_field)) !== null) {
+ field_list[field_list.length] = field_match[1]
+ }
+ else {
+ throw new SyntaxError("[sprintf] failed to parse named argument key")
+ }
+ }
+ }
+ else {
+ throw new SyntaxError("[sprintf] failed to parse named argument key")
+ }
+ match[2] = field_list
+ }
+ else {
+ arg_names |= 2
+ }
+ if (arg_names === 3) {
+ throw new Error("[sprintf] mixing positional and named placeholders is not (yet) supported")
+ }
+ parse_tree[parse_tree.length] = match
+ }
+ else {
+ throw new SyntaxError("[sprintf] unexpected placeholder")
+ }
+ _fmt = _fmt.substring(match[0].length)
+ }
+ return parse_tree
+ }
+
+ var vsprintf = function(fmt, argv, _argv) {
+ _argv = (argv || []).slice(0)
+ _argv.splice(0, 0, fmt)
+ return sprintf.apply(null, _argv)
+ }
+
+ /**
+ * helpers
+ */
+
+ var preformattedPadding = {
+ '0': ['', '0', '00', '000', '0000', '00000', '000000', '0000000'],
+ ' ': ['', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
+ '_': ['', '_', '__', '___', '____', '_____', '______', '_______'],
+ }
+ function str_repeat(input, multiplier) {
+ if (multiplier >= 0 && multiplier <= 7 && preformattedPadding[input]) {
+ return preformattedPadding[input][multiplier]
+ }
+ return Array(multiplier + 1).join(input)
+ }
+
+ /**
+ * export to either browser or node.js
+ */
+ if (typeof exports !== 'undefined') {
+ exports.sprintf = sprintf
+ exports.vsprintf = vsprintf
+ }
+ else {
+ window.sprintf = sprintf
+ window.vsprintf = vsprintf
+
+ if (typeof define === 'function' && define.amd) {
+ define(function() {
+ return {
+ sprintf: sprintf,
+ vsprintf: vsprintf
+ }
+ })
+ }
+ }
+})(typeof window === 'undefined' ? this : window);