/* 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/. */
/* global BigInt */

"use strict";

function matchingProperties(obj, regexp) {
  return Object.getOwnPropertyNames(obj)
    .filter(n => regexp.test(n))
    .map(n => obj[n])
    .filter(v => typeof v == "function");
}

function allProperties(obj) {
  return matchingProperties(obj, /./);
}

function getter(obj, name) {
  return Object.getOwnPropertyDescriptor(obj, name).get;
}

const TypedArray = Reflect.getPrototypeOf(Int8Array);

const functionAllowList = [
  Array,
  Array.from,
  Array.isArray,
  Array.of,
  Array.prototype.concat,
  Array.prototype.entries,
  Array.prototype.every,
  Array.prototype.filter,
  Array.prototype.find,
  Array.prototype.findIndex,
  Array.prototype.flat,
  Array.prototype.flatMap,
  Array.prototype.forEach,
  Array.prototype.includes,
  Array.prototype.indexOf,
  Array.prototype.join,
  Array.prototype.keys,
  Array.prototype.lastIndexOf,
  Array.prototype.map,
  Array.prototype.reduce,
  Array.prototype.reduceRight,
  Array.prototype.slice,
  Array.prototype.some,
  Array.prototype.values,
  ArrayBuffer,
  ArrayBuffer.isView,
  ArrayBuffer.prototype.slice,
  BigInt,
  ...allProperties(BigInt),
  Boolean,
  DataView,
  Date,
  Date.now,
  Date.parse,
  Date.UTC,
  ...matchingProperties(Date.prototype, /^get/),
  ...matchingProperties(Date.prototype, /^to.*?String$/),
  Error,
  Function,
  Function.prototype.apply,
  Function.prototype.bind,
  Function.prototype.call,
  Function.prototype[Symbol.hasInstance],
  Int8Array,
  Uint8Array,
  Uint8ClampedArray,
  Int16Array,
  Uint16Array,
  Int32Array,
  Uint32Array,
  Float32Array,
  Float64Array,
  TypedArray.from,
  TypedArray.of,
  TypedArray.prototype.entries,
  TypedArray.prototype.every,
  TypedArray.prototype.filter,
  TypedArray.prototype.find,
  TypedArray.prototype.findIndex,
  TypedArray.prototype.forEach,
  TypedArray.prototype.includes,
  TypedArray.prototype.indexOf,
  TypedArray.prototype.join,
  TypedArray.prototype.keys,
  TypedArray.prototype.lastIndexOf,
  TypedArray.prototype.map,
  TypedArray.prototype.reduce,
  TypedArray.prototype.reduceRight,
  TypedArray.prototype.slice,
  TypedArray.prototype.some,
  TypedArray.prototype.subarray,
  TypedArray.prototype.values,
  ...allProperties(JSON),
  Map,
  Map.prototype.forEach,
  Map.prototype.get,
  Map.prototype.has,
  Map.prototype.entries,
  Map.prototype.keys,
  Map.prototype.values,
  ...allProperties(Math),
  Number,
  ...allProperties(Number),
  ...allProperties(Number.prototype),
  Object,
  Object.create,
  Object.keys,
  Object.entries,
  Object.getOwnPropertyDescriptor,
  Object.getOwnPropertyDescriptors,
  Object.getOwnPropertyNames,
  Object.getOwnPropertySymbols,
  Object.getPrototypeOf,
  Object.is,
  Object.isExtensible,
  Object.isFrozen,
  Object.isSealed,
  Object.values,
  Object.prototype.hasOwnProperty,
  Object.prototype.isPrototypeOf,
  Proxy,
  Proxy.revocable,
  Reflect.apply,
  Reflect.construct,
  Reflect.get,
  Reflect.getOwnPropertyDescriptor,
  Reflect.getPrototypeOf,
  Reflect.has,
  Reflect.isExtensible,
  Reflect.ownKeys,
  RegExp,
  RegExp.prototype.exec,
  RegExp.prototype.test,
  RegExp.prototype[Symbol.match],
  RegExp.prototype[Symbol.search],
  RegExp.prototype[Symbol.replace],
  Set,
  Set.prototype.entries,
  Set.prototype.forEach,
  Set.prototype.has,
  Set.prototype.values,
  String,
  ...allProperties(String),
  ...allProperties(String.prototype),
  Symbol,
  Symbol.keyFor,
  WeakMap,
  WeakMap.prototype.get,
  WeakMap.prototype.has,
  WeakSet,
  WeakSet.prototype.has,
  decodeURI,
  decodeURIComponent,
  encodeURI,
  encodeURIComponent,
  escape,
  isFinite,
  isNaN,
  unescape,
];

