Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[New] ES2018+: Add CreateAsyncFromSyncIterator #105

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -367,10 +367,12 @@
2019/ArrayCreate.js spackled linguist-generated=true
2019/ArraySetLength.js spackled linguist-generated=true
2019/ArraySpeciesCreate.js spackled linguist-generated=true
2019/AsyncFromSyncIteratorContinuation.js spackled linguist-generated=true
2019/Call.js spackled linguist-generated=true
2019/CanonicalNumericIndexString.js spackled linguist-generated=true
2019/CompletePropertyDescriptor.js spackled linguist-generated=true
2019/CopyDataProperties.js spackled linguist-generated=true
2019/CreateAsyncFromSyncIterator.js spackled linguist-generated=true
2019/CreateDataProperty.js spackled linguist-generated=true
2019/CreateDataPropertyOrThrow.js spackled linguist-generated=true
2019/CreateHTML.js spackled linguist-generated=true
Expand Down Expand Up @@ -482,4 +484,4 @@
2019/thisBooleanValue.js spackled linguist-generated=true
2019/thisNumberValue.js spackled linguist-generated=true
2019/thisStringValue.js spackled linguist-generated=true
2019/thisSymbolValue.js spackled linguist-generated=true
2019/thisSymbolValue.js spackled linguist-generated=true
37 changes: 37 additions & 0 deletions 2018/AsyncFromSyncIteratorContinuation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
'use strict';

var GetIntrinsic = require('../GetIntrinsic.js');
var callBound = require('../helpers/callBound.js');

var Call = require('./Call.js');
var CreateIterResultObject = require('./CreateIterResultObject.js');
var IteratorComplete = require('./IteratorComplete.js');
var IteratorValue = require('./IteratorValue.js');

var PromiseResolve = require('./PromiseResolve.js');

var $Promise = GetIntrinsic('%Promise%', true);
var PerformPromiseThen = callBound('%Promise.prototype.then%', true);

// https://www.ecma-international.org/ecma-262/9.0/#sec-asyncfromsynciteratorcontinuation

module.exports = function AsyncFromSyncIteratorContinuation(result, promiseCapability) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the spec doesn't have the proper assertions here (see tc39/ecma262#2050) but i think we should write a helper and the runtime check here.

try {
var done = IteratorComplete(result);
var value = IteratorValue(result);
var valueWrapper = PromiseResolve($Promise, value);

PerformPromiseThen(
valueWrapper,
function onFulfilled(unwrappedValue) {
Call(promiseCapability['[[Resolve]]'], undefined, [
CreateIterResultObject(unwrappedValue, done)
]);
},
promiseCapability['[[Reject]]']
);
} catch (error) {
Call(promiseCapability['[[Reject]]'], undefined, [error]);
}
return promiseCapability['[[Promise]]'];
};
183 changes: 183 additions & 0 deletions 2018/CreateAsyncFromSyncIterator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
'use strict';

var internalSlot = require('internal-slot');
var GetIntrinsic = require('../GetIntrinsic.js');

var AsyncFromSyncIteratorContinuation = require('./AsyncFromSyncIteratorContinuation.js');
var Call = require('./Call.js');
var CreateIterResultObject = require('./CreateIterResultObject.js');
var CreateMethodProperty = require('./CreateMethodProperty.js');
var DefinePropertyOrThrow = require('./DefinePropertyOrThrow.js');
var Get = require('./Get.js');
var GetMethod = require('./GetMethod.js');
var IteratorNext = require('./IteratorNext.js');
var OrdinaryObjectCreate = require('./ObjectCreate.js');
var Type = require('./Type.js');

var $asyncIterator = GetIntrinsic('%Symbol.asyncIterator%', true);
var $toStringTag = GetIntrinsic('%Symbol.toStringTag%', true);
var $TypeError = GetIntrinsic('%TypeError%');

// TODO: Use %AsyncIterator.from% once it's in ECMA-262

var AsyncIteratorPrototype
= GetIntrinsic('%AsyncIteratorPrototype%', true)
|| (function () {
var result = {};
if ($toStringTag) {
DefinePropertyOrThrow(result, $toStringTag, {
'[[Writable]]': false,
'[[Enumerable]]': false,
'[[Configurable]]': true,
'[[Value]]': 'AsyncIterator'
});
}
if ($asyncIterator) {
var method = {
'[Symbol.asyncIterator]': function () {
return this;
}
}['[Symbol.asyncIterator]'];
CreateMethodProperty(result, $asyncIterator, method);
}
return result;
}());

