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

feat(ses): anticipate iterator helpers #1655

Merged
merged 1 commit into from
Jul 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
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
1 change: 1 addition & 0 deletions packages/ses/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
"ava": "^5.3.0",
"babel-eslint": "^10.0.3",
"c8": "^7.14.0",
"core-js": "^3.31.0",
"eslint": "^8.42.0",
"eslint-config-airbnb-base": "^15.0.0",
"eslint-config-prettier": "^8.8.0",
Expand Down
23 changes: 23 additions & 0 deletions packages/ses/src/get-anonymous-intrinsics.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
matchAllRegExp,
matchAllSymbol,
regexpPrototype,
globalThis,
} from './commons.js';
import { InertCompartment } from './compartment-shim.js';

Expand Down Expand Up @@ -134,5 +135,27 @@ export const getAnonymousIntrinsics = () => {
'%InertCompartment%': InertCompartment,
};

if (globalThis.Iterator) {
intrinsics['%IteratorHelperPrototype%'] = getPrototypeOf(
// eslint-disable-next-line @endo/no-polymorphic-call
globalThis.Iterator.from([]).take(0),
);
intrinsics['%WrapForValidIteratorPrototype%'] = getPrototypeOf(
// eslint-disable-next-line @endo/no-polymorphic-call
globalThis.Iterator.from({ next() {} }),
);
}

if (globalThis.AsyncInterator) {
intrinsics['%AsyncIteratorHelperPrototype%'] = getPrototypeOf(
// eslint-disable-next-line @endo/no-polymorphic-call
globalThis.AsyncIterator.from([]).take(0),
);
intrinsics['%WrapForValidAsyncIteratorPrototype%'] = getPrototypeOf(
// eslint-disable-next-line @endo/no-polymorphic-call
globalThis.AsyncIterator.from({ next() {} }),
);
}