const getterAllowList = [
  getter(ArrayBuffer.prototype, "byteLength"),
  getter(ArrayBuffer, Symbol.species),
  getter(Array, Symbol.species),
  getter(DataView.prototype, "buffer"),
  getter(DataView.prototype, "byteLength"),
  getter(DataView.prototype, "byteOffset"),
  getter(Error.prototype, "stack"),
  getter(Function.prototype, "arguments"),
  getter(Function.prototype, "caller"),
  getter(Intl.Locale.prototype, "baseName"),
  getter(Intl.Locale.prototype, "calendar"),
  getter(Intl.Locale.prototype, "caseFirst"),
  getter(Intl.Locale.prototype, "collation"),
  getter(Intl.Locale.prototype, "hourCycle"),
  getter(Intl.Locale.prototype, "numeric"),
  getter(Intl.Locale.prototype, "numberingSystem"),
  getter(Intl.Locale.prototype, "language"),
  getter(Intl.Locale.prototype, "script"),
  getter(Intl.Locale.prototype, "region"),
  getter(Map.prototype, "size"),
  getter(Map, Symbol.species),
  // NOTE: Object.prototype.__proto__ is not safe, because it can internally
  //       invoke Proxy getPrototypeOf handler.
  getter(Promise, Symbol.species),
  getter(RegExp, "input"),
  getter(RegExp, "lastMatch"),
  getter(RegExp, "lastParen"),
  getter(RegExp, "leftContext"),
  getter(RegExp, "rightContext"),
  getter(RegExp, "$1"),
  getter(RegExp, "$2"),
  getter(RegExp, "$3"),
  getter(RegExp, "$4"),
  getter(RegExp, "$5"),
  getter(RegExp, "$6"),
  getter(RegExp, "$7"),
  getter(RegExp, "$8"),
  getter(RegExp, "$9"),
  getter(RegExp, "$_"),
  getter(RegExp, "$&"),
  getter(RegExp, "$+"),
  getter(RegExp, "$`"),
  getter(RegExp, "$'"),
  getter(RegExp.prototype, "dotAll"),
  getter(RegExp.prototype, "flags"),
  getter(RegExp.prototype, "global"),
  getter(RegExp.prototype, "hasIndices"),
  getter(RegExp.prototype, "ignoreCase"),
  getter(RegExp.prototype, "multiline"),
  getter(RegExp.prototype, "source"),
  getter(RegExp.prototype, "sticky"),
  getter(RegExp.prototype, "unicode"),
  getter(RegExp.prototype, "unicodeSets"),
  getter(RegExp, Symbol.species),
  getter(Set.prototype, "size"),
  getter(Set, Symbol.species),
  getter(Symbol.prototype, "description"),
  getter(TypedArray.prototype, "buffer"),
  getter(TypedArray.prototype, "byteLength"),
  getter(TypedArray.prototype, "byteOffset"),
  getter(TypedArray.prototype, "length"),
  getter(TypedArray.prototype, Symbol.toStringTag),
  getter(TypedArray, Symbol.species),
];

// TODO: Integrate in main list when changes array by copy ships by default
const changesArrayByCopy = [
  Array.prototype.toReversed,
  Array.prototype.toSorted,
  Array.prototype.toSpliced,
  Array.prototype.with,
  TypedArray.prototype.toReversed,
  TypedArray.prototype.toSorted,
  TypedArray.prototype.with,
];
for (const fn of changesArrayByCopy) {
  if (typeof fn == "function") {
    functionAllowList.push(fn);
  }
}

module.exports = { functions: functionAllowList, getters: getterAllowList };
