s1-mod/deps/asmjit/tools/generator-commons.js

593 lines
13 KiB
JavaScript
Raw Normal View History

2024-02-27 03:09:30 -05:00
// This file is part of AsmJit project <https://asmjit.com>
//
// See asmjit.h or LICENSE.md for license and copyright information
// SPDX-License-Identifier: Zlib
const hasOwn = Object.prototype.hasOwnProperty;
function nop(x) { return x; }
// Generator - Constants
// ---------------------
const kIndent = " ";
exports.kIndent = kIndent;
const kLineWidth = 120;
// Generator - Logging
// -------------------
let VERBOSE = false;
function setDebugVerbosity(value) {
VERBOSE = value;
}
exports.setDebugVerbosity = setDebugVerbosity;
function DEBUG(msg) {
if (VERBOSE)
console.log(msg);
}
exports.DEBUG = DEBUG;
function WARN(msg) {
console.log(msg);
}
exports.WARN = WARN;
function FATAL(msg) {
console.log(`FATAL: ${msg}`);
throw new Error(msg);
}
exports.FATAL = FATAL;
// Generator - Object Utilities
// ----------------------------
class ObjectUtils {
static clone(map) {
return Object.assign(Object.create(null), map);
}
static merge(a, b) {
if (a === b)
return a;
for (let k in b) {
let av = a[k];
let bv = b[k];
if (typeof av === "object" && typeof bv === "object")
ObjectUtils.merge(av, bv);
else
a[k] = bv;
}
return a;
}
static equals(a, b) {
if (a === b)
return true;
if (typeof a !== typeof b)
return false;
if (typeof a !== "object")
return a === b;
if (Array.isArray(a) || Array.isArray(b)) {
if (Array.isArray(a) !== Array.isArray(b))
return false;
const len = a.length;
if (b.length !== len)
return false;
for (let i = 0; i < len; i++)
if (!ObjectUtils.equals(a[i], b[i]))
return false;
}
else {
if (a === null || b === null)
return a === b;
for (let k in a)
if (!hasOwn.call(b, k) || !ObjectUtils.equals(a[k], b[k]))
return false;
for (let k in b)
if (!hasOwn.call(a, k))
return false;
}
return true;
}
static equalsExcept(a, b, except) {
if (a === b)
return true;
if (typeof a !== "object" || typeof b !== "object" || Array.isArray(a) || Array.isArray(b))
return ObjectUtils.equals(a, b);
for (let k in a)
if (!hasOwn.call(except, k) && (!hasOwn.call(b, k) || !ObjectUtils.equals(a[k], b[k])))
return false;
for (let k in b)
if (!hasOwn.call(except, k) && !hasOwn.call(a, k))
return false;
return true;
}
static findKey(map, keys) {
for (let key in keys)
if (hasOwn.call(map, key))
return key;
return undefined;
}
static hasAny(map, keys) {
for (let key in keys)
if (hasOwn.call(map, key))
return true;
return false;
}
static and(a, b) {
const out = Object.create(null);
for (let k in a)
if (hasOwn.call(b, k))
out[k] = true;
return out;
}
static xor(a, b) {
const out = Object.create(null);
for (let k in a) if (!hasOwn.call(b, k)) out[k] = true;
for (let k in b) if (!hasOwn.call(a, k)) out[k] = true;
return out;
}
}
exports.ObjectUtils = ObjectUtils;
// Generator - Array Utilities
// ---------------------------
class ArrayUtils {
static min(arr, fn) {
if (!arr.length)
return null;
if (!fn)
fn = nop;
let v = fn(arr[0]);
for (let i = 1; i < arr.length; i++)
v = Math.min(v, fn(arr[i]));
return v;
}
static max(arr, fn) {
if (!arr.length)
return null;
if (!fn)
fn = nop;
let v = fn(arr[0]);
for (let i = 1; i < arr.length; i++)
v = Math.max(v, fn(arr[i]));
return v;
}
static sorted(obj, cmp) {
const out = Array.isArray(obj) ? obj.slice() : Object.getOwnPropertyNames(obj);
out.sort(cmp);
return out;
}
static deepIndexOf(arr, what) {
for (let i = 0; i < arr.length; i++)
if (ObjectUtils.equals(arr[i], what))
return i;
return -1;
}
static toDict(arr, value) {
if (value === undefined)
value = true;
const out = Object.create(null);
for (let i = 0; i < arr.length; i++)
out[arr[i]] = value;
return out;
}
}
exports.ArrayUtils = ArrayUtils;
// Generator - String Utilities
// ----------------------------
class StringUtils {
static asString(x) { return String(x); }
static countOf(s, pattern) {
if (!pattern)
FATAL(`Pattern cannot be empty`);
let n = 0;
let pos = 0;
while ((pos = s.indexOf(pattern, pos)) >= 0) {
n++;
pos += pattern.length;
}
return n;
}
static trimLeft(s) { return s.replace(/^\s+/, ""); }
static trimRight(s) { return s.replace(/\s+$/, ""); }
static upFirst(s) {
if (!s) return "";
return s[0].toUpperCase() + s.substr(1);
}
static decToHex(n, nPad) {
let hex = Number(n < 0 ? 0x100000000 + n : n).toString(16);
while (nPad > hex.length)
hex = "0" + hex;
return "0x" + hex.toUpperCase();
}
static format(array, indent, showIndex, mapFn) {
if (!mapFn)
mapFn = StringUtils.asString;
let s = "";
let threshold = 80;
if (showIndex === -1)
s += indent;
for (let i = 0; i < array.length; i++) {
const item = array[i];
const last = i === array.length - 1;
if (showIndex !== -1)
s += indent;
s += mapFn(item);
if (showIndex > 0) {
s += `${last ? " " : ","} // #${i}`;
if (typeof array.refCountOf === "function")
s += ` [ref=${array.refCountOf(item)}x]`;
}
else if (!last) {
s += ",";
}
if (showIndex === -1) {
if (s.length >= threshold - 1 && !last) {
s += "\n" + indent;
threshold += 80;
}
else {
if (!last) s += " ";
}
}
else {
if (!last) s += "\n";
}
}
return s;
}
static makeCxxArray(array, code, indent) {
if (typeof indent !== "string")
indent = kIndent;
return `${code} = {\n${indent}` + array.join(`,\n${indent}`) + `\n};\n`;
}
static makeCxxArrayWithComment(array, code, indent) {
if (typeof indent !== "string")
indent = kIndent;
let s = "";
for (let i = 0; i < array.length; i++) {
const last = i === array.length - 1;
s += indent + array[i].data +
(last ? " // " : ", // ") + (array[i].refs ? "#" + String(i) : "").padEnd(5) + array[i].comment + "\n";
}
return `${code} = {\n${s}};\n`;
}
static formatCppStruct(...args) {
return "{ " + args.join(", ") + " }";
}
static formatCppFlags(obj, fn, none) {
if (none == null)
none = "0";
if (!fn)
fn = nop;
let out = "";
for (let k in obj) {
if (obj[k])
out += (out ? " | " : "") + fn(k);
}
return out ? out : none;
}
static formatRecords(array, indent, fn) {
if (typeof indent !== "string")
indent = kIndent;
if (!fn)
fn = nop;
let s = "";
let line = "";
for (let i = 0; i < array.length; i++) {
const item = fn(array[i]);
const combined = line ? line + ", " + item : item;
if (combined.length >= kLineWidth) {
s = s ? s + ",\n" + line : line;
line = item;
}
else {
line = combined;
}
}
if (line) {
s = s ? s + ",\n" + line : line;
}
return StringUtils.indent(s, indent);
}
static disclaimer(s) {
return "// ------------------- Automatically generated, do not edit -------------------\n" +
s +
"// ----------------------------------------------------------------------------\n";
}
static indent(s, indentation) {
if (typeof indentation === "number")
indentation = " ".repeat(indentation);
let lines = s.split(/\r?\n/g);
if (indentation) {
for (let i = 0; i < lines.length; i++) {
let line = lines[i];
if (line)
lines[i] = indentation + line;
}
}
return lines.join("\n");
}
static extract(s, start, end) {
const iStart = s.indexOf(start);
const iEnd = s.indexOf(end);
if (iStart === -1)
FATAL(`StringUtils.extract(): Couldn't locate start mark '${start}'`);
if (iEnd === -1)
FATAL(`StringUtils.extract(): Couldn't locate end mark '${end}'`);
return s.substring(iStart + start.length, iEnd).trim();
}
static inject(s, start, end, code) {
let iStart = s.indexOf(start);
let iEnd = s.indexOf(end);
if (iStart === -1)
FATAL(`StringUtils.inject(): Couldn't locate start mark '${start}'`);
if (iEnd === -1)
FATAL(`StringUtils.inject(): Couldn't locate end mark '${end}'`);
let nIndent = 0;
while (iStart > 0 && s[iStart-1] === " ") {
iStart--;
nIndent++;
}
if (nIndent) {
const indentation = " ".repeat(nIndent);
code = StringUtils.indent(code, indentation) + indentation;
}
return s.substr(0, iStart + start.length + nIndent) + code + s.substr(iEnd);
}
static makePriorityCompare(priorityArray) {
const map = Object.create(null);
priorityArray.forEach((str, index) => { map[str] = index; });
return function(a, b) {
const ax = hasOwn.call(map, a) ? map[a] : Infinity;
const bx = hasOwn.call(map, b) ? map[b] : Infinity;
return ax != bx ? ax - bx : a < b ? -1 : a > b ? 1 : 0;
}
}
}
exports.StringUtils = StringUtils;
// Generator - Indexed Array
// =========================
// IndexedArray is an Array replacement that allows to index each item inserted to it. Its main purpose
// is to avoid data duplication, if an item passed to `addIndexed()` is already within the Array then
// it's not inserted and the existing index is returned instead.
function IndexedArray_keyOf(item) {
return typeof item === "string" ? item : JSON.stringify(item);
}
class IndexedArray extends Array {
constructor() {
super();
this._index = Object.create(null);
}
refCountOf(item) {
const key = IndexedArray_keyOf(item);
const idx = this._index[key];
return idx !== undefined ? idx.refCount : 0;
}
addIndexed(item) {
const key = IndexedArray_keyOf(item);
let idx = this._index[key];
if (idx !== undefined) {
idx.refCount++;
return idx.data;
}
idx = this.length;
this._index[key] = {
data: idx,
refCount: 1
};
this.push(item);
return idx;
}
}
exports.IndexedArray = IndexedArray;
// Generator - Indexed String
// ==========================
// IndexedString is mostly used to merge all instruction names into a single string with external
// index. It's designed mostly for generating C++ tables. Consider the following cases in C++:
//
// a) static const char* const* instNames = { "add", "mov", "vpunpcklbw" };
//
// b) static const char instNames[] = { "add\0" "mov\0" "vpunpcklbw\0" };
// static const uint16_t instNameIndex[] = { 0, 4, 8 };
//
// The latter (b) has an advantage that it doesn't have to be relocated by the linker, which saves
// a lot of space in the resulting binary and a lot of CPU cycles (and memory) when the linker loads
// it. AsmJit supports thousands of instructions so each optimization like this makes it smaller and
// faster to load.
class IndexedString {
constructor() {
this.map = Object.create(null);
this.array = [];
this.size = -1;
}
add(s) {
this.map[s] = -1;
}
index() {
const map = this.map;
const array = this.array;
const partialMap = Object.create(null);
let k, kp;
let i, len;
// Create a map that will contain all keys and partial keys.
for (k in map) {
if (!k) {
partialMap[k] = k;
}
else {
for (i = 0, len = k.length; i < len; i++) {
kp = k.substr(i);
if (!hasOwn.call(partialMap, kp) || partialMap[kp].length < len)
partialMap[kp] = k;
}
}
}
// Create an array that will only contain keys that are needed.
for (k in map)
if (partialMap[k] === k)
array.push(k);
array.sort();
// Create valid offsets to the `array`.
let offMap = Object.create(null);
let offset = 0;
for (i = 0, len = array.length; i < len; i++) {
k = array[i];
offMap[k] = offset;
offset += k.length + 1;
}
this.size = offset;
// Assign valid offsets to `map`.
for (kp in map) {
k = partialMap[kp];
map[kp] = offMap[k] + k.length - kp.length;
}
}
format(indent, justify) {
if (this.size === -1)
FATAL(`IndexedString.format(): not indexed yet, call index()`);
const array = this.array;
if (!justify) justify = 0;
let i;
let s = "";
let line = "";
for (i = 0; i < array.length; i++) {
const item = "\"" + array[i] + ((i !== array.length - 1) ? "\\0\"" : "\";");
const newl = line + (line ? " " : indent) + item;
if (newl.length <= justify) {
line = newl;
continue;
}
else {
s += line + "\n";
line = indent + item;
}
}
return s + line;
}
getSize() {
if (this.size === -1)
FATAL(`IndexedString.getSize(): Not indexed yet, call index()`);
return this.size;
}
getIndex(k) {
if (this.size === -1)
FATAL(`IndexedString.getIndex(): Not indexed yet, call index()`);
if (!hasOwn.call(this.map, k))
FATAL(`IndexedString.getIndex(): Key '${k}' not found.`);
return this.map[k];
}
}
exports.IndexedString = IndexedString;