Rust编写的JavaScript引擎,该项目是一个试验性质的项目。
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

379 lines
13 KiB

//! Edition detection utilities.
//!
//! This module contains the [`SpecEdition`] struct, which is used in the tester to
//! classify all tests per minimum required ECMAScript edition.
use std::fmt::Display;
use serde_repr::{Deserialize_repr, Serialize_repr};
use crate::read::{MetaData, TestFlag};
/// Minimum edition required by a specific feature in the `test262` repository.
static FEATURE_EDITION: phf::Map<&'static str, SpecEdition> = phf::phf_map! {
// Proposed language features
// Intl.Locale Info
// https://github.com/tc39/proposal-intl-locale-info
"Intl.Locale-info" => SpecEdition::ESNext,
// FinalizationRegistry#cleanupSome
// https://github.com/tc39/proposal-cleanup-some
"FinalizationRegistry.prototype.cleanupSome" => SpecEdition::ESNext,
// Intl.NumberFormat V3
// https://github.com/tc39/proposal-intl-numberformat-v3
"Intl.NumberFormat-v3" => SpecEdition::ESNext,
// Legacy RegExp features
// https://github.com/tc39/proposal-regexp-legacy-features
"legacy-regexp" => SpecEdition::ESNext,
// Import Attributes
// https://github.com/tc39/proposal-import-attributes/
"import-attributes" => SpecEdition::ESNext,
// Import Assertions
// https://github.com/tc39/proposal-import-assertions/
"import-assertions" => SpecEdition::ESNext,
// JSON modules
// https://github.com/tc39/proposal-json-modules
"json-modules" => SpecEdition::ESNext,
// Resizable Arraybuffer
// https://github.com/tc39/proposal-resizablearraybuffer
"resizable-arraybuffer" => SpecEdition::ESNext,
// ArrayBuffer transfer
// https://github.com/tc39/proposal-arraybuffer-transfer
"arraybuffer-transfer" => SpecEdition::ESNext,
// Temporal
// https://github.com/tc39/proposal-temporal
"Temporal" => SpecEdition::ESNext,
// ShadowRealm, née Callable Boundary Realms
// https://github.com/tc39/proposal-realms
"ShadowRealm" => SpecEdition::ESNext,
// Array.prototype.group & Array.prototype.groupToMap
// https://github.com/tc39/proposal-array-grouping
"array-grouping" => SpecEdition::ESNext,
// Intl.DurationFormat
// https://github.com/tc39/proposal-intl-duration-format
"Intl.DurationFormat" => SpecEdition::ESNext,
// Decorators
// https://github.com/tc39/proposal-decorators
"decorators" => SpecEdition::ESNext,
// Duplicate named capturing groups
// https://github.com/tc39/proposal-duplicate-named-capturing-groups
"regexp-duplicate-named-groups" => SpecEdition::ESNext,
// https://tc39.es/proposal-array-from-async/
"Array.fromAsync" => SpecEdition::ESNext,
// JSON.parse with source
// https://github.com/tc39/proposal-json-parse-with-source
"json-parse-with-source" => SpecEdition::ESNext,
// Iterator Helpers
// https://github.com/tc39/proposal-iterator-helpers
"iterator-helpers" => SpecEdition::ESNext,
// Promise.withResolvers
// https://github.com/tc39/proposal-promise-with-resolvers
"promise-with-resolvers" => SpecEdition::ESNext,
// Set methods
// https://github.com/tc39/proposal-set-methods
"set-methods" => SpecEdition::ESNext,
// Part of the next ES15 edition
"Atomics.waitAsync" => SpecEdition::ESNext,
"regexp-v-flag" => SpecEdition::ESNext,
"String.prototype.isWellFormed" => SpecEdition::ESNext,
"String.prototype.toWellFormed" => SpecEdition::ESNext,
// Standard language features
"AggregateError" => SpecEdition::ES12,
"align-detached-buffer-semantics-with-web-reality" => SpecEdition::ES12,
"arbitrary-module-namespace-names" => SpecEdition::ES13,
"ArrayBuffer" => SpecEdition::ES6,
"array-find-from-last" => SpecEdition::ES14,
"Array.prototype.at" => SpecEdition::ES13,
"Array.prototype.flat" => SpecEdition::ES10,
"Array.prototype.flatMap" => SpecEdition::ES10,
"Array.prototype.includes" => SpecEdition::ES7,
"Array.prototype.values" => SpecEdition::ES6,
"arrow-function" => SpecEdition::ES6,
"async-iteration" => SpecEdition::ES9,
"async-functions" => SpecEdition::ES8,
"Atomics" => SpecEdition::ES8,
"BigInt" => SpecEdition::ES11,
"caller" => SpecEdition::ES5,
"change-array-by-copy" => SpecEdition::ES14,
"class" => SpecEdition::ES6,
"class-fields-private" => SpecEdition::ES13,
"class-fields-private-in" => SpecEdition::ES13,
"class-fields-public" => SpecEdition::ES13,
"class-methods-private" => SpecEdition::ES13,
"class-static-block" => SpecEdition::ES13,
"class-static-fields-private" => SpecEdition::ES13,
"class-static-fields-public" => SpecEdition::ES13,
"class-static-methods-private" => SpecEdition::ES13,
"coalesce-expression" => SpecEdition::ES11,
"computed-property-names" => SpecEdition::ES6,
"const" => SpecEdition::ES6,
"cross-realm" => SpecEdition::ES6,
"DataView" => SpecEdition::ES6,
"DataView.prototype.getFloat32" => SpecEdition::ES6,
"DataView.prototype.getFloat64" => SpecEdition::ES6,
"DataView.prototype.getInt16" => SpecEdition::ES6,
"DataView.prototype.getInt32" => SpecEdition::ES6,
"DataView.prototype.getInt8" => SpecEdition::ES6,
"DataView.prototype.getUint16" => SpecEdition::ES6,
"DataView.prototype.getUint32" => SpecEdition::ES6,
"DataView.prototype.setUint8" => SpecEdition::ES6,
"default-parameters" => SpecEdition::ES6,
"destructuring-assignment" => SpecEdition::ES6,
"destructuring-binding" => SpecEdition::ES6,
"dynamic-import" => SpecEdition::ES11,
"error-cause" => SpecEdition::ES13,
"exponentiation" => SpecEdition::ES7,
"export-star-as-namespace-from-module" => SpecEdition::ES11,
"FinalizationRegistry" => SpecEdition::ES12,
"for-in-order" => SpecEdition::ES11,
"for-of" => SpecEdition::ES6,
"Float32Array" => SpecEdition::ES6,
"Float64Array" => SpecEdition::ES6,
"generators" => SpecEdition::ES6,
"globalThis" => SpecEdition::ES11,
"hashbang" => SpecEdition::ES14,
"import.meta" => SpecEdition::ES11,
"Int8Array" => SpecEdition::ES6,
"Int16Array" => SpecEdition::ES6,
"Int32Array" => SpecEdition::ES6,
"Intl-enumeration" => SpecEdition::ES14,
"intl-normative-optional" => SpecEdition::ES8,
"Intl.DateTimeFormat-datetimestyle" => SpecEdition::ES12,
"Intl.DateTimeFormat-dayPeriod" => SpecEdition::ES8,
"Intl.DateTimeFormat-extend-timezonename" => SpecEdition::ES13,
"Intl.DateTimeFormat-formatRange" => SpecEdition::ES12,
"Intl.DateTimeFormat-fractionalSecondDigits" => SpecEdition::ES12,
"Intl.DisplayNames" => SpecEdition::ES12,
"Intl.DisplayNames-v2" => SpecEdition::ES13,
"Intl.ListFormat" => SpecEdition::ES12,
"Intl.Locale" => SpecEdition::ES12,
"Intl.NumberFormat-unified" => SpecEdition::ES11,
"Intl.RelativeTimeFormat" => SpecEdition::ES11,
"Intl.Segmenter" => SpecEdition::ES13,
"json-superset" => SpecEdition::ES10,
"let" => SpecEdition::ES6,
"logical-assignment-operators" => SpecEdition::ES12,
"Map" => SpecEdition::ES6,
"new.target" => SpecEdition::ES6,
"numeric-separator-literal" => SpecEdition::ES12,
"object-rest" => SpecEdition::ES9,
"object-spread" => SpecEdition::ES9,
"Object.fromEntries" => SpecEdition::ES10,
"Object.hasOwn" => SpecEdition::ES13,
"Object.is" => SpecEdition::ES6,
"optional-catch-binding" => SpecEdition::ES10,
"optional-chaining" => SpecEdition::ES11,
"Promise" => SpecEdition::ES6,
"Promise.allSettled" => SpecEdition::ES11,
"Promise.any" => SpecEdition::ES12,
"Promise.prototype.finally" => SpecEdition::ES9,
"Proxy" => SpecEdition::ES6,
"proxy-missing-checks" => SpecEdition::ES6,
"Reflect" => SpecEdition::ES6,
"Reflect.construct" => SpecEdition::ES6,
"Reflect.set" => SpecEdition::ES6,
"Reflect.setPrototypeOf" => SpecEdition::ES6,
"regexp-dotall" => SpecEdition::ES9,
"regexp-lookbehind" => SpecEdition::ES9,
"regexp-match-indices" => SpecEdition::ES13,
"regexp-named-groups" => SpecEdition::ES9,
"regexp-unicode-property-escapes" => SpecEdition::ES9,
"rest-parameters" => SpecEdition::ES6,
"Set" => SpecEdition::ES6,
"SharedArrayBuffer" => SpecEdition::ES8,
"string-trimming" => SpecEdition::ES10,
"String.fromCodePoint" => SpecEdition::ES6,
"String.prototype.at" => SpecEdition::ES13,
"String.prototype.endsWith" => SpecEdition::ES6,
"String.prototype.includes" => SpecEdition::ES6,
"String.prototype.matchAll" => SpecEdition::ES11,
"String.prototype.replaceAll" => SpecEdition::ES12,
"String.prototype.trimEnd" => SpecEdition::ES10,
"String.prototype.trimStart" => SpecEdition::ES10,
"super" => SpecEdition::ES6,
"Symbol" => SpecEdition::ES6,
"symbols-as-weakmap-keys" => SpecEdition::ES14,
"Symbol.asyncIterator" => SpecEdition::ES9,
"Symbol.hasInstance" => SpecEdition::ES6,
"Symbol.isConcatSpreadable" => SpecEdition::ES6,
"Symbol.iterator" => SpecEdition::ES6,
"Symbol.match" => SpecEdition::ES6,
"Symbol.matchAll" => SpecEdition::ES11,
"Symbol.prototype.description" => SpecEdition::ES10,
"Symbol.replace" => SpecEdition::ES6,
"Symbol.search" => SpecEdition::ES6,
"Symbol.species" => SpecEdition::ES6,
"Symbol.split" => SpecEdition::ES6,
"Symbol.toPrimitive" => SpecEdition::ES6,
"Symbol.toStringTag" => SpecEdition::ES6,
"Symbol.unscopables" => SpecEdition::ES6,
"tail-call-optimization" => SpecEdition::ES6,
"template" => SpecEdition::ES6,
"top-level-await" => SpecEdition::ES13,
"TypedArray" => SpecEdition::ES6,
"TypedArray.prototype.at" => SpecEdition::ES13,
"u180e" => SpecEdition::ES7,
"Uint8Array" => SpecEdition::ES6,
"Uint16Array" => SpecEdition::ES6,
"Uint32Array" => SpecEdition::ES6,
"Uint8ClampedArray" => SpecEdition::ES6,
"WeakMap" => SpecEdition::ES6,
"WeakRef" => SpecEdition::ES12,
"WeakSet" => SpecEdition::ES6,
"well-formed-json-stringify" => SpecEdition::ES10,
"__proto__" => SpecEdition::ES6,
"__getter__" => SpecEdition::ES8,
"__setter__" => SpecEdition::ES8,
// Test-Harness Features
"IsHTMLDDA" => SpecEdition::ES9,
"host-gc-required" => SpecEdition::ES5,
};
/// List of ECMAScript editions that can be tested in the `test262` repository.
#[derive(
Debug,
Clone,
Copy,
PartialEq,
Eq,
PartialOrd,
Ord,
Default,
Serialize_repr,
Deserialize_repr,
clap::ValueEnum,
)]
#[repr(u8)]
pub(crate) enum SpecEdition {
/// ECMAScript 5.1 Edition
///
/// <https://262.ecma-international.org/5.1>
ES5 = 5,
/// ECMAScript 6th Edition
///
/// <https://262.ecma-international.org/6.0>
ES6,
/// ECMAScript 7th Edition
///
/// <https://262.ecma-international.org/7.0>
ES7,
/// ECMAScript 8th Edition
///
/// <https://262.ecma-international.org/8.0>
ES8,
/// ECMAScript 9th Edition
///
/// <https://262.ecma-international.org/9.0>
ES9,
/// ECMAScript 10th Edition
///
/// <https://262.ecma-international.org/10.0>
ES10,
/// ECMAScript 11th Edition
///
/// <https://262.ecma-international.org/11.0>
ES11,
/// ECMAScript 12th Edition
///
/// <https://262.ecma-international.org/12.0>
ES12,
/// ECMAScript 13th Edition
///
/// <https://262.ecma-international.org/13.0>
ES13,
/// ECMAScript 14th Edition
///
/// <https://262.ecma-international.org/14.0>
ES14,
/// The edition being worked on right now.
///
/// A draft is currently available [here](https://tc39.es/ecma262).
#[default]
ESNext = 255,
}
impl Display for SpecEdition {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match *self {
Self::ESNext => write!(f, "ECMAScript Next"),
Self::ES5 => write!(f, "ECMAScript 5.1"),
v => write!(f, "ECMAScript {}", v as u8),
}
}
}
impl SpecEdition {
/// Gets the minimum required ECMAScript edition of a test from its metadata.
///
/// If the function finds unknown features in `metadata`, returns an `Err(Vec<&str>)` containing
/// the list of unknown features.
pub(crate) fn from_test_metadata(metadata: &MetaData) -> Result<Self, Vec<&str>> {
let mut min_edition = if metadata.flags.contains(&TestFlag::Async) {
Self::ES8
} else if metadata.flags.contains(&TestFlag::Module)
|| metadata.esid.is_some()
|| metadata.es6id.is_some()
{
Self::ES6
} else {
Self::ES5
};
let mut unknowns = Vec::new();
for feature in &*metadata.features {
let Some(feature_edition) = FEATURE_EDITION.get(feature).copied() else {
unknowns.push(&**feature);
continue;
};
min_edition = std::cmp::max(min_edition, feature_edition);
}
if unknowns.is_empty() {
Ok(min_edition)
} else {
Err(unknowns)
}
}
/// Gets an iterator of all currently available editions.
pub(crate) fn all_editions() -> impl Iterator<Item = Self> {
[
Self::ES5,
Self::ES6,
Self::ES7,
Self::ES8,
Self::ES9,
Self::ES10,
Self::ES11,
Self::ES12,
Self::ES13,
Self::ES14,
Self::ESNext,
]
.into_iter()
}
}