Merge commit '6e92d2345e8231a44556ce7d416451f5f3e6c398' as 'engine262'

This commit is contained in:
2026-03-27 10:07:29 +05:30
433 changed files with 90478 additions and 0 deletions
+165
View File
@@ -0,0 +1,165 @@
import {
surroundingAgent,
} from '../host-defined/engine.mts';
import {
JSStringValue,
ObjectValue,
Value,
wellKnownSymbols,
type Arguments,
type FunctionCallContext,
} from '../value.mts';
import { Q } from '../completion.mts';
import type { ParseNode } from '../parser/ParseNode.mts';
import type { ValueEvaluator } from '../evaluator.mts';
import {
isDecimalDigit, isLineTerminator, isWhitespace,
} from '../parser/Lexer.mts';
import { isAsciiLetter, isControlEscape, isSyntaxCharacter } from '../parser/RegExpParser.mts';
import { bootstrapConstructor } from './bootstrap.mts';
import { UnicodeEscape } from './JSON.mts';
import {
Get,
IsRegExp,
Realm,
RegExpAlloc,
RegExpInitialize,
SameValue,
type FunctionObject,
type OrdinaryObject,
Assert, isLeadingSurrogate, isTrailingSurrogate, StringToCodePoints, UTF16EncodeCodePoint, type CodePoint, type RegExpMatcher, type RegExpRecord,
} from '#self';
export interface RegExpObject extends OrdinaryObject {
readonly OriginalSource: JSStringValue;
readonly OriginalFlags: JSStringValue;
readonly RegExpMatcher: RegExpMatcher;
readonly RegExpRecord: RegExpRecord;
readonly parsedPattern: ParseNode.RegExp.Pattern;
}
export function isRegExpObject(o: Value): o is RegExpObject {
return 'RegExpMatcher' in o;
}
/** https://tc39.es/ecma262/#sec-regexp-constructor */
function* RegExpConstructor([pattern = Value.undefined, flags = Value.undefined]: Arguments, { NewTarget }: FunctionCallContext): ValueEvaluator {
// 1. Let patternIsRegExp be ? IsRegExp(pattern).
const patternIsRegExp = Q(yield* IsRegExp(pattern));
let newTarget;
// 2. If NewTarget is undefined, then
if (NewTarget === Value.undefined) {
// a. Let newTarget be the active function object.
newTarget = surroundingAgent.activeFunctionObject;
// b. If patternIsRegExp is true and flags is undefined, then
if (patternIsRegExp === Value.true && flags === Value.undefined) {
// i. Let patternConstructor be ? Get(pattern, "constructor").
const patternConstructor = Q(yield* Get(pattern as ObjectValue, Value('constructor')));
// ii. If SameValue(newTarget, patternConstructor) is true, return pattern.
if (SameValue(newTarget, patternConstructor) === Value.true) {
return pattern;
}
}
} else { // 3. Else, let newTarget be NewTarget.
newTarget = NewTarget;
}
let P;
let F;
// 4. If Type(pattern) is Object and pattern has a [[RegExpMatcher]] internal slot, then
if (isRegExpObject(pattern)) {
// a. Let P be pattern.[[OriginalSource]].
P = pattern.OriginalSource;
// b. If flags is undefined, let F be pattern.[[OriginalFlags]].
if (flags === Value.undefined) {
F = pattern.OriginalFlags;
} else { // c. Else, let F be flags.
F = flags;
}
} else if (patternIsRegExp === Value.true) { // 5. Else if patternIsRegExp is true, then
// a. Else if patternIsRegExp is true, then
P = Q(yield* Get(pattern as ObjectValue, Value('source')));
// b. If flags is undefined, then
if (flags === Value.undefined) {
// i. Let F be ? Get(pattern, "flags").
F = Q(yield* Get(pattern as ObjectValue, Value('flags')));
} else { // c. Else, let F be flags.
F = flags;
}
} else { // 6. Else,
// a. Let P be pattern.
P = pattern;
// b. Let F be flags.
F = flags;
}
// 7. Let O be ? RegExpAlloc(newTarget).
const O = Q(yield* RegExpAlloc(newTarget as FunctionObject));
// 8. Return ? RegExpInitialize(O, P, F).
return Q(yield* RegExpInitialize(O, P, F));
}
/** https://tc39.es/ecma262/#sec-regexp.escape */
function* RegExp_escape([S = Value.undefined]: Arguments) {
if (!(S instanceof JSStringValue)) {
return surroundingAgent.Throw('TypeError', 'NotAString', S);
}
let escaped = '';
const cpList = StringToCodePoints(S.stringValue());
for (const cp of cpList) {
if (escaped === '' && (isDecimalDigit(String.fromCodePoint(cp)) || isAsciiLetter(cp))) {
const numericValue = cp;
const hex = numericValue.toString(16);
Assert(hex.length === 2);
escaped += `\u{005C}x${hex}`;
} else {
escaped += EncodeForRegExpEscape(cp);
}
}
return Value(escaped);
}
const table67: Record<number, string> = {
9: 't',
10: 'n',
11: 'v',
12: 'f',
13: 'r',
};
function EncodeForRegExpEscape(cp: CodePoint) {
const ch = String.fromCharCode(cp);
if (cp === 0x002F || isSyntaxCharacter(ch)) {
return `\u{005C}${UTF16EncodeCodePoint(cp)}`;
} else if (isControlEscape(cp)) {
return `\u{005C}${table67[cp]!}`;
}
const otherPunctuators = ",-=<>#&!%:;@~'`\u{0022}";
const toEscape = StringToCodePoints(otherPunctuators);
if (toEscape.includes(cp) || isWhitespace(ch) || isLineTerminator(ch) || isLeadingSurrogate(cp) || isTrailingSurrogate(cp)) {
const cpNum = cp;
if (cpNum <= 0xFF) {
const hex = cpNum.toString(16);
return `\u{005C}x${hex.padStart(2, '0')}`;
}
let escaped = '';
const codeUnits = UTF16EncodeCodePoint(cp);
for (const cu of codeUnits) {
escaped += UnicodeEscape(cu);
}
return escaped;
}
return UTF16EncodeCodePoint(cp);
}
/** https://tc39.es/ecma262/#sec-get-regexp-@@species */
function RegExp_speciesGetter(_args: Arguments, { thisValue }: FunctionCallContext) {
return thisValue;
}
export function bootstrapRegExp(realmRec: Realm) {
const proto = realmRec.Intrinsics['%RegExp.prototype%'];
const cons = bootstrapConstructor(realmRec, RegExpConstructor, 'RegExp', 2, proto, [
[wellKnownSymbols.species, [RegExp_speciesGetter]],
['escape', RegExp_escape, 1],
]);
realmRec.Intrinsics['%RegExp%'] = cons;
}