import { surroundingAgent } from '../host-defined/engine.mts'; import { BigIntValue, DataBlock, Descriptor, NumberValue, ObjectValue, UndefinedValue, Value, BooleanValue, } from '../value.mts'; import { Q, X } from '../completion.mts'; import type { PlainEvaluator } from '../evaluator.mts'; import type { Decimal } from '../host-defined/decimal.mts'; import { Assert, CreateDataProperty, Get, HasProperty, IsCallable, OrdinaryObjectCreate, ToBoolean, type FunctionObject, } from './all.mts'; import { isNonNegativeInteger } from './data-types-and-values.mts'; import { Throw } from '#self'; /** https://tc39.es/ecma262/#mathematical-value */ export type MathematicalValue = Decimal; /** https://tc39.es/ecma262/#extended-mathematical-value */ export type ExtendedMathematicalValue = MathematicalValue | 'Inf' | '-Inf'; /** https://tc39.es/ecma262/#sec-ecmascript-language-types-number-type */ export type Num = number; export type F = Num; /** https://tc39.es/ecma262/#sec-ecmascript-language-types-bigint-type */ export type BigInts = bigint & { /** @internal */ type?: 'bigint' }; /** https://tc39.es/ecma262/#integer */ export type Integer = bigint & { /** @internal */ type?: 'integer' }; /** https://tc39.es/ecma262/#integral-number */ export type IntegralNumber = Num & { /** @internal */ integral?: true; /** @internal */ finite?: true }; export type NaN = Num & { /** @internal */ integral?: false; /** @internal */ finite?: false; /** @internal */ value: 'NaN' }; // #𝔽 export function F(x: number): NumberValue { Assert(typeof x === 'number'); return Value(x); } // #ℤ export function Z(x: bigint): BigIntValue { Assert(typeof x === 'bigint'); return Value(x); } // #ℝ export function R(x: NumberValue): number; export function R(x: BigIntValue): bigint; export function R(x: BigIntValue | NumberValue): bigint | number; export function R(x: unknown) { if (x instanceof BigIntValue) { return x.bigintValue(); // eslint-disable-line @engine262/mathematical-value } Assert(x instanceof NumberValue); const number = x.numberValue(); // eslint-disable-line @engine262/mathematical-value if (Object.is(number, -0)) { return 0; } return number; } // 6.2.5.1 IsAccessorDescriptor export function IsAccessorDescriptor(Desc: Descriptor): Desc is Descriptor & { Get: Value; Set: Value } { if (Desc.Get === undefined && Desc.Set === undefined) { return false; } return true; } // 6.2.5.2 IsDataDescriptor export function IsDataDescriptor(Desc: Descriptor): Desc is Descriptor & { Value: Value; Writable: BooleanValue } { if (Desc.Value === undefined && Desc.Writable === undefined) { return false; } return true; } // 6.2.5.3 IsGenericDescriptor export function IsGenericDescriptor(Desc: Descriptor) { if (!IsAccessorDescriptor(Desc) && !IsDataDescriptor(Desc)) { return true; } return false; } /** https://tc39.es/ecma262/#sec-frompropertydescriptor */ export function FromPropertyDescriptor(Desc: Descriptor | UndefinedValue) { if (Desc instanceof UndefinedValue) { return Value.undefined; } const obj = OrdinaryObjectCreate(surroundingAgent.intrinsic('%Object.prototype%')); if (Desc.Value !== undefined) { X(CreateDataProperty(obj, Value('value'), Desc.Value)); } if (Desc.Writable !== undefined) { X(CreateDataProperty(obj, Value('writable'), Desc.Writable)); } if (Desc.Get !== undefined) { X(CreateDataProperty(obj, Value('get'), Desc.Get)); } if (Desc.Set !== undefined) { X(CreateDataProperty(obj, Value('set'), Desc.Set)); } if (Desc.Enumerable !== undefined) { X(CreateDataProperty(obj, Value('enumerable'), Desc.Enumerable)); } if (Desc.Configurable !== undefined) { X(CreateDataProperty(obj, Value('configurable'), Desc.Configurable)); } // Assert: All of the above CreateDataProperty operations return true. return obj; } /** https://tc39.es/ecma262/#sec-topropertydescriptor */ export function* ToPropertyDescriptor(Obj: Value): PlainEvaluator { if (!(Obj instanceof ObjectValue)) { return Throw.TypeError('$1 is not an object', Obj); } let desc = Descriptor({}); const hasEnumerable = Q(yield* HasProperty(Obj, Value('enumerable'))); if (hasEnumerable === Value.true) { const enumerable = ToBoolean(Q(yield* Get(Obj, Value('enumerable')))); desc = Descriptor({ ...desc, Enumerable: enumerable }); } const hasConfigurable = Q(yield* HasProperty(Obj, Value('configurable'))); if (hasConfigurable === Value.true) { const conf = ToBoolean(Q(yield* Get(Obj, Value('configurable')))); desc = Descriptor({ ...desc, Configurable: conf }); } const hasValue = Q(yield* HasProperty(Obj, Value('value'))); if (hasValue === Value.true) { const value = Q(yield* Get(Obj, Value('value'))); desc = Descriptor({ ...desc, Value: value }); } const hasWritable = Q(yield* HasProperty(Obj, Value('writable'))); if (hasWritable === Value.true) { const writable = ToBoolean(Q(yield* Get(Obj, Value('writable')))); desc = Descriptor({ ...desc, Writable: writable }); } const hasGet = Q(yield* HasProperty(Obj, Value('get'))); if (hasGet === Value.true) { const getter = Q(yield* Get(Obj, Value('get'))); if (!IsCallable(getter) && !(getter instanceof UndefinedValue)) { return Throw.TypeError('getter ($1) in a property descriptor $2 must be a function', getter, Obj); } desc = Descriptor({ ...desc, Get: getter as FunctionObject }); } const hasSet = Q(yield* HasProperty(Obj, Value('set'))); if (hasSet === Value.true) { const setter = Q(yield* Get(Obj, Value('set'))); if (!IsCallable(setter) && !(setter instanceof UndefinedValue)) { return Throw.TypeError('setter ($1) in a property descriptor $2 must be a function', setter, Obj); } desc = Descriptor({ ...desc, Set: setter as FunctionObject }); } if (desc.Get !== undefined || desc.Set !== undefined) { if (desc.Value !== undefined || desc.Writable !== undefined) { return Throw.TypeError('Property descriptors must not specify both accessors and a value or writable attribute, but $1 does', Obj); } } return desc; } /** https://tc39.es/ecma262/#sec-completepropertydescriptor */ export function CompletePropertyDescriptor(Desc: Descriptor) { Assert(Desc instanceof Descriptor); const like = Descriptor({ Value: Value.undefined, Writable: Value.false, Get: Value.undefined, Set: Value.undefined, Enumerable: Value.false, Configurable: Value.false, }); if (IsGenericDescriptor(Desc) || IsDataDescriptor(Desc)) { if (Desc.Value === undefined) { Desc = Descriptor({ ...Desc, Value: like.Value }); } if (Desc.Writable === undefined) { Desc = Descriptor({ ...Desc, Writable: like.Writable }); } } else { if (Desc.Get === undefined) { Desc = Descriptor({ ...Desc, Get: like.Get }); } if (Desc.Set === undefined) { Desc = Descriptor({ ...Desc, Set: like.Set }); } } if (Desc.Enumerable === undefined) { Desc = Descriptor({ ...Desc, Enumerable: like.Enumerable }); } if (Desc.Configurable === undefined) { Desc = Descriptor({ ...Desc, Configurable: like.Configurable }); } return Desc; } /** @internal */ export let hostSupportResizableArrayBuffer = false; /** https://tc39.es/ecma262/#sec-createbytedatablock */ export function CreateByteDataBlock(size: number, _notInSpecMaxByteLength?: number | undefined) { Assert(isNonNegativeInteger(size)); if (size > 2 ** 53 - 1) { return Throw.RangeError('Invalid length'); } let db; try { const buffer = new ArrayBuffer(size, { get maxByteLength() { hostSupportResizableArrayBuffer = true; return _notInSpecMaxByteLength; }, }); db = new DataBlock(buffer); } catch (err) { return Throw.RangeError('Cannot allocate memory'); } return db; } /** https://tc39.es/ecma262/#sec-copydatablockbytes */ export function CopyDataBlockBytes(toBlock: DataBlock, toIndex: number, fromBlock: DataBlock, fromIndex: number, count: number) { Assert(fromBlock !== toBlock); Assert(Number.isSafeInteger(fromIndex) && fromIndex >= 0); Assert(Number.isSafeInteger(toIndex) && toIndex >= 0); Assert(Number.isSafeInteger(count) && count >= 0); const fromSize = fromBlock.byteLength; Assert(fromIndex + count <= fromSize); const toSize = toBlock.byteLength; Assert(toIndex + count <= toSize); while (count > 0) { toBlock[toIndex] = fromBlock[fromIndex]; toIndex += 1; fromIndex += 1; count -= 1; } }