var AsyncFromSyncIteratorPrototype
= GetIntrinsic('%AsyncFromSyncIteratorPrototype%', true)
// eslint-disable-next-line max-lines-per-function
|| (function () {
var $Promise = GetIntrinsic('%Promise%', true);
if (!$Promise) {
return;
}

// eslint-disable-next-line no-shadow
var AsyncFromSyncIteratorPrototype = OrdinaryObjectCreate(AsyncIteratorPrototype);
CreateMethodProperty(AsyncFromSyncIteratorPrototype, 'next', function next(value) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Arguably, these function bodies should be wrapped in the callback to new $Promise.

// eslint-disable-next-line no-invalid-this
var O = this;
var promiseCapability = {};
promiseCapability['[[Promise]]'] = new $Promise(function (resolve, reject) {
promiseCapability['[[Resolve]]'] = resolve;
promiseCapability['[[Reject]]'] = reject;
});

if (Type(O) !== 'Object' || !internalSlot.has(O, '[[SyncIteratorRecord]]')) {
Call(promiseCapability['[[Reject]]'], undefined, [
new $TypeError('%AsyncFromSyncIteratorPrototype%.next called on invalid receiver')
]);
return promiseCapability['[[Promise]]'];
}

var result;
try {
var syncIteratorRecord = internalSlot.get(O, '[[SyncIteratorRecord]]');
result = IteratorNext(syncIteratorRecord, value);
} catch (error) {
Call(promiseCapability['[[Reject]]'], undefined, [error]);
return promiseCapability['[[Promise]]'];
}
return AsyncFromSyncIteratorContinuation(result, promiseCapability);
});

CreateMethodProperty(AsyncFromSyncIteratorPrototype, 'return', function (value) {
// eslint-disable-next-line no-invalid-this
var O = this;
var promiseCapability = {};
promiseCapability['[[Promise]]'] = new $Promise(function (resolve, reject) {
promiseCapability['[[Resolve]]'] = resolve;
promiseCapability['[[Reject]]'] = reject;
});

if (Type(O) !== 'Object' || !internalSlot.has(O, '[[SyncIteratorRecord]]')) {
Call(promiseCapability['[[Reject]]'], undefined, [
new $TypeError('%AsyncFromSyncIteratorPrototype%.return called on invalid receiver')
]);
return promiseCapability['[[Promise]]'];
}

var syncIterator = internalSlot.get(O, '[[SyncIteratorRecord]]')['[[SyncIterator]]'];
var result;
try {
var returnMethod = GetMethod(syncIterator, 'return');
if (returnMethod === undefined) {
var iterResult = CreateIterResultObject(value, true);
Call(promiseCapability['[[Resolve]]'], undefined, [iterResult]);
return promiseCapability['[[Promise]]'];
}

result = Call(returnMethod, syncIterator, [value]);
if (Type(result) !== 'Object') {
Call(promiseCapability['[[Reject]]'], undefined, [
new $TypeError('iterator return must return an object')
]);
return promiseCapability['[[Promise]]'];
}
} catch (error) {
Call(promiseCapability['[[Reject]]'], undefined, [error]);
return promiseCapability['[[Promise]]'];
}
return AsyncFromSyncIteratorContinuation(result, promiseCapability);
});

CreateMethodProperty(AsyncFromSyncIteratorPrototype, 'throw', function (value) {
// eslint-disable-next-line no-invalid-this
var O = this;
var promiseCapability = {};
promiseCapability['[[Promise]]'] = new $Promise(function (resolve, reject) {
promiseCapability['[[Resolve]]'] = resolve;
promiseCapability['[[Reject]]'] = reject;
});

if (Type(O) !== 'Object' || !internalSlot.has(O, '[[SyncIteratorRecord]]')) {
Call(promiseCapability['[[Reject]]'], undefined, [
new $TypeError('%AsyncFromSyncIteratorPrototype%.return called on invalid receiver')
]);
return promiseCapability['[[Promise]]'];
}

var syncIterator = internalSlot.get(O, '[[SyncIteratorRecord]]')['[[SyncIterator]]'];
var result;
try {
var throwMethod = GetMethod(syncIterator, 'throw');
if (throwMethod === undefined) {
var iterResult = CreateIterResultObject(value, true);
Call(promiseCapability['[[Reject]]'], undefined, [iterResult]);
return promiseCapability['[[Promise]]'];
}

result = Call(throwMethod, syncIterator, [value]);
if (Type(result) !== 'Object') {
Call(promiseCapability['[[Reject]]'], undefined, [
new $TypeError('iterator throw must return an object')
]);
return promiseCapability['[[Promise]]'];
}
} catch (error) {
Call(promiseCapability['[[Reject]]'], undefined, [error]);
return promiseCapability['[[Promise]]'];
}
return AsyncFromSyncIteratorContinuation(result, promiseCapability);
});

// eslint-disable-next-line consistent-return
return AsyncFromSyncIteratorPrototype;
}());

// https://www.ecma-international.org/ecma-262/9.0/#sec-createasyncfromsynciterator

module.exports = function CreateAsyncFromSyncIterator(syncIteratorRecord) {
if (!AsyncFromSyncIteratorPrototype) {
throw new SyntaxError('This environment does not support Promises.');
}

var asyncIterator = OrdinaryObjectCreate(AsyncFromSyncIteratorPrototype);
internalSlot.set(asyncIterator, '[[SyncIteratorRecord]]', syncIteratorRecord);
var nextMethod = Get(asyncIterator, 'next');
return {
'[[Iterator]]': asyncIterator,
'[[NextMethod]]': nextMethod,
'[[Done]]': false
};
};
37 changes: 37 additions & 0 deletions 2019/AsyncFromSyncIteratorContinuation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
'use strict';

var GetIntrinsic = require('../GetIntrinsic.js');
var callBound = require('../helpers/callBound.js');

var Call = require('./Call.js');
var CreateIterResultObject = require('./CreateIterResultObject.js');
var IteratorComplete = require('./IteratorComplete.js');
var IteratorValue = require('./IteratorValue.js');

var PromiseResolve = require('./PromiseResolve.js');

var $Promise = GetIntrinsic('%Promise%', true);
var PerformPromiseThen = callBound('%Promise.prototype.then%', true);

// https://www.ecma-international.org/ecma-262/9.0/#sec-asyncfromsynciteratorcontinuation

module.exports = function AsyncFromSyncIteratorContinuation(result, promiseCapability) {
try {
var done = IteratorComplete(result);
var value = IteratorValue(result);
var valueWrapper = PromiseResolve($Promise, value);

PerformPromiseThen(
valueWrapper,
function onFulfilled(unwrappedValue) {
Call(promiseCapability['[[Resolve]]'], undefined, [
CreateIterResultObject(unwrappedValue, done)
]);
},
promiseCapability['[[Reject]]']
);
} catch (error) {
Call(promiseCapability['[[Reject]]'], undefined, [error]);
}
return promiseCapability['[[Promise]]'];
};
Loading