Files
ask262/engine262/src/intrinsics/Object.mts
T

466 lines
18 KiB
TypeScript

import {
NullValue,
ObjectValue,
Value,
type Arguments,
type FunctionCallContext,
UndefinedValue,
type PropertyKeyValue,
Descriptor,
SymbolValue,
JSStringValue,
} from '../value.mts';
import {
surroundingAgent,
} from '../host-defined/engine.mts';
import { Q, X, type ValueEvaluator } from '../completion.mts';
import { AddEntriesFromIterable } from './Map.mts';
import { bootstrapConstructor } from './bootstrap.mts';
import {
Assert,
CreateArrayFromList,
CreateDataProperty,
DefinePropertyOrThrow,
CreateDataPropertyOrThrow,
EnumerableOwnProperties,
FromPropertyDescriptor,
Get,
HasOwnProperty,
IsExtensible,
OrdinaryObjectCreate,
OrdinaryCreateFromConstructor,
RequireObjectCoercible,
SameValue,
Set,
SetIntegrityLevel,
TestIntegrityLevel,
ToObject,
ToPropertyDescriptor,
ToPropertyKey,
CreateBuiltinFunction,
Realm,
type FunctionObject,
GroupBy,
type KeyedGroupRecord,
} from '#self';
/** https://tc39.es/ecma262/#sec-object-value */
function* ObjectConstructor([value = Value.undefined]: Arguments, { NewTarget }: FunctionCallContext): ValueEvaluator {
// 1. If NewTarget is neither undefined nor the active function, then
if (NewTarget !== Value.undefined && NewTarget !== surroundingAgent.activeFunctionObject) {
// a. Return ? OrdinaryCreateFromConstructor(NewTarget, "%Object.prototype%").
return yield* OrdinaryCreateFromConstructor(NewTarget as FunctionObject, '%Object.prototype%');
}
// 2. If value is undefined or null, return OrdinaryObjectCreate(%Object.prototype%).
if (value === Value.null || value === Value.undefined) {
return OrdinaryObjectCreate(surroundingAgent.intrinsic('%Object.prototype%'));
}
// 3. Return ! ToObject(value).
return X(ToObject(value));
}
/** https://tc39.es/ecma262/#sec-object.assign */
function* Object_assign([target = Value.undefined, ...sources]: Arguments): ValueEvaluator {
// 1. Let to be ? ToObject(target).
const to = Q(ToObject(target));
// 2. If only one argument was passed, return to.
if (sources.length === 0) {
return to;
}
// 3. Let sources be the List of argument values starting with the second argument.
// 4. For each element nextSource of sources, in ascending index order, do
for (const nextSource of (sources as Arguments).values()) {
// a. If nextSource is neither undefined nor null, then
if (nextSource !== Value.undefined && nextSource !== Value.null) {
// i. Let from be ! ToObject(nextSource).
const from = X(ToObject(nextSource));
// ii. Let keys be ? from.[[OwnPropertyKeys]]().
const keys = Q(yield* from.OwnPropertyKeys());
// iii. For each element nextKey of keys in List order, do
for (const nextKey of keys) {
// 1. Let desc be ? from.[[GetOwnProperty]](nextKey).
const desc = Q(yield* from.GetOwnProperty(nextKey));
// 2. If desc is not undefined and desc.[[Enumerable]] is true, then
if (!(desc instanceof UndefinedValue) && desc.Enumerable === Value.true) {
// a. Let propValue be ? Get(from, nextKey).
const propValue = Q(yield* Get(from, nextKey));
// b. Perform ? Set(to, nextKey, propValue, true).
Q(yield* Set(to, nextKey, propValue, Value.true));
}
}
}
}
// 5. Return to.
return to;
}
/** https://tc39.es/ecma262/#sec-object.create */
function* Object_create([O = Value.undefined, Properties = Value.undefined]: Arguments) {
// 1. If Type(O) is neither Object nor Null, throw a TypeError exception.
if (!(O instanceof ObjectValue) && !(O instanceof NullValue)) {
return surroundingAgent.Throw('TypeError', 'ObjectPrototypeType');
}
// 2. Let obj be OrdinaryObjectCreate(O).
const obj = OrdinaryObjectCreate(O);
// 3. If Properties is not undefined, then
if (Properties !== Value.undefined) {
// a. Return ? ObjectDefineProperties(obj, Properties).
return Q(yield* ObjectDefineProperties(obj, Properties));
}
// 4. Return obj.
return obj;
}
/** https://tc39.es/ecma262/#sec-object.defineproperties */
function* Object_defineProperties([O = Value.undefined, Properties = Value.undefined]: Arguments): ValueEvaluator {
// 1. Return ? ObjectDefineProperties(O, Properties).
return Q(yield* ObjectDefineProperties(O, Properties));
}
/** https://tc39.es/ecma262/#sec-objectdefineproperties ObjectDefineProperties */
function* ObjectDefineProperties(O: Value, Properties: Value) {
// 1. If Type(O) is not Object, throw a TypeError exception.
if (!(O instanceof ObjectValue)) {
return surroundingAgent.Throw('TypeError', 'NotAnObject', O);
}
// 2. Let props be ? ToObject(Properties).
const props = Q(ToObject(Properties));
// 3. Let keys be ? props.[[OwnPropertyKeys]]().
const keys = Q(yield* props.OwnPropertyKeys());
// 4. Let descriptors be a new empty List.
const descriptors: [PropertyKeyValue, Descriptor][] = [];
// 5. For each element nextKey of keys in List order, do
for (const nextKey of keys) {
// a. Let propDesc be ? props.[[GetOwnProperty]](nextKey).
const propDesc = Q(yield* props.GetOwnProperty(nextKey));
// b. If propDesc is not undefined and propDesc.[[Enumerable]] is true, then
if (!(propDesc instanceof UndefinedValue) && propDesc.Enumerable === Value.true) {
// i. Let descObj be ? Get(props, nextKey).
const descObj = Q(yield* Get(props, nextKey));
// ii. Let desc be ? ToPropertyDescriptor(descObj).
const desc = Q(yield* ToPropertyDescriptor(descObj));
// iii. Append the pair (a two element List) consisting of nextKey and desc to the end of descriptors.
descriptors.push([nextKey, desc]);
}
}
// 6. For each pair from descriptors in list order, do
for (const pair of descriptors) {
// a. Let P be the first element of pair.
const P = pair[0];
// b. Let desc be the second element of pair.
const desc = pair[1];
// c. Perform ? DefinePropertyOrThrow(O, P, desc).
Q(yield* DefinePropertyOrThrow(O, P, desc));
}
// 7. Return O.
return O;
}
/** https://tc39.es/ecma262/#sec-object.defineproperty */
function* Object_defineProperty([O = Value.undefined, P = Value.undefined, Attributes = Value.undefined]: Arguments) {
// 1. If Type(O) is not Object, throw a TypeError exception.
if (!(O instanceof ObjectValue)) {
return surroundingAgent.Throw('TypeError', 'NotAnObject', O);
}
// 2. Let key be ? ToPropertyKey(P).
const key = Q(yield* ToPropertyKey(P));
// 3. Let desc be ? ToPropertyDescriptor(Attributes).
const desc = Q(yield* ToPropertyDescriptor(Attributes));
// 4. Perform ? DefinePropertyOrThrow(O, key, desc).
Q(yield* DefinePropertyOrThrow(O, key, desc));
// 5. Return O.
return O;
}
/** https://tc39.es/ecma262/#sec-object.entries */
function* Object_entries([O = Value.undefined]: Arguments): ValueEvaluator {
// 1. Let obj be ? ToObject(O).
const obj = Q(ToObject(O));
// 2. Let nameList be ? EnumerableOwnPropertyNames(obj, key+value).
const nameList = Q(yield* EnumerableOwnProperties(obj, 'key+value'));
// 3. Return CreateArrayFromList(nameList).
return CreateArrayFromList(nameList);
}
/** https://tc39.es/ecma262/#sec-object.freeze */
function* Object_freeze([O = Value.undefined]: Arguments) {
// 1. If Type(O) is not Object, return O.
if (!(O instanceof ObjectValue)) {
return O;
}
// 2. Let status be ? SetIntegrityLevel(O, frozen).
const status = Q(yield* SetIntegrityLevel(O, 'frozen'));
// 3. If status is false, throw a TypeError exception.
if (status === Value.false) {
return surroundingAgent.Throw('TypeError', 'UnableToFreeze', O);
}
// 4. Return O.
return O;
}
/** https://tc39.es/ecma262/#sec-object.fromentries */
function* Object_fromEntries([iterable = Value.undefined]: Arguments): ValueEvaluator {
// 1. Perform ? RequireObjectCoercible(iterable).
Q(RequireObjectCoercible(iterable));
// 2. Let obj be ! OrdinaryObjectCreate(%Object.prototype%).
const obj = X(OrdinaryObjectCreate(surroundingAgent.intrinsic('%Object.prototype%')));
// 3. Assert: obj is an extensible ordinary object with no own properties.
Assert(obj.Extensible === Value.true && obj.properties.size === 0);
// 4. Let closure be a new Abstract Closure with parameters (key, value) that captures obj and performs the following steps when called:
function* closure([key = Value.undefined, value = Value.undefined]: Arguments): ValueEvaluator {
// a. Let propertyKey be ? ToPropertyKey(key).
const propertyKey = Q(yield* ToPropertyKey(key));
// b. Perform ! CreateDataPropertyOrThrow(obj, propertyKey, value).
X(CreateDataPropertyOrThrow(obj, propertyKey, value));
// c. Return undefined.
return Value.undefined;
}
// 5. Let adder be ! CreateBuiltinFunction(closure, 2, "", « »).
const adder = X(CreateBuiltinFunction(closure, 2, Value(''), []));
// 6. Return ? AddEntriesFromIterable(obj, iterable, adder).
return Q(yield* AddEntriesFromIterable(obj, iterable, adder));
}
/** https://tc39.es/ecma262/#sec-object.getownpropertydescriptor */
function* Object_getOwnPropertyDescriptor([O = Value.undefined, P = Value.undefined]: Arguments): ValueEvaluator {
// 1. Let obj be ? ToObject(O).
const obj = Q(ToObject(O));
// 2. Let key be ? ToPropertyKey(P).
const key = Q(yield* ToPropertyKey(P));
// 3. Let desc be ? obj.[[GetOwnProperty]](key).
const desc = Q(yield* obj.GetOwnProperty(key));
// 4. Return FromPropertyDescriptor(desc).
return FromPropertyDescriptor(desc);
}
/** https://tc39.es/ecma262/#sec-object.getownpropertydescriptors */
function* Object_getOwnPropertyDescriptors([O = Value.undefined]: Arguments): ValueEvaluator {
// 1. Let obj be ? ToObject(O).
const obj = Q(ToObject(O));
// 2. Let ownKeys be ? obj.[[OwnPropertyKeys]]().
const ownKeys = Q(yield* obj.OwnPropertyKeys());
// 3. Let descriptors be ! OrdinaryObjectCreate(%Object.prototype%).
const descriptors = X(OrdinaryObjectCreate(surroundingAgent.intrinsic('%Object.prototype%')));
// 4. For each element key of ownKeys in List order, do
for (const key of ownKeys) {
// a. Let desc be ? obj.[[GetOwnProperty]](key).
const desc = Q(yield* obj.GetOwnProperty(key));
// b. Let descriptor be ! FromPropertyDescriptor(desc).
const descriptor = X(FromPropertyDescriptor(desc));
// c. If descriptor is not undefined, perform ! CreateDataPropertyOrThrow(descriptors, key, descriptor).
if (descriptor !== Value.undefined) {
X(CreateDataProperty(descriptors, key, descriptor));
}
}
// 5. Return descriptors.
return descriptors;
}
/** https://tc39.es/ecma262/#sec-getownpropertykeys */
function* GetOwnPropertyKeys(O: Value, type: 'String' | 'Symbol'): ValueEvaluator {
// 1. Let obj be ? ToObject(O).
const obj = Q(ToObject(O));
// 2. Let keys be ? obj.[[OwnPropertyKeys]]().
const keys = Q(yield* obj.OwnPropertyKeys());
// 3. Let nameList be a new empty List.
const nameList: PropertyKeyValue[] = [];
// 4. For each element nextKey of keys in List order, do
keys.forEach((nextKey) => {
// a. If nextKey is a Symbol and type is symbol, or if nextKey is a String and type is string, then
if ((type === 'Symbol' && nextKey instanceof SymbolValue) || (type === 'String' && nextKey instanceof JSStringValue)) {
// i. Append nextKey as the last element of nameList.
nameList.push(nextKey);
}
});
return CreateArrayFromList(nameList);
}
/** https://tc39.es/ecma262/#sec-object.getownpropertynames */
function* Object_getOwnPropertyNames([O = Value.undefined]: Arguments): ValueEvaluator {
// 1. Return ? GetOwnPropertyKeys(O, string).
return Q(yield* GetOwnPropertyKeys(O, 'String'));
}
/** https://tc39.es/ecma262/#sec-object.getownpropertysymbols */
function* Object_getOwnPropertySymbols([O = Value.undefined]: Arguments): ValueEvaluator {
// 1. Return ? GetOwnPropertyKeys(O, symbol).
return Q(yield* GetOwnPropertyKeys(O, 'Symbol'));
}
/** https://tc39.es/ecma262/#sec-object.getprototypeof */
function* Object_getPrototypeOf([O = Value.undefined]: Arguments): ValueEvaluator {
// 1. Let obj be ? ToObject(O).
const obj = Q(ToObject(O));
// 2. Return ? obj.[[GetPrototypeOf]]().
return Q(yield* obj.GetPrototypeOf());
}
/** https://tc39.es/ecma262/#sec-object.groupby */
function* Object_groupBy([items = Value.undefined, callback = Value.undefined]: Arguments): ValueEvaluator {
/*
1. Let groups be ? GroupBy(items, callback, property).
2. Let obj be OrdinaryObjectCreate(null).
3. For each Record { [[Key]], [[Elements]] } g of groups, do
a. Let elements be CreateArrayFromList(g.[[Elements]]).
b. Perform ! CreateDataPropertyOrThrow(obj, g.[[Key]], elements).
4. Return obj.
*/
const groups: KeyedGroupRecord[] = Q(yield* GroupBy(items, callback, 'property'));
const obj = OrdinaryObjectCreate(Value.null);
for (const g of groups) {
const elements = CreateArrayFromList(g.Elements);
X(CreateDataPropertyOrThrow(obj, g.Key, elements));
}
return obj;
}
/** https://tc39.es/ecma262/#sec-object.hasown */
function* Object_hasOwn([O = Value.undefined, P = Value.undefined]: Arguments): ValueEvaluator {
// 1. Let obj be ? ToObject(O).
const obj = Q(ToObject(O));
// 2. Let O be ? ToObject(this value).
const key = Q(yield* ToPropertyKey(P));
// 3. Return ? HasOwnProperty(obj, key).
return yield* HasOwnProperty(obj, key);
}
/** https://tc39.es/ecma262/#sec-object.is */
function Object_is([value1 = Value.undefined, value2 = Value.undefined]: Arguments) {
// 1. Return SameValue(value1, value2).
return SameValue(value1, value2);
}
/** https://tc39.es/ecma262/#sec-object.isextensible */
function* Object_isExtensible([O = Value.undefined]: Arguments): ValueEvaluator {
// 1. If Type(O) is not Object, return false.
if (!(O instanceof ObjectValue)) {
return Value.false;
}
// 2. Return ? IsExtensible(O).
return Q(yield* IsExtensible(O));
}
/** https://tc39.es/ecma262/#sec-object.isfrozen */
function* Object_isFrozen([O = Value.undefined]: Arguments): ValueEvaluator {
// 1. If Type(O) is not Object, return true.
if (!(O instanceof ObjectValue)) {
return Value.true;
}
// 2. Return ? TestIntegrityLevel(O, frozen).
return Q(yield* TestIntegrityLevel(O, 'frozen'));
}
/** https://tc39.es/ecma262/#sec-object.issealed */
function* Object_isSealed([O = Value.undefined]: Arguments): ValueEvaluator {
// 1. If Type(O) is not Object, return true.
if (!(O instanceof ObjectValue)) {
return Value.true;
}
// 2. Return ? TestIntegrityLevel(O, sealed).
return Q(yield* TestIntegrityLevel(O, 'sealed'));
}
/** https://tc39.es/ecma262/#sec-object.keys */
function* Object_keys([O = Value.undefined]: Arguments): ValueEvaluator {
// 1. Let obj be ? ToObject(O).
const obj = Q(ToObject(O));
// 2. Let nameList be ? EnumerableOwnPropertyNames(obj, key).
const nameList = Q(yield* EnumerableOwnProperties(obj, 'key'));
// 3. Return CreateArrayFromList(nameList).
return CreateArrayFromList(nameList);
}
/** https://tc39.es/ecma262/#sec-object.preventextensions */
function* Object_preventExtensions([O = Value.undefined]: Arguments) {
// 1. If Type(O) is not Object, return O.
if (!(O instanceof ObjectValue)) {
return O;
}
// 2. Let status be ? O.[[PreventExtensions]]().
const status = Q(yield* O.PreventExtensions());
// 3. If status is false, throw a TypeError exception.
if (status === Value.false) {
return surroundingAgent.Throw('TypeError', 'UnableToPreventExtensions', O);
}
// 4. Return O.
return O;
}
/** https://tc39.es/ecma262/#sec-object.seal */
function* Object_seal([O = Value.undefined]: Arguments) {
// 1. If Type(O) is not Object, return O.
if (!(O instanceof ObjectValue)) {
return O;
}
// 2. Let status be ? SetIntegrityLevel(O, sealed).
const status = Q(yield* SetIntegrityLevel(O, 'sealed'));
// 3. If status is false, throw a TypeError exception.
if (status === Value.false) {
return surroundingAgent.Throw('TypeError', 'UnableToSeal', O);
}
// 4. Return O.
return O;
}
/** https://tc39.es/ecma262/#sec-object.setprototypeof */
function* Object_setPrototypeOf([O = Value.undefined, proto = Value.undefined]: Arguments) {
// 1. Perform ? RequireObjectCoercible(O).
Q(RequireObjectCoercible(O));
// 2. If Type(proto) is neither Object nor Null, throw a TypeError exception.
if (!(proto instanceof ObjectValue) && !(proto instanceof NullValue)) {
return surroundingAgent.Throw('TypeError', 'ObjectPrototypeType');
}
// 3. If Type(O) is not Object, return O.
if (!(O instanceof ObjectValue)) {
return O;
}
// 4. Let status be ? O.[[SetPrototypeOf]](proto).
const status = Q(yield* O.SetPrototypeOf(proto));
// 5. If status is false, throw a TypeError exception.
if (status === Value.false) {
return surroundingAgent.Throw('TypeError', 'ObjectSetPrototype');
}
// 6. Return O.
return O;
}
/** https://tc39.es/ecma262/#sec-object.values */
function* Object_values([O = Value.undefined]: Arguments): ValueEvaluator {
// 1. Let obj be ? ToObject(O).
const obj = Q(ToObject(O));
// 2. Let nameList be ? EnumerableOwnPropertyNames(obj, value).
const nameList = Q(yield* EnumerableOwnProperties(obj, 'value'));
// 3. Return CreateArrayFromList(nameList).
return CreateArrayFromList(nameList);
}
export function bootstrapObject(realmRec: Realm) {
const objectConstructor = bootstrapConstructor(realmRec, ObjectConstructor, 'Object', 1, realmRec.Intrinsics['%Object.prototype%'], [
['assign', Object_assign, 2],
['create', Object_create, 2],
['defineProperties', Object_defineProperties, 2],
['defineProperty', Object_defineProperty, 3],
['entries', Object_entries, 1],
['freeze', Object_freeze, 1],
['fromEntries', Object_fromEntries, 1],
['getOwnPropertyDescriptor', Object_getOwnPropertyDescriptor, 2],
['getOwnPropertyDescriptors', Object_getOwnPropertyDescriptors, 1],
['getOwnPropertyNames', Object_getOwnPropertyNames, 1],
['getOwnPropertySymbols', Object_getOwnPropertySymbols, 1],
['getPrototypeOf', Object_getPrototypeOf, 1],
['groupBy', Object_groupBy, 2],
['hasOwn', Object_hasOwn, 2],
['is', Object_is, 2],
['isExtensible', Object_isExtensible, 1],
['isFrozen', Object_isFrozen, 1],
['isSealed', Object_isSealed, 1],
['keys', Object_keys, 1],
['preventExtensions', Object_preventExtensions, 1],
['seal', Object_seal, 1],
['setPrototypeOf', Object_setPrototypeOf, 2],
['values', Object_values, 1],
]);
realmRec.Intrinsics['%Object%'] = objectConstructor;
}