return intrinsics;
};
2 changes: 1 addition & 1 deletion packages/ses/src/permits-intrinsics.js
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ export default function whitelistIntrinsics(
* Visit all properties for a permit.
*/
function visitProperties(path, obj, permit) {
if (obj === undefined) {
if (obj === undefined || obj === null) {
return;
}

Expand Down
82 changes: 81 additions & 1 deletion packages/ses/src/permits.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ export const universalPropertyNames = {
URIError: 'URIError',
WeakMap: 'WeakMap',
WeakSet: 'WeakSet',
// https://github.com/tc39/proposal-iterator-helpers
Iterator: 'Iterator',
// https://github.com/tc39/proposal-async-iterator-helpers
AsyncIterator: 'AsyncIterator',

// *** Other Properties of the Global Object

Expand Down Expand Up @@ -246,7 +250,7 @@ export const FunctionInstance = {
};

// AsyncFunction Instances
const AsyncFunctionInstance = {
export const AsyncFunctionInstance = {
// This property is not mentioned in ECMA 262, but is present in V8 and
// necessary for lockdown to succeed.
'[[Proto]]': '%AsyncFunctionPrototype%',
Expand Down Expand Up @@ -1233,14 +1237,90 @@ export const permitted = {

// *** Control Abstraction Objects

// https://github.com/tc39/proposal-iterator-helpers
Iterator: {
// Properties of the Iterator Constructor
'[[Proto]]': '%FunctionPrototype%',
prototype: '%IteratorPrototype%',
from: fn,
},

'%IteratorPrototype%': {
// The %IteratorPrototype% Object
'@@iterator': fn,
// https://github.com/tc39/proposal-iterator-helpers
constructor: 'Iterator',
map: fn,
filter: fn,
take: fn,
drop: fn,
flatMap: fn,
reduce: fn,
toArray: fn,
forEach: fn,
some: fn,
every: fn,
find: fn,
'@@toStringTag': 'string',
// https://github.com/tc39/proposal-async-iterator-helpers
toAsync: fn,
},

// https://github.com/tc39/proposal-iterator-helpers
'%WrapForValidIteratorPrototype%': {
'[[Proto]]': '%IteratorPrototype%',
next: fn,
return: fn,
},
erights marked this conversation as resolved.
Show resolved Hide resolved

// https://github.com/tc39/proposal-iterator-helpers
'%IteratorHelperPrototype%': {
'[[Proto]]': '%IteratorPrototype%',
next: fn,
return: fn,
'@@toStringTag': 'string',
},

// https://github.com/tc39/proposal-async-iterator-helpers
AsyncIterator: {
// Properties of the Iterator Constructor
'[[Proto]]': '%FunctionPrototype%',
prototype: '%AsyncIteratorPrototype%',
from: fn,
},

'%AsyncIteratorPrototype%': {
// The %AsyncIteratorPrototype% Object
'@@asyncIterator': fn,
// https://github.com/tc39/proposal-async-iterator-helpers
constructor: 'AsyncIterator',
map: fn,
filter: fn,
take: fn,
drop: fn,
flatMap: fn,
reduce: fn,
toArray: fn,
forEach: fn,
some: fn,
every: fn,
find: fn,
'@@toStringTag': 'string',
},

// https://github.com/tc39/proposal-async-iterator-helpers
'%WrapForValidAsyncIteratorPrototype%': {
'[[Proto]]': '%AsyncIteratorPrototype%',
next: fn,
return: fn,
},

// https://github.com/tc39/proposal-async-iterator-helpers
'%AsyncIteratorHelperPrototype%': {
'[[Proto]]': 'Async%IteratorPrototype%',
next: fn,
return: fn,
'@@toStringTag': 'string',
},

'%InertGeneratorFunction%': {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// KLUDGE HAZARD The core-js shims are written as sloppy code
// and so introduce sloppy functions.
erights marked this conversation as resolved.
Show resolved Hide resolved
import 'core-js/actual/async-iterator/index.js';
import test from 'ava';
import '../index.js';

// KLUDGE HAZARD only for testing with the sloppy modules of the
// core-js iterator shim.
// We mutate the permits to tolerates the sloppy functions for testing
// by sacrificing security. The caller and arguments properties of
// sloppy functions violate ocap encapsulation rules.
import { AsyncFunctionInstance } from '../src/permits.js';

AsyncFunctionInstance.arguments = {};
AsyncFunctionInstance.caller = {};
erights marked this conversation as resolved.
Show resolved Hide resolved

// Skipped because the core-js shim seems to miss the
// actual %AsyncIteratorPrototype%,
// so it creates a new one, causing us to fail because lockdown correctly
// detects the conflicting definitions.
// TODO report the bug to core-js
erights marked this conversation as resolved.
Show resolved Hide resolved
test.skip('shimmed async-iterator helpers', t => {
lockdown();

const AsyncIteratorHelperPrototype = Object.getPrototypeOf(
AsyncIterator.from([]).take(0),
);
t.assert(Object.isFrozen(AsyncIteratorHelperPrototype));

const WrapForValidAsyncIteratorPrototype = Object.getPrototypeOf(
AsyncIterator.from({
async next() {
return undefined;
},
}),
);
t.assert(Object.isFrozen(WrapForValidAsyncIteratorPrototype));
});
42 changes: 42 additions & 0 deletions packages/ses/test/test-anticipate-iterator-helpers-shimmed.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// KLUDGE HAZARD The core-js shims are written as sloppy code
// and so introduce sloppy functions.
import 'core-js/actual/iterator/index.js';
import test from 'ava';
import '../index.js';

// KLUDGE HAZARD only for testing with the sloppy modules of the
// core-js iterator shim.
// We mutate the permits to tolerates the sloppy functions for testing
// by sacrificing security. The caller and arguments properties of
// sloppy functions violate ocap encapsulation rules.
Copy link

@zloirock zloirock Jul 6, 2023

Choose a reason for hiding this comment

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

core-js use strict mode, but, for modules size economy, not in all cases - for example, in cases where this depends on it. I didn't think that .arguments and .caller are interesting to anyone. I can consider adding the strict mode in all affected cases. You could open an issue in the core-js repo.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

import { FunctionInstance } from '../src/permits.js';

FunctionInstance.arguments = {};
FunctionInstance.caller = {};

test('shimmed iterator helpers', t => {
lockdown();

t.deepEqual(
(function* g(i) {
// eslint-disable-next-line no-plusplus
while (true) yield i++;
})(1)
.drop(1)
.take(5)
.filter(it => it % 2)
.map(it => it ** 2)
.toArray(),
[9, 25],
);

const IteratorHelperPrototype = Object.getPrototypeOf(
Iterator.from([]).take(0),
);
t.assert(Object.isFrozen(IteratorHelperPrototype));

const WrapForValidIteratorPrototype = Object.getPrototypeOf(
Iterator.from({ next() {} }),
);
t.assert(Object.isFrozen(WrapForValidIteratorPrototype));
});
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4204,6 +4204,11 @@ core-js@^2.4.0:
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec"
integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==

core-js@^3.31.0:
version "3.31.0"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.31.0.tgz#4471dd33e366c79d8c0977ed2d940821719db344"
integrity sha512-NIp2TQSGfR6ba5aalZD+ZQ1fSxGhDo/s1w0nx3RYzf2pnJxt7YynxFlFScP6eV7+GZsKO95NSjGxyJsU3DZgeQ==

[email protected]:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
Expand Down