Skip to content

Commit

Permalink
Use spec enums for ToPrimitive and OrdinaryToPrimitive
Browse files Browse the repository at this point in the history
  • Loading branch information
ExE-Boss committed Oct 20, 2020
1 parent d81af93 commit 48e131a
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 36 deletions.
16 changes: 8 additions & 8 deletions src/abstract-ops/testing-comparison.mjs
Expand Up @@ -207,16 +207,16 @@ export function AbstractRelationalComparison(x, y, LeftFirst = true) {
let py;
// 1. If the LeftFirst flag is true, then
if (LeftFirst === true) {
// a. Let px be ? ToPrimitive(x, hint Number).
px = Q(ToPrimitive(x, 'Number'));
// b. Let py be ? ToPrimitive(y, hint Number).
py = Q(ToPrimitive(y, 'Number'));
// a. Let px be ? ToPrimitive(x, number).
px = Q(ToPrimitive(x, 'number'));
// b. Let py be ? ToPrimitive(y, number).
py = Q(ToPrimitive(y, 'number'));
} else {
// a. NOTE: The order of evaluation needs to be reversed to preserve left to right evaluation.
// b. Let py be ? ToPrimitive(y, hint Number).
py = Q(ToPrimitive(y, 'Number'));
// c. Let px be ? ToPrimitive(x, hint Number).
px = Q(ToPrimitive(x, 'Number'));
// b. Let py be ? ToPrimitive(y, number).
py = Q(ToPrimitive(y, 'number'));
// c. Let px be ? ToPrimitive(x, number).
px = Q(ToPrimitive(x, 'number'));
}
// 3. If Type(px) is String and Type(py) is String, then
if (Type(px) === 'String' && Type(py) === 'String') {
Expand Down
78 changes: 55 additions & 23 deletions src/abstract-ops/type-conversion.mjs
Expand Up @@ -23,53 +23,77 @@ import {
} from './all.mjs';

// 7.1.1 #sec-toprimitive
export function ToPrimitive(input, PreferredType) {
export function ToPrimitive(input, preferredType) {
// 1. Assert: input is an ECMAScript language value.
Assert(input instanceof Value);
// 2. If Type(input) is Object, then
if (Type(input) === 'Object') {
let hint;
if (PreferredType === undefined) {
hint = new Value('default');
} else if (PreferredType === 'String') {
hint = new Value('string');
} else {
Assert(PreferredType === 'Number');
hint = new Value('number');
}
// a. Let exoticToPrim be ? GetMethod(input, @@toPrimitive).
const exoticToPrim = Q(GetMethod(input, wellKnownSymbols.toPrimitive));
// b. If exoticToPrim is not undefined, then
if (exoticToPrim !== Value.undefined) {
let hint;
// i. If preferredType is not present, let hint be "default".
if (preferredType === undefined) {
hint = 'default';
} else if (preferredType === 'string') { // ii. Else if preferredType is string, let hint be "string".
hint = 'string';
} else { // iii. Else,
// 1. Assert: preferredType is number.
Assert(preferredType === 'number');
// 2. Let hint be "number".
hint = 'number';
}
// iv. Let result be ? Call(exoticToPrim, input, « hint »).
const result = Q(Call(exoticToPrim, input, [hint]));
// v. If Type(result) is not Object, return result.
if (Type(result) !== 'Object') {
return result;
}
// vi. Throw a TypeError exception.
return surroundingAgent.Throw('TypeError', 'ObjectToPrimitive');
}
if (hint.stringValue() === 'default') {
hint = new Value('number');
// c. If preferredType is not present, let preferredType be number.
if (preferredType === undefined) {
preferredType = 'number';
}
return Q(OrdinaryToPrimitive(input, hint));
// d. Return ? OrdinaryToPrimitive(input, preferredType).
return Q(OrdinaryToPrimitive(input, preferredType));
}
// 3. Return input.
return input;
}

// 7.1.1.1 #sec-ordinarytoprimitive
export function OrdinaryToPrimitive(O, hint) {
// 1. Assert: Type(O) is Object.
Assert(Type(O) === 'Object');
Assert(Type(hint) === 'String' && (hint.stringValue() === 'string' || hint.stringValue() === 'number'));
// 2. Assert: hint is either string or number.
Assert(hint === 'string' || hint === 'number');
let methodNames;
if (hint.stringValue() === 'string') {
// 3. If hint is string, then
if (hint === 'string') {
// a. Let methodNames be « "toString", "valueOf" ».
methodNames = [new Value('toString'), new Value('valueOf')];
} else {
} else { // 4. Else,
// a. Let methodNames be « "valueOf", "toString" ».
methodNames = [new Value('valueOf'), new Value('toString')];
}
// 5. For each element name of methodNames, do
for (const name of methodNames) {
// a. Let method be ? Get(O, name).
const method = Q(Get(O, name));
// b. If IsCallable(method) is true, then
if (IsCallable(method) === Value.true) {
// i. Let result be ? Call(method, O).
const result = Q(Call(method, O));
// ii. If Type(result) is not Object, return result.
if (Type(result) !== 'Object') {
return result;
}
}
}
// 6. Throw a TypeError exception.
return surroundingAgent.Throw('TypeError', 'ObjectToPrimitive');
}

Expand Down Expand Up @@ -109,8 +133,8 @@ export function ToBoolean(argument) {

// #sec-tonumeric
export function ToNumeric(value) {
// 1. Let primValue be ? ToPrimitive(value, hint Number).
const primValue = Q(ToPrimitive(value, 'Number'));
// 1. Let primValue be ? ToPrimitive(value, number).
const primValue = Q(ToPrimitive(value, 'number'));
// 2. If Type(primValue) is BigInt, return primValue.
if (Type(primValue) === 'BigInt') {
return primValue;
Expand Down Expand Up @@ -141,7 +165,9 @@ export function ToNumber(argument) {
case 'Symbol':
return surroundingAgent.Throw('TypeError', 'CannotConvertSymbol', 'number');
case 'Object': {
const primValue = Q(ToPrimitive(argument, 'Number'));
// 1. Let primValue be ? ToPrimitive(argument, number).
const primValue = Q(ToPrimitive(argument, 'number'));
// 2. Return ? ToNumber(primValue).
return Q(ToNumber(primValue));
}
default:
Expand Down Expand Up @@ -278,8 +304,8 @@ export function ToUint8Clamp(argument) {

// #sec-tobigint
export function ToBigInt(argument) {
// 1. Let prim be ? ToPrimitive(argument, hint Number).
const prim = Q(ToPrimitive(argument, 'Number'));
// 1. Let prim be ? ToPrimitive(argument, number).
const prim = Q(ToPrimitive(argument, 'number'));
// 2. Return the value that prim corresponds to in Table 12 (#table-tobigint).
switch (Type(prim)) {
case 'Undefined':
Expand Down Expand Up @@ -374,7 +400,9 @@ export function ToString(argument) {
// Return ! BigInt::toString(argument).
return X(BigIntValue.toString(argument));
case 'Object': {
const primValue = Q(ToPrimitive(argument, 'String'));
// 1. Let primValue be ? ToPrimitive(argument, string).
const primValue = Q(ToPrimitive(argument, 'string'));
// 2. Return ? ToString(primValue).
return Q(ToString(primValue));
}
default:
Expand Down Expand Up @@ -421,10 +449,14 @@ export function ToObject(argument) {

// 7.1.14 #sec-topropertykey
export function ToPropertyKey(argument) {
const key = Q(ToPrimitive(argument, 'String'));
// 1. Let key be ? ToPrimitive(argument, string).
const key = Q(ToPrimitive(argument, 'string'));
// 2. If Type(key) is Symbol, then
if (Type(key) === 'Symbol') {
// a. Return key.
return key;
}
// 3. Return ! ToString(key).
return X(ToString(key));
}

Expand Down
4 changes: 2 additions & 2 deletions src/intrinsics/BigInt.mjs
Expand Up @@ -11,8 +11,8 @@ function BigIntConstructor([value], { NewTarget }) {
if (NewTarget !== Value.undefined) {
return surroundingAgent.Throw('TypeError', 'NotAConstructor', 'BigInt');
}
// 2. Let prim be ? ToPrimitive(value, hint Number).
const prim = Q(ToPrimitive(value, 'Number'));
// 2. Let prim be ? ToPrimitive(value, number).
const prim = Q(ToPrimitive(value, 'number'));
// 3. If Type(prim) is Number, return ? NumberToBigInt(prim).
// 4. Otherwise, return ? ToBigInt(value).
if (Type(prim) === 'Number') {
Expand Down
6 changes: 3 additions & 3 deletions src/intrinsics/DatePrototype.mjs
Expand Up @@ -515,7 +515,7 @@ function DateProto_toISOString(args, { thisValue }) {
// 20.3.4.37 #sec-date.prototype.tojson
function DateProto_toJSON(args, { thisValue }) {
const O = Q(ToObject(thisValue));
const tv = Q(ToPrimitive(O, 'Number'));
const tv = Q(ToPrimitive(O, 'number'));
if (Type(tv) === 'Number' && !Number.isFinite(tv.numberValue())) {
return Value.null;
}
Expand Down Expand Up @@ -644,9 +644,9 @@ function DateProto_toPrimitive([hint = Value.undefined], { thisValue }) {
}
let tryFirst;
if (Type(hint) === 'String' && (hint.stringValue() === 'string' || hint.stringValue() === 'default')) {
tryFirst = new Value('string');
tryFirst = 'string';
} else if (Type(hint) === 'String' && hint.stringValue() === 'number') {
tryFirst = new Value('number');
tryFirst = 'number';
} else {
return surroundingAgent.Throw('TypeError', 'InvalidHint', hint);
}
Expand Down

0 comments on commit 48e131a

Please sign in to comment.