move IteratorBindingInitialization to its own function

This commit is contained in:
Caleb Meredith 2017-05-26 16:55:15 -07:00
parent 4ac76366e9
commit a84e482941
6 changed files with 217 additions and 31 deletions

View File

@ -1196,7 +1196,6 @@ function filterFeatures(data: BannerData): boolean {
let features = data.features;
if (features.includes("class")) return false;
if (features.includes("default-parameters")) return false;
if (features.includes("destructuring-binding")) return false;
if (features.includes("generators")) return false;
return true;
}

View File

@ -541,6 +541,26 @@ export function CreateDataProperty(realm: Realm, O: ObjectValue, P: PropertyKeyV
return O.$DefineOwnProperty(P, newDesc);
}
// ECMA262 7.3.5
export function CreateMethodProperty(realm: Realm, O: ObjectValue, P: PropertyKeyValue, V: Value): boolean {
// 1. Assert: Type(O) is Object.
invariant(O instanceof ObjectValue, "Not an object value");
// 2. Assert: IsPropertyKey(P) is true.
invariant(IsPropertyKey(realm, P), "Not a property key");
// 3. Let newDesc be the PropertyDescriptor{[[Value]]: V, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true}.
let newDesc = {
value: V,
writable: true,
enumerable: false,
configurable: true
};
// 4. Return ? O.[[DefineOwnProperty]](P, newDesc).
return O.$DefineOwnProperty(P, newDesc);
}
// ECMA262 7.3.6
export function CreateDataPropertyOrThrow(realm: Realm, O: Value, P: PropertyKeyValue, V: Value): boolean {
// 1. Assert: Type(O) is Object.

View File

@ -34,6 +34,7 @@ import {
Reference,
LexicalEnvironment
} from "../environment.js";
import { AbruptCompletion, ThrowCompletion } from "../completions.js";
import {
GetV,
GetThisValue,
@ -42,7 +43,17 @@ import {
RequireObjectCoercible,
HasSomeCompatibleType
} from "./index.js";
import type { BabelNode, BabelNodeVariableDeclaration, BabelNodeIdentifier, BabelNodeRestElement, BabelNodeObjectPattern, BabelNodeArrayPattern, BabelNodeStatement } from "babel-types";
import { IteratorStep, IteratorValue } from "./iterator.js";
import type {
BabelNode,
BabelNodeVariableDeclaration,
BabelNodeIdentifier,
BabelNodeRestElement,
BabelNodeObjectPattern,
BabelNodeArrayPattern,
BabelNodeStatement,
BabelNodeLVal,
} from "babel-types";
// ECMA262 6.2.3
@ -488,6 +499,87 @@ export function BindingInitialization(realm: Realm, node: BabelNode, value: Valu
throw new Error("Unknown node " + node.type);
}
export function IteratorBindingInitialization(realm: Realm, formals: Array<BabelNodeLVal>, iteratorRecord: {$Iterator: ObjectValue, $Done: boolean}, strict: boolean, environment?: LexicalEnvironment) {
for (let param of formals) {
switch (param.type) {
// ECMA262 13.3.3.6
// SingleNameBinding : BindingIdentifier Initializer
case 'Identifier': {
// 1. Let bindingId be StringValue of BindingIdentifier.
let bindingId = param.name;
// 2. Let lhs be ? ResolveBinding(bindingId, environment).
let lhs = ResolveBinding(realm, param.name, strict, environment);
// Initialized later in the algorithm.
let v;
// 3. If iteratorRecord.[[Done]] is false, then
if (iteratorRecord.$Done === false) {
// a. Let next be IteratorStep(iteratorRecord.[[Iterator]]).
let next;
try {
next = IteratorStep(realm, iteratorRecord.$Iterator);
} catch (e) {
// b. If next is an abrupt completion, set iteratorRecord.[[Done]] to true.
if (e instanceof AbruptCompletion) {
iteratorRecord.$Done = true;
}
// c. ReturnIfAbrupt(next).
throw e;
}
// d. If next is false, set iteratorRecord.[[Done]] to true.
if (next === false) {
iteratorRecord.$Done = true;
// Normally this assignment would be done in step 4, but we do it
// here so that Flow knows `v` will always be initialized by step 5.
v = new UndefinedValue(realm);
} else { // e. Else,
// i. Let v be IteratorValue(next).
try {
v = IteratorValue(realm, next);
} catch (e) {
// ii. If v is an abrupt completion, set iteratorRecord.[[Done]] to true.
if (e instanceof AbruptCompletion) {
iteratorRecord.$Done = true;
}
// iii. ReturnIfAbrupt(v).
throw e;
}
}
} else { // 4. If iteratorRecord.[[Done]] is true, let v be undefined.
v = new UndefinedValue(realm);
}
// TODO:
// 5. If Initializer is present and v is undefined, then
// a. Let defaultValue be the result of evaluating Initializer.
// b. Let v be ? GetValue(defaultValue).
// c. If IsAnonymousFunctionDefinition(Initializer) is true, then
// i. Let hasNameProperty be ? HasOwnProperty(v, "name").
// ii. If hasNameProperty is false, perform SetFunctionName(v, bindingId).
// 6. If environment is undefined, return ? PutValue(lhs, v).
if (!environment) {
PutValue(realm, lhs, v);
break;
}
// 7. Return InitializeReferencedBinding(lhs, v).
InitializeReferencedBinding(realm, lhs, v);
break;
}
default:
throw new ThrowCompletion(new StringValue(realm, "only plain identifiers are supported in parameter lists"));
}
}
}
// ECMA262 12.1.5.1
export function InitializeBoundName(realm: Realm, name: string, value: Value, environment: void | LexicalEnvironment) {
// 1. Assert: Type(name) is String.

View File

@ -21,8 +21,10 @@ import { OrdinaryCreateFromConstructor, CreateUnmappedArgumentsObject, CreateMap
import { OrdinaryCallEvaluateBody, OrdinaryCallBindThis, PrepareForOrdinaryCall, Call } from "./call.js";
import { SameValue } from "../methods/abstract.js";
import { Construct } from "../methods/construct.js";
import { IteratorBindingInitialization } from "../methods/environment.js";
import { joinPossiblyNormalCompletionWithAbruptCompletion, composePossiblyNormalCompletions,
BoundNames, ContainsExpression, GetActiveScriptOrModule, UpdateEmpty } from "../methods/index.js";
import { CreateListIterator } from "../methods/iterator.js";
import { PutValue } from "./properties.js";
import traverse from "../traverse.js";
import invariant from "../invariant.js";
@ -291,42 +293,20 @@ export function FunctionDeclarationInstantiation(realm: Realm, func: FunctionVal
}
// 23. Let iteratorRecord be Record {[[Iterator]]: CreateListIterator(argumentsList), [[Done]]: false}.
let iteratorRecord = 0;
let iteratorRecord = {
$Iterator: CreateListIterator(realm, argumentsList),
$Done: false,
};
// 24. If hasDuplicates is true, then
if (hasDuplicates === true) {
// a. Perform ? IteratorBindingInitialization for formals with iteratorRecord and undefined as arguments.
invariant(formals !== undefined);
for (let i = 0; i < formals.length; ++i) {
let param = formals[i];
switch (param.type) {
case "Identifier":
let value = argumentsList[iteratorRecord] || realm.intrinsics.undefined;
++iteratorRecord;
let lhs = ResolveBinding(realm, param.name, strict);
PutValue(realm, lhs, value);
break;
default:
throw new ThrowCompletion(new StringValue(realm, "only plain identifiers are supported in parameter lists"));
}
}
IteratorBindingInitialization(realm, formals, iteratorRecord, strict);
} else { // 25. Else,
// a. Perform ? IteratorBindingInitialization for formals with iteratorRecord and env as arguments.
invariant(formals !== undefined);
for (let i = 0; i < formals.length; ++i) {
let param = formals[i];
switch (param.type) {
case "Identifier":
let value = argumentsList[iteratorRecord] || realm.intrinsics.undefined;
++iteratorRecord;
envRec.InitializeBinding(param.name, value);
break;
default:
throw new ThrowCompletion(new StringValue(realm, "only plain identifiers are supported in parameter lists"));
}
}
IteratorBindingInitialization(realm, formals, iteratorRecord, strict, env);
}

View File

@ -12,7 +12,14 @@
import type { Realm } from "../realm.js";
import type { CallableObjectValue } from "../types.js";
import { ThrowCompletion, AbruptCompletion } from "../completions.js";
import { NumberValue, ObjectValue, UndefinedValue, Value } from "../values/index.js";
import {
NumberValue,
StringValue,
ObjectValue,
UndefinedValue,
NativeFunctionValue,
Value,
} from "../values/index.js";
import {
GetMethod,
Call,
@ -23,6 +30,8 @@ import {
} from "./index.js";
import invariant from "../invariant.js";
import type { IterationKind } from "../types.js";
import { SameValue } from "./abstract.js";
import { CreateMethodProperty, CreateIterResultObject } from "./create.js";
// ECMA262 7.4.1
export function GetIterator(realm: Realm, obj: Value = realm.intrinsics.undefined, method?: Value): ObjectValue {
@ -98,6 +107,87 @@ export function IteratorNext(realm: Realm, iterator: Value, value?: Value): Obje
return result;
}
// ECMA262 7.4.8
export function CreateListIterator(realm: Realm, list: Array<Value>): ObjectValue {
// 1. Let iterator be ObjectCreate(%IteratorPrototype%, « [[IteratorNext]], [[IteratedList]], [[ListIteratorNextIndex]] »).
let iterator = ObjectCreate(realm, realm.intrinsics.IteratorPrototype, {
$IteratorNext: undefined,
$IteratedList: undefined,
$ListIteratorNextIndex: undefined,
});
// 2. Set iterator's [[IteratedList]] internal slot to list.
iterator.$IteratedList = list;
// 3. Set iterator's [[ListIteratorNextIndex]] internal slot to 0.
iterator.$ListIteratorNextIndex = 0;
// 4. Let next be a new built-in function object as defined in ListIterator next (7.4.8.1).
let next = ListIterator_next(realm);
// 5. Set iterator's [[IteratorNext]] internal slot to next.
iterator.$IteratorNext = next;
// 6. Perform CreateMethodProperty(iterator, "next", next).
CreateMethodProperty(realm, iterator, new StringValue(realm, 'next'), next);
// 7. Return iterator.
return iterator;
}
// ECMA262 7.4.8.1
function ListIterator_next(realm: Realm): NativeFunctionValue {
let func = new NativeFunctionValue(realm, undefined, "next", 0, (context: ObjectValue) => {
// 1. Let O be the this value.
let O = context;
// 2. Let f be the active function object.
let f = func;
// 3. If O does not have a [[IteratorNext]] internal slot, throw a TypeError exception.
if (!O.$IteratorNext) {
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "O does not have an [[IteratorNext]] internal slot");
}
// 4. Let next be the value of the [[IteratorNext]] internal slot of O.
let next = O.$IteratorNext;
// 5. If SameValue(f, next) is false, throw a TypeError exception.
if (!SameValue(realm, f, next)) {
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError);
}
// 6. If O does not have an [[IteratedList]] internal slot, throw a TypeError exception.
if (!O.$IteratedList) {
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "O does not have an [[IteratedList]] internal slot");
}
// 7. Let list be the value of the [[IteratedList]] internal slot of O.
let list = O.$IteratedList;
// 8. Let index be the value of the [[ListIteratorNextIndex]] internal slot of O.
// Default to 0 for Flow.
let index = O.$ListIteratorNextIndex || 0;
// 9. Let len be the number of elements of list.
let len = list.length;
// 10. If index ≥ len, then
if (index >= len) {
// a. Return CreateIterResultObject(undefined, true).
return CreateIterResultObject(realm, new UndefinedValue(realm), true);
}
// 11. Set the value of the [[ListIteratorNextIndex]] internal slot of O to index+1.
O.$ListIteratorNextIndex = index + 1;
// 12. Return CreateIterResultObject(list[index], false).
return CreateIterResultObject(realm, list[index], false);
});
return func;
}
// ECMA262 23.1.5.1
export function CreateMapIterator(realm: Realm, map: Value, kind: IterationKind): ObjectValue {
// 1. If Type(map) is not Object, throw a TypeError exception.

View File

@ -100,6 +100,11 @@ export default class ObjectValue extends ConcreteValue {
$Capabilities: void | PromiseCapability;
$RemainingElements: void | { value: number };
// iterator
$IteratedList: void | Array<Value>;
$ListIteratorNextIndex: void | number;
$IteratorNext: void | NativeFunctionValue;
// set
$SetIterationKind: void | IterationKind;
$SetNextIndex: void | number;