mirror of
https://github.com/bendtherules/ask262.git
synced 2026-08-19 00:31:06 +00:00
Merge commit '6e92d2345e8231a44556ce7d416451f5f3e6c398' as 'engine262'
This commit is contained in:
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user