Delete engine262 project

This commit is contained in:
2020-09-07 18:53:23 +05:30
parent c63d957d0f
commit 8a495eae12
314 changed files with 0 additions and 48143 deletions
-3
View File
@@ -1,3 +0,0 @@
!.eslintrc.js
test/test262/test262
test/json/JSONTestSuite
-81
View File
@@ -1,81 +0,0 @@
'use strict';
const Module = require('module');
const ModuleFindPath = Module._findPath;
const hacks = [
'eslint-plugin-engine262',
];
Module._findPath = (request, paths, isMain) => {
const r = ModuleFindPath(request, paths, isMain);
if (!r && hacks.includes(request)) {
return require.resolve(`./test/${request}`);
}
return r;
};
module.exports = {
root: true,
extends: 'airbnb-base',
plugins: ['engine262'],
parser: '@babel/eslint-parser',
parserOptions: {
ecmaVersion: 2020,
requireConfigFile: false,
},
overrides: [
{
files: ['*.js'],
parserOptions: { sourceType: 'script' },
},
],
globals: {
globalThis: false,
Atomics: false,
BigInt: false,
BigUint64Array: false,
SharedArrayBuffer: false,
},
rules: {
'arrow-parens': ['error', 'always'],
'brace-style': ['error', '1tbs', { allowSingleLine: false }],
'curly': ['error', 'all'],
'engine262/no-use-in-def': 'error',
'engine262/valid-feature': 'error',
'engine262/valid-throw': 'error',
'import/order': ['error', { 'newlines-between': 'never' }],
'import/no-extraneous-dependencies': ['error', { devDependencies: true }],
'no-multiple-empty-lines': ['error', { maxBOF: 0, max: 2 }],
'no-unused-vars': ['error', {
vars: 'all',
varsIgnorePattern: '^_',
args: 'after-used',
argsIgnorePattern: '^_',
}],
'no-empty': ['error', { allowEmptyCatch: true }],
'quote-props': ['error', 'consistent'],
'strict': ['error', 'global'],
'camelcase': 'off',
'class-methods-use-this': 'off',
'global-require': 'off',
'import/extensions': 'off',
'import/no-cycle': 'off',
'import/no-mutable-exports': 'off',
'import/prefer-default-export': 'off',
'lines-between-class-members': 'off',
'max-classes-per-file': 'off',
'max-len': 'off',
'no-bitwise': 'off',
'no-constant-condition': 'off',
'no-continue': 'off',
'no-else-return': 'off',
'no-lonely-if': 'off',
'no-loop-func': 'off',
'no-param-reassign': 'off',
'no-restricted-syntax': 'off',
'no-underscore-dangle': 'off',
'no-use-before-define': 'off',
'prefer-destructuring': 'off',
},
};
-12
View File
@@ -1,12 +0,0 @@
# These are supported funding model platforms
github: [engine262, devsnek] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: # Replace with a single custom sponsorship URL
-55
View File
@@ -1,55 +0,0 @@
name: Publish Package
on:
push:
branches:
- main
jobs:
publish-gpr:
runs-on: ubuntu-latest
steps:
# Set everything up
- uses: actions/checkout@v2
with:
persist-credentials: false
- uses: actions/setup-node@master
with:
node-version: 12
registry-url: https://npm.pkg.github.com/
scope: '@engine262'
always-auth: true # required if using yarn
- run: git submodule update --init --recursive
# Run tests and whatnot
- run: npm install
- run: npm run build
- run: npm run lint
- run: npm run coverage
env:
CONTINUOUS_INTEGRATION: 1
# Upload coverage data
- name: Coveralls
uses: coverallsapp/github-action@master
with:
github-token: ${{github.token}}
# Publish built package
- run: npm publish --access=public
env:
NODE_AUTH_TOKEN: ${{github.token}}
# Push build to to gh-pages
- run: |
git config --global user.email "gha@example.com"
git config --global user.name "GHA"
git remote add github "https://$GITHUB_ACTOR:$BETTER_GITHUB_TOKEN@github.com/$GITHUB_REPOSITORY.git"
git fetch github
git checkout gh-pages
cp dist/* .
git add engine262.*
git commit -m "autobuild" || exit 0 # exit silently if nothing changed
git push -u github gh-pages
env:
BETTER_GITHUB_TOKEN: ${{secrets.BETTER_GITHUB_TOKEN}}
-6
View File
@@ -1,6 +0,0 @@
node_modules
dist
test.mjs
.eslintcache
coverage
.nyc_output
-6
View File
@@ -1,6 +0,0 @@
[submodule "test/JSONTestSuite"]
path = test/json/JSONTestSuite
url = https://github.com/nst/JSONTestSuite
[submodule "test/test262/test262"]
path = test/test262/test262
url = https://github.com/tc39/test262
-9
View File
@@ -1,9 +0,0 @@
test
src
scripts
coverage
rollup.config.js
.eslintcache
.eslintrc.js
.eslintignore
.travis.yml
-1
View File
@@ -1 +0,0 @@
package-lock=false
-46
View File
@@ -1,46 +0,0 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at devsnek@users.noreply.github.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/
-19
View File
@@ -1,19 +0,0 @@
Copyright (c) 2018 engine262 Contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.
-163
View File
@@ -1,163 +0,0 @@
# engine262
An implementation of ECMA-262 in JavaScript
Goals
- 100% Spec Compliance
- Introspection
- Ease of modification
Non-Goals
- Speed at the expense of any of the goals
This project is bound by a [Code of Conduct][COC].
Join us on [#engine262 on freenode][irc] ([web][irc-webchat]).
## Why this exists
While helping develop new features for JavaScript, I've found that one of the
most useful methods of finding what works and what doesn't is being able to
actually run code using the new feature. [Babel][] is fantastic for this, but
sometimes features just can't be nicely represented with it. Similarly,
implementing a feature in one of the engines is a large undertaking, involving
long compile times and annoying bugs with the optimizing compilers.
engine262 is a tool to allow JavaScript developers to have a sandbox where new
features can be quickly prototyped and explored. As an example, adding
[do expressions][] to this engine is as simple as the following diff:
```diff
--- a/src/evaluator.mjs
+++ b/src/evaluator.mjs
@@ -232,6 +232,8 @@ export function* Evaluate(node) {
case 'GeneratorBody':
case 'AsyncGeneratorBody':
return yield* Evaluate_AnyFunctionBody(node);
+ case 'DoExpression':
+ return yield* Evaluate_Block(node.Block);
default:
throw new OutOfRange('Evaluate', node);
}
--- a/src/parser/ExpressionParser.mjs
+++ b/src/parser/ExpressionParser.mjs
@@ -579,6 +579,12 @@ export class ExpressionParser extends FunctionParser {
return this.parseRegularExpressionLiteral();
case Token.LPAREN:
return this.parseParenthesizedExpression();
+ case Token.DO: {
+ const node = this.startNode();
+ this.next();
+ node.Block = this.parseBlock();
+ return this.finishNode(node, 'DoExpression');
+ }
default:
return this.unexpected();
}
```
This simplicity applies to many other proposals, such as [optional chaining][],
[pattern matching][], [the pipeline operator][], and more. This engine has also
been used to find bugs in ECMA-262 and test262, the test suite for
conforming JavaScript implementations.
## Requirements
To run engine262 itself, a engine with support for recent ECMAScript features
is needed. Additionally, the CLI (`bin/engine262.js`) and test262 runner
(`test/test262.js`) require a recent version of Node.js.
## Using engine262
Use it online: https://engine262.js.org
You can install the latest engine262 build from [GitHub Packages][].
If you install it globally, you can use the CLI like so:
`$ engine262`
Or, you can install it locally and use the API:
```js
'use strict';
const {
Agent,
setSurroundingAgent,
ManagedRealm,
Value,
CreateDataProperty,
inspect,
} = require('engine262');
const agent = new Agent({
// onDebugger() {},
// ensureCanCompileStrings() {},
// hasSourceTextAvailable() {},
// onNodeEvaluation() {},
// features: [],
});
setSurroundingAgent(agent);
const realm = new ManagedRealm({
// promiseRejectionTracker() {},
// resolveImportedModule() {},
// getImportMetaProperties() {},
// finalizeImportMeta() {},
// randomSeed() {},
});
realm.scope(() => {
// Add print function from host
const print = new Value((args) => {
console.log(...args.map((tmp) => inspect(tmp)));
return Value.undefined;
});
CreateDataProperty(realm.GlobalObject, new Value('print'), print);
});
realm.evaluateScript(`
'use strict';
async function* numbers() {
let i = 0;
while (true) {
const n = await Promise.resolve(i++);
yield n;
}
}
(async () => {
for await (const item of numbers()) {
print(item);
}
})();
`);
// a stream of numbers fills your console. it fills you with determination.
```
## Related Projects
Many people and organizations have attempted to write a JavaScript interpreter
in JavaScript much like engine262, with different goals. Some of them are
included here for reference, though engine262 is not based on any of them.
- https://github.com/facebook/prepack
- https://github.com/mozilla/narcissus
- https://github.com/NeilFraser/JS-Interpreter
- https://github.com/metaes/metaes
- https://github.com/Siubaak/sval
[Babel]: https://babeljs.io/
[COC]: https://github.com/engine262/engine262/blob/master/CODE_OF_CONDUCT.md
[do expressions]: https://github.com/tc39/proposal-do-expressions
[irc]: ircs://chat.freenode.net:6697/engine262
[irc-webchat]: https://webchat.freenode.net/?channels=engine262
[optional chaining]: https://github.com/tc39/proposal-optional-chaining
[pattern matching]: https://github.com/tc39/proposal-pattern-matching
[the pipeline operator]: https://github.com/tc39/proposal-pipeline-operator
[GitHub Packages]: https://github.com/engine262/engine262/packages
-217
View File
@@ -1,217 +0,0 @@
#!/usr/bin/env node
'use strict';
/* eslint-disable import/order */
try {
require('@snek/source-map-support/register');
} catch {
// empty
}
const repl = require('repl');
const fs = require('fs');
const path = require('path');
const util = require('util');
const packageJson = require('../package.json');
const snekparse = require('./snekparse');
const {
Agent,
setSurroundingAgent,
FEATURES,
inspect,
Value,
CreateDataProperty,
OrdinaryObjectCreate,
Type,
Completion,
AbruptCompletion,
Throw,
} = require('..');
const { createRealm } = require('./test262_realm');
const execArgv = [];
let entry;
const programArgv = [];
{
let target = execArgv;
process.argv.slice(2).forEach((a) => {
if (a.startsWith('--')) {
target.push(a);
} else if (!entry) {
entry = a;
target = programArgv;
} else {
target.push(a);
}
});
}
const help = `
engine262 v${require('../package.json').version}
Usage:
engine262 [options]
engine262 [options] [input file]
engine262 [input file]
Options:
-h, --help Show help (this screen)
-m, --module Evaluate contents of input-file as a module.
Must be followed by input-file
--features=... A comma separated list of features. If no features
are provided, the available features are listed.
`;
const argv = snekparse(execArgv);
if (argv.h || argv.help) {
process.stdout.write(help);
process.exit(0);
} else if (argv.features === true) {
let nameLength = 0;
let flagLength = 0;
FEATURES.forEach((f) => {
if (f.name.length > nameLength) {
nameLength = f.name.length;
}
if (f.flag.length > flagLength) {
flagLength = f.flag.length;
}
});
const log = (n, f, u) => {
process.stdout.write(`${n.padEnd(nameLength, ' ')} ${f.padEnd(flagLength, ' ')} ${u}\n`);
};
log('name', 'flag', 'url');
log('----', '----', '---');
FEATURES.forEach((f) => {
log(f.name, f.flag, f.url);
});
process.exit(0);
}
let features;
if (argv.features === 'all') {
features = FEATURES.map((f) => f.flag);
} else if (argv.features) {
features = argv.features.split(',');
} else {
features = [];
}
const agent = new Agent({ features });
setSurroundingAgent(agent);
const { realm, resolverCache } = createRealm({ printCompatMode: true });
realm.scope(() => {
const console = OrdinaryObjectCreate(realm.Intrinsics['%Object.prototype%']);
CreateDataProperty(realm.GlobalObject, new Value('console'), console);
const format = (args) => args.map((a, i) => {
if (i === 0 && Type(a) === 'String') {
return a.stringValue();
}
return inspect(a);
}).join(' ');
const log = new Value((args) => {
process.stdout.write(`${format(args)}\n`);
return Value.undefined;
});
CreateDataProperty(console, new Value('log'), log);
const error = new Value((args) => {
process.stderr.write(`${format(args)}\n`);
return Value.undefined;
});
CreateDataProperty(console, new Value('error'), error);
const debug = new Value((args) => {
process.stderr.write(`${util.format(...args)}\n`);
return Value.undefined;
});
CreateDataProperty(console, new Value('debug'), debug);
});
if (argv.inspector) {
const inspector = require('../inspector');
inspector.attachRealm(realm);
}
function oneShotEval(source, filename) {
realm.scope(() => {
let result;
if (argv.m || argv.module || filename.endsWith('.mjs')) {
result = realm.createSourceTextModule(filename, source);
if (!(result instanceof AbruptCompletion)) {
const module = result;
resolverCache.set(filename, result);
result = module.Link();
if (!(result instanceof AbruptCompletion)) {
result = module.Evaluate();
}
if (!(result instanceof AbruptCompletion)) {
if (result.PromiseState === 'rejected') {
result = Throw(result.PromiseResult);
}
}
}
} else {
result = realm.evaluateScript(source, { specifier: filename });
}
if (result instanceof AbruptCompletion) {
const inspected = inspect(result);
process.stderr.write(`${inspected}\n`);
process.exit(1);
}
});
}
if (entry) {
const source = fs.readFileSync(entry, 'utf8');
oneShotEval(source, path.resolve(entry));
} else if (!process.stdin.isTTY) {
process.stdin.setEncoding('utf8');
let source = '';
process.stdin.on('data', (data) => {
source += data;
});
process.stdin.once('end', () => {
oneShotEval(source, process.cwd());
});
} else {
process.stdout.write(`${packageJson.name} v${packageJson.version}
Please report bugs to ${packageJson.bugs.url}
`);
repl.start({
prompt: '> ',
eval: (cmd, context, filename, callback) => {
try {
const result = realm.evaluateScript(cmd, { specifier: '(engine262)' });
callback(null, result);
} catch (e) {
callback(e, null);
}
},
preview: false,
completer: () => [],
writer: (o) => realm.scope(() => {
if (o instanceof Value || o instanceof Completion) {
return inspect(o);
}
return util.inspect(o);
}),
});
}
-76
View File
@@ -1,76 +0,0 @@
'use strict';
function snekparse(args) {
if (typeof args === 'string') {
args = args.split(' ');
}
const argv = [];
for (let i = 0; i < args.length; i += 1) {
const arg = args[i];
if (/^--.+=/.test(arg)) {
const match = arg.match(/^--([^=]+)=([\s\S]*)$/);
argv[match[1]] = match[2];
} else if (/^--no-.+/.test(arg)) {
argv[arg.match(/^--no-(.+)/)[1]] = false;
} else if (/^--.+/.test(arg)) {
const key = arg.match(/^--(.+)/)[1];
const next = args[i + 1];
if (typeof next !== 'undefined' && !/^-/.test(next)) {
argv[key] = next;
} else if (/^(true|false)$/.test(next)) {
argv[key] = next === 'true';
} else {
argv[key] = true;
}
} else if (/^-[^-]+/.test(arg)) {
const letters = arg.slice(1, -1).split('');
let broken = false;
for (const j of letters) {
const next = arg.slice(j + 2);
if (next === '-') {
argv[letters[j]] = next;
continue;
}
if (/[A-Za-z]/.test(letters[j]) && /=/.test(next)) {
argv[letters[j]] = next.split('=')[1];
broken = true;
break;
}
if (/[A-Za-z]/.test(letters[j])
&& /-?\d+(\.\d*)?(e-?\d+)?$/.test(next)) {
argv[letters[j]] = next;
broken = true;
break;
}
if (letters[j + 1] && letters[j + 1].match(/\W/)) {
argv[letters[j]] = arg.slice(j + 2);
broken = true;
break;
} else {
argv[letters[j]] = true;
}
}
const key = arg.slice(-1)[0];
if (!broken && key !== '-') {
if (args[i + 1] && !/^(-|--)[^-]/.test(args[i + 1])) {
argv[key] = args[i + 1];
} else if (args[i + 1] && /true|false/.test(args[i + 1])) {
argv[key] = args[i + 1] === 'true';
} else {
argv[key] = true;
}
}
} else {
argv.push(arg);
}
}
return argv;
}
module.exports = snekparse;
-129
View File
@@ -1,129 +0,0 @@
'use strict';
const path = require('path');
const fs = require('fs');
const {
Value,
CreateDataProperty,
DetachArrayBuffer,
OrdinaryObjectCreate,
ToString,
Type,
Throw,
AbruptCompletion,
ManagedRealm,
inspect,
gc,
} = require('..');
const createRealm = ({ printCompatMode = false } = {}) => {
const resolverCache = new Map();
const trackedPromises = new Set();
const realm = new ManagedRealm({
promiseRejectionTracker(promise, operation) {
switch (operation) {
case 'reject':
trackedPromises.add(promise);
break;
case 'handle':
trackedPromises.delete(promise);
break;
/* istanbul ignore next */
default:
throw new RangeError('promiseRejectionTracker', operation);
}
},
resolveImportedModule(referencingScriptOrModule, specifier) {
try {
const base = path.dirname(referencingScriptOrModule.HostDefined.specifier);
const resolved = path.resolve(base, specifier);
if (resolverCache.has(resolved)) {
return resolverCache.get(resolved);
}
const source = fs.readFileSync(resolved, 'utf8');
const m = realm.createSourceTextModule(resolved, source);
resolverCache.set(resolved, m);
return m;
} catch (e) {
return Throw(e.name, 'Raw', e.message);
}
},
});
return realm.scope(() => {
const $262 = OrdinaryObjectCreate(realm.Intrinsics['%Object.prototype%']);
let printHandle;
const setPrintHandle = (f) => {
printHandle = f;
};
CreateDataProperty(realm.GlobalObject, new Value('print'), new Value((args) => {
/* istanbul ignore next */
if (printHandle !== undefined) {
printHandle(...args);
} else {
if (printCompatMode) {
for (let i = 0; i < args.length; i += 1) {
const arg = args[i];
const s = ToString(arg);
if (s instanceof AbruptCompletion) {
return s;
}
process.stdout.write(s.stringValue());
if (i !== args.length - 1) {
process.stdout.write(' ');
}
}
process.stdout.write('\n');
return Value.undefined;
} else {
const formatted = args.map((a, i) => {
if (i === 0 && Type(a) === 'String') {
return a.stringValue();
}
return inspect(a, realm);
}).join(' ');
console.log(formatted); // eslint-disable-line no-console
}
}
return Value.undefined;
}));
[
['global', realm.GlobalObject],
['createRealm', () => {
const info = createRealm();
return info.$262;
}],
['evalScript', ([sourceText]) => realm.evaluateScript(sourceText.stringValue())],
['detachArrayBuffer', ([arrayBuffer]) => DetachArrayBuffer(arrayBuffer)],
['gc', () => {
gc();
return Value.undefined;
}],
['spec', ([v]) => {
if (v.nativeFunction && v.nativeFunction.section) {
return new Value(v.nativeFunction.section);
}
return Value.undefined;
}],
].forEach(([name, value]) => {
const v = value instanceof Value ? value : new Value(value);
CreateDataProperty($262, new Value(name), v);
});
CreateDataProperty(realm.GlobalObject, new Value('$262'), $262);
CreateDataProperty(realm.GlobalObject, new Value('$'), $262);
return {
realm,
$262,
resolverCache,
trackedPromises,
setPrintHandle,
};
});
};
module.exports = { createRealm };
-330
View File
@@ -1,330 +0,0 @@
'use strict';
const engine262 = require('..');
const contexts = [];
class InspectorContext {
constructor(realm) {
this.realm = realm;
this.idToObject = new Map();
this.objectToId = new Map();
this.objectCounter = 0;
this.previewStack = [];
}
internObject(object, group = 'default') {
if (this.objectToId.has(object)) {
return this.objectToId.get(object);
}
const id = `${group}:${this.objectCounter}`;
this.objectCounter += 1;
this.idToObject.set(id, object);
this.objectToId.set(object, id);
return id;
}
releaseObjectGroup(group) {
for (const [id, object] of this.idToObject.entries()) {
if (id.startsWith(group)) {
this.idToObject.delete(id);
this.objectToId.delete(object);
}
}
}
getObject(objectId) {
return this.idToObject.get(objectId);
}
toRemoteObject(object, options) {
const result = {};
switch (engine262.Type(object)) {
case 'Object':
result.objectId = this.internObject(object, options.objectGroup);
if ('Call' in object) {
result.type = 'function';
} else {
result.type = 'object';
if ('PromiseState' in object) {
result.subtype = 'promise';
} else if ('MapData' in object) {
result.subtype = 'map';
} else if ('SetData' in object) {
result.subtype = 'set';
} else if ('ErrorData' in object) {
result.subtype = 'error';
} else if ('TypedArrayName' in object) {
result.subtype = 'typedarray';
} else if ('DataView' in object) {
result.subtype = 'dataview';
} else if ('ProxyTarget' in object) {
result.subtype = 'proxy';
} else if ('DateValue' in object) {
result.subtype = 'date';
} else if ('GeneratorState' in object) {
result.subtype = 'generator';
} else if (engine262.IsArray(object) === engine262.Value.true) {
result.subtype = 'array';
}
}
break;
case 'Null':
result.type = 'object';
result.subtype = 'null';
result.value = null;
break;
case 'Undefined':
result.type = 'undefined';
break;
case 'String':
result.type = 'string';
result.value = object.stringValue();
break;
case 'Number': {
result.type = 'number';
const v = object.numberValue();
if (!Number.isFinite(v)) {
result.unserializableValue = v.toString();
} else {
result.value = v;
}
break;
}
case 'Boolean':
result.type = 'boolean';
result.value = object.booleanValue();
break;
case 'BigInt':
result.type = 'bigint';
result.unserializableValue = `${object.bigintValue().toString()}n`;
break;
case 'Symbol':
result.type = 'symbol';
result.description = object.Description === engine262.Value.undefined
? undefined
: object.Description.stringValue();
break;
default:
throw new RangeError();
}
if (options.generatePreview
&& result.type === 'object'
&& result.subtype !== 'null'
&& !this.previewStack.includes(object)) {
this.previewStack.push(object);
const properties = this.getPropertyPreview(object, options);
let entries;
if ('MapData' in object) {
entries = object.MapData.map((d) => ({
key: this.toRemoteObject(d.Key, options).preview,
value: this.toRemoteObject(d.Value, options).preview,
}));
}
this.previewStack.pop();
result.preview = {
type: result.type,
subtype: result.subtype,
overflow: properties.length > 5,
properties: properties.slice(0, 5),
entries,
};
}
return result;
}
getProperties(object, options) {
const wrap = (v) => this.toRemoteObject(v, options);
const properties = [];
const internalProperties = [];
let p = object;
while (p !== engine262.Value.null) {
const keys = p.OwnPropertyKeys();
if (keys instanceof engine262.AbruptCompletion) {
return keys;
}
for (const key of keys) {
const desc = p.GetOwnProperty(key);
if (desc instanceof engine262.AbruptCompletion) {
return desc;
}
if (options.accessorPropertiesOnly && desc.Value) {
continue;
}
const descriptor = {
name: key.stringValue
? key.stringValue()
: undefined,
value: desc.Value ? wrap(desc.Value) : undefined,
writable: desc.Writable === engine262.Value.true,
get: desc.Get ? wrap(desc.Get) : undefined,
set: desc.Set ? wrap(desc.Set) : undefined,
configurable: desc.Configurable === engine262.Value.true,
enumerable: desc.Enumerable === engine262.Value.true,
wasThrown: false,
isOwn: p === object,
symbol: key.stringValue ? undefined : wrap(key),
};
properties.push(descriptor);
}
if (options.ownProperties) {
break;
}
p = p.GetPrototypeOf();
if (p instanceof engine262.AbruptCompletion) {
return p;
}
}
if ('PromiseState' in object) {
internalProperties.push({
name: '[[PromiseState]]',
value: {
type: 'string',
value: object.PromiseState,
},
});
internalProperties.push({
name: '[[PromiseResult]]',
value: wrap(object.PromiseResult),
});
}
return { properties, internalProperties };
}
getPropertyPreview(object, options) {
const wrap = (v) => this.toRemoteObject(v, options);
const keys = object.OwnPropertyKeys();
if (keys instanceof engine262.AbruptCompletion) {
return keys;
}
const properties = [];
for (const key of keys) {
const desc = object.GetOwnProperty(key);
if (desc instanceof engine262.AbruptCompletion) {
return desc;
}
const descriptor = {
name: key.stringValue
? key.stringValue()
: `Symbol(${key.Description.stringValue ? key.Description.stringValue() : ''})`,
};
if (desc.Value) {
desc.valuePreview = wrap(desc.Value).preview;
switch (engine262.Type(desc.Value)) {
case 'Object':
if ('Call' in desc.Value) {
descriptor.type = 'function';
} else {
descriptor.type = 'object';
if ('PromiseState' in desc.Value) {
descriptor.subtype = 'promise';
} else if ('MapData' in desc.Value) {
descriptor.subtype = 'map';
} else if ('SetData' in desc.Value) {
descriptor.subtype = 'set';
} else if ('ErrorData' in desc.Value) {
descriptor.subtype = 'error';
} else if ('TypedArrayName' in desc.Value) {
descriptor.subtype = 'typedarray';
} else if ('DataView' in desc.Value) {
descriptor.subtype = 'dataview';
} else if ('ProxyTarget' in desc.Value) {
descriptor.subtype = 'proxy';
} else if ('DateValue' in desc.Value) {
descriptor.subtype = 'date';
} else if ('GeneratorState' in desc.Value) {
descriptor.subtype = 'generator';
} else if (engine262.IsArray(desc.Value) === engine262.Value.true) {
descriptor.subtype = 'array';
}
}
break;
case 'Null':
descriptor.type = 'object';
descriptor.subtype = 'null';
descriptor.value = 'null';
break;
case 'Undefined':
descriptor.type = 'undefined';
descriptor.value = 'undefined';
break;
case 'String':
descriptor.type = 'string';
descriptor.value = desc.Value.stringValue();
break;
case 'Number': {
descriptor.type = 'number';
descriptor.value = desc.Value.numberValue().toString();
break;
}
case 'Boolean':
descriptor.type = 'boolean';
descriptor.value = desc.Value.booleanValue().toString();
break;
case 'BigInt':
descriptor.type = 'bigint';
descriptor.value = `${desc.Value.bigintValue().toString()}n`;
break;
case 'Symbol': {
descriptor.type = 'symbol';
const description = desc.Value.Description === engine262.Value.undefined
? ''
: desc.Value.Description.stringValue();
descriptor.value = `Symbol(${description})`;
break;
}
default:
throw new RangeError();
}
} else {
desc.type = 'accessor';
}
properties.push(descriptor);
}
if ('PromiseState' in object) {
properties.push({
name: '[[PromiseState]]',
type: 'string',
value: object.PromiseState,
});
}
return properties;
}
createEvaluationResult(completion, options) {
if (completion instanceof engine262.AbruptCompletion) {
return {
exceptionDetails: {
text: 'uh oh',
lineNumber: 0,
columnNumber: 0,
exception: this.toRemoteObject(completion.Value, options),
},
};
} else {
return {
result: this.toRemoteObject(completion.Value, options),
};
}
}
}
function attachRealm(realm) {
contexts.push(new InspectorContext(realm));
}
function getContext(id) {
return contexts[id] || contexts[0];
}
module.exports = { attachRealm, getContext };
-6
View File
@@ -1,6 +0,0 @@
'use strict';
require('./server');
const { attachRealm } = require('./context');
module.exports = { attachRealm };
File diff suppressed because it is too large Load Diff
-105
View File
@@ -1,105 +0,0 @@
'use strict';
const engine262 = require('..');
const { getContext } = require('./context');
module.exports = {
Debugger: {
enable() {
return { debuggerId: 'debugger.0' };
},
setAsyncCallStackDepth() {},
setBlackboxPatterns() {},
setPauseOnExceptions() {},
},
Profiler: {
enable() {},
},
Runtime: {
enable() {},
compileScript() {
return { scriptId: 'script.0' };
},
callFunctionOn(options) {
const context = getContext(options.executionContextId);
const { Value: F } = context.realm.evaluateScript(`(${options.functionDeclaration})`);
const thisValue = options.objectId
? context.getObject(options.objectId)
: engine262.Value.undefined;
const args = options.arguments.map((a) => {
if ('value' in a) {
return new engine262.Value(context.realm, a.value);
}
if ('objectId' in a) {
return context.getObject(a.objectId);
}
if ('unserializableValue' in a) {
throw new RangeError();
}
return engine262.Value.undefined;
});
const r = engine262.Call(F, thisValue, args);
return context.createEvaluationResult(r, options);
},
evaluate(options) {
if (options.throwOnSideEffect || options.awaitPromise) {
return {
exceptionDetails: {
text: 'unsupported',
lineNumber: 0,
columnNumber: 0,
},
};
}
const context = getContext(options.contextId);
const r = context.realm.evaluateScript(options.expression);
return context.createEvaluationResult(r, options);
},
getHeapUsage() {
return { usedSize: 0, totalSize: 0 };
},
getIsolateId() {
return { id: 'isolate.0' };
},
getProperties(options) {
const context = getContext();
const object = context.getObject(options.objectId);
const properties = context.getProperties(object, options);
if (properties instanceof engine262.AbruptCompletion) {
return context.createEvaluationResult(properties, options);
}
return {
result: properties.properties,
internalProperties: properties.internalProperties,
};
},
globalLexicalScopeNames({ executionContextId }) {
const context = getContext(executionContextId);
const envRec = context.realm.realm.GlobalEnv.EnvironmentRecord;
const names = Map.prototype.keys.call(envRec.DeclarativeRecord.bindings);
return {
names: [...names],
};
},
releaseObjectGroup({ objectGroup }) {
getContext().releaseObjectGroup(objectGroup);
},
runIfWaitingForDebugger(params, ctx) {
ctx.sendEvent('Runtime.executionContextCreated', {
context: {
id: '0',
origin: 'file://',
name: 'context.0',
auxData: {},
},
});
},
},
HeapProfiler: {
enable() {},
collectGarbage() {},
},
};
-87
View File
@@ -1,87 +0,0 @@
'use strict';
const http = require('http');
const WebSocket = require('ws'); // eslint-disable-line import/no-extraneous-dependencies
const packageJson = require('../package.json');
const protocol = require('./js_protocol.json');
const methods = require('./methods');
const server = http.createServer((req, res) => {
if (req.method !== 'GET') {
res.writeHead(405);
res.end();
return;
}
const json = (obj) => {
const s = JSON.stringify(obj);
res.writeHead(200, {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(s),
});
res.end(s);
};
switch (req.url) {
case '/json':
case '/json/list':
json([{
description: `${packageJson.name} instance`,
devtoolsFrontendUrl: 'chrome-devtools://devtools/bundled/js_app.html?experiments=true&v8only=true&ws=localhost:9229/',
devtoolsFrontendUrlCompat: 'chrome-devtools://devtools/bundled/inspector.html?experiments=true&v8only=true&ws=localhost:9229/',
faviconUrl: 'https://avatars0.githubusercontent.com/u/51185628',
id: 'inspector.0',
title: 'engine262',
type: 'node',
url: `file://${process.cwd()}`,
webSocketDebuggerUrl: 'ws://localhost:9229/',
}]);
break;
case '/json/version':
json({
'Browser': `${packageJson.name}/v${packageJson.version}`,
'Protocol-Version': `${protocol.version.major}.${protocol.version.minor}`,
});
break;
case '/json/protocol':
json(protocol);
break;
default:
res.writeHead(404);
res.end();
break;
}
});
const wss = new WebSocket.Server({ server });
wss.on('connection', (ws) => {
const send = (obj) => {
const s = JSON.stringify(obj);
// console.log('<-', s);
ws.send(s);
};
ws._socket.unref();
const context = {
sendEvent(event, params) {
send({ method: event, params });
},
};
ws.on('message', (data) => {
// console.log('->', data);
const { id, method, params } = JSON.parse(data);
const [k, v] = method.split('.');
Promise.resolve(methods[k][v](params, context))
.then((result = {}) => {
send({ id, result });
});
});
});
server.listen(9229, '127.0.0.1', () => {
console.log('Debugger listening at localhost:9229'); // eslint-disable-line no-console
});
server.unref();
-54
View File
@@ -1,54 +0,0 @@
{
"name": "engine262",
"version": "0.0.1",
"description": "Implementation of ECMA-262 in JavaScript",
"author": "engine262 Contributors",
"license": "MIT",
"homepage": "https://github.com/engine262/engine262#readme",
"bugs": {
"url": "https://github.com/engine262/engine262/issues"
},
"main": "dist/engine262",
"scripts": {
"lint": "eslint rollup.config.js test/ src/ bin/ inspector/ scripts/ --cache --ext=js,mjs",
"build": "npm run build:engine",
"build:engine": "rollup -c",
"test": "bash test/test_root.sh",
"test:test262": "node test/test262/test262.js",
"test:supplemental": "node test/supplemental.js",
"test:json": "node test/json/json.js",
"coverage": "nyc --reporter=lcov npm run test",
"prepublishOnly": "node scripts/tag_version_with_git_hash.js",
"postpublish": "git reset --hard HEAD"
},
"bin": {
"engine262": "bin/engine262.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/engine262/engine262.git"
},
"publishConfig": {
"registry": "https://npm.pkg.github.com/@engine262"
},
"dependencies": {},
"devDependencies": {
"@babel/core": "^7.11.1",
"@babel/eslint-parser": "^7.11.3",
"@rollup/plugin-babel": "^5.1.0",
"@rollup/plugin-commonjs": "^14.0.0",
"@rollup/plugin-node-resolve": "^8.4.0",
"@snek/source-map-support": "^1.0.4",
"acorn": "^7.4.0",
"eslint": "^7.2.0",
"eslint-config-airbnb-base": "^14.2.0",
"eslint-plugin-import": "^2.22.0",
"glob": "^7.1.6",
"minimatch": "^3.0.4",
"nyc": "^15.1.0",
"rollup": "^2.23.1",
"test262-stream": "^1.3.0",
"unicode-13.0.0": "^0.8.0",
"ws": "^7.2.3"
}
}
-55
View File
@@ -1,55 +0,0 @@
'use strict';
const fs = require('fs');
const { execSync } = require('child_process');
const { babel } = require('@rollup/plugin-babel');
const commonjs = require('@rollup/plugin-commonjs');
const { nodeResolve } = require('@rollup/plugin-node-resolve');
const { name, version } = require('./package.json');
const hash = execSync('git rev-parse HEAD', { encoding: 'utf8' }).trim();
const banner = `/*!
* engine262 ${version} ${hash}
*
* ${fs.readFileSync('./LICENSE', 'utf8').trim().split('\n').join('\n * ')}
*/
`;
module.exports = () => ({
input: './src/api.mjs',
plugins: [
commonjs(),
nodeResolve(),
babel({
babelHelpers: 'bundled',
exclude: 'node_modules/**',
plugins: [
'./scripts/transform.js',
],
}),
],
output: [
{
file: 'dist/engine262.js',
format: 'umd',
sourcemap: true,
name,
banner,
},
{
file: 'dist/engine262.mjs',
format: 'es',
sourcemap: true,
banner,
},
],
onwarn(warning, warn) {
if (warning.code === 'CIRCULAR_DEPENDENCY') {
// Squelch.
return;
}
process.exitCode = 1;
warn(warning);
},
});
@@ -1,20 +0,0 @@
'use strict';
const { execSync } = require('child_process');
const fs = require('fs');
const pjsonPath = require.resolve('../package.json');
const pjson = JSON.parse(fs.readFileSync(pjsonPath, 'utf8'));
process.stdout.write('Checking package.json for git revision...\n');
if (!pjson.version.includes('-')) {
process.stdout.write('Inserting git revision into package.json...\n');
const hash = execSync('git rev-parse HEAD', { encoding: 'utf8' }).trim();
pjson.version = `${pjson.version}-${hash}`;
fs.writeFileSync(pjsonPath, `${JSON.stringify(pjson, null, 2)}\n`);
}
process.stdout.write('Done!\n');
-260
View File
@@ -1,260 +0,0 @@
'use strict';
const { relative, resolve } = require('path');
const COMPLETION_PATH = resolve('./src/completion.mjs');
const ABSTRACT_OPS_PATH = resolve('./src/abstract-ops/all.mjs');
const VALUE_PATH = resolve('./src/value.mjs');
function fileToImport(file, refPath) {
return relative(file.opts.filename, refPath)
.replace(/\\/g, '/') // Support building on Windows
.replace('../', './');
}
function findParentStatementPath(path) {
while (path && !path.isStatement()) {
path = path.parentPath;
}
return path;
}
function getEnclosingConditionalExpression(path) {
while (path && !path.isStatement()) {
if (path.isConditionalExpression()) {
return path;
}
path = path.parentPath;
}
return null;
}
module.exports = ({ types: t, template }) => {
function createImportCompletion(file) {
const r = fileToImport(file, COMPLETION_PATH);
return template.ast(`
import { Completion } from "${r}";
`);
}
function createImportAbruptCompletion(file) {
const r = fileToImport(file, COMPLETION_PATH);
return template.ast(`
import { AbruptCompletion } from "${r}";
`);
}
function createImportAssert(file) {
const r = fileToImport(file, ABSTRACT_OPS_PATH);
return template.ast(`
import { Assert } from "${r}";
`);
}
function createImportCall(file) {
const r = fileToImport(file, ABSTRACT_OPS_PATH);
return template.ast(`
import { Call } from "${r}";
`);
}
function createImportValue(file) {
const r = fileToImport(file, VALUE_PATH);
return template.ast(`
import { Value } from "${r}";
`);
}
function addSectionFromComments(path) {
if (path.node.leadingComments) {
for (const c of path.node.leadingComments) {
const lines = c.value.split('\n');
for (const line of lines) {
if (/#sec/.test(line)) {
const section = line.split(' ').find((l) => l.includes('#sec'));
const url = section.includes('https') ? section : `https://tc39.es/ecma262/${section}`;
path.insertAfter(template.ast(`${path.node.id ? path.node.id.name : path.node.declarations[0].id.name}.section = '${url}';`));
return;
}
}
}
}
}
const MACROS = {
Q: {
template: template(`
let ID = ARGUMENT;
/* istanbul ignore if */
if (ID instanceof AbruptCompletion) {
return ID;
}
/* istanbul ignore if */
if (ID instanceof Completion) {
ID = ID.Value;
}
`, { preserveComments: true }),
imports: ['AbruptCompletion', 'Completion'],
},
X: {
template: template(`
let ID = ARGUMENT;
Assert(!(ID instanceof AbruptCompletion), SOURCE + ' returned an abrupt completion');
/* istanbul ignore if */
if (ID instanceof Completion) {
ID = ID.Value;
}
`, { preserveComments: true }),
imports: ['Assert', 'Completion', 'AbruptCompletion'],
},
IfAbruptRejectPromise: {
template: template(`
/* istanbul ignore if */
if (ID instanceof AbruptCompletion) {
const hygenicTemp2 = Call(CAPABILITY.Reject, Value.undefined, [ID.Value]);
if (hygenicTemp2 instanceof AbruptCompletion) {
return hygenicTemp2;
}
return CAPABILITY.Promise;
}
/* istanbul ignore if */
if (ID instanceof Completion) {
ID = ID.Value;
}
`, { preserveComments: true }),
imports: ['Call', 'Value', 'AbruptCompletion', 'Completion'],
},
};
MACROS.ReturnIfAbrupt = MACROS.Q;
const MACRO_NAMES = Object.keys(MACROS);
return {
visitor: {
Program: {
enter(path, state) {
state.needed = {};
},
exit(path, state) {
if (state.needed.Completion && !state.file.opts.filename.endsWith('completion.mjs')) {
path.node.body.unshift(createImportCompletion(state.file));
}
if (state.needed.AbruptCompletion && !state.file.opts.filename.endsWith('completion.mjs')) {
path.node.body.unshift(createImportAbruptCompletion(state.file));
}
if (state.needed.Assert) {
path.node.body.unshift(createImportAssert(state.file));
}
if (state.needed.Call) {
path.node.body.unshift(createImportCall(state.file));
}
if (state.needed.Value) {
path.node.body.unshift(createImportValue(state.file));
}
},
},
CallExpression(path, state) {
if (!t.isIdentifier(path.node.callee)) {
return;
}
const macroName = path.node.callee.name;
if (MACRO_NAMES.includes(macroName)) {
const enclosingConditional = getEnclosingConditionalExpression(path);
if (enclosingConditional !== null) {
if (enclosingConditional.parentPath.isVariableDeclarator()) {
const declaration = enclosingConditional.parentPath.parentPath;
const id = enclosingConditional.parentPath.get('id');
declaration.replaceWithMultiple(template.ast(`
let ${id};
if (${enclosingConditional.get('test')}) {
${id} = ${enclosingConditional.get('consequent')}
} else {
${id} = ${enclosingConditional.get('alternate')}
}
`));
return;
} else {
throw path.buildCodeFrameError('Macros may not be used within conditional expressions');
}
}
const macro = MACROS[macroName];
const [argument] = path.node.arguments;
if (macro === MACROS.Q && (t.isReturnStatement(path.parentPath) || path.parentPath.isArrowFunctionExpression())) {
path.replaceWith(path.node.arguments[0]);
return;
}
if (path.parentPath.isArrowFunctionExpression()) {
throw path.buildCodeFrameError('Macros may not be the sole expression of an arrow function');
}
const statementPath = findParentStatementPath(path);
macro.imports.forEach((i) => {
state.needed[i] = path.scope.getBinding(i) === undefined;
});
if (macro === MACROS.Q && t.isIdentifier(argument)) {
const binding = path.scope.getBinding(argument.name);
binding.path.parent.kind = 'let';
statementPath.insertBefore(template(`
/* istanbul ignore if */
if (ID instanceof AbruptCompletion) {
return ID;
}
/* istanbul ignore if */
if (ID instanceof Completion) {
ID = ID.Value;
}
`, { preserveComments: true })({ ID: argument }));
path.replaceWith(argument);
} else {
if (macro === MACROS.IfAbruptRejectPromise) {
const [, capability] = path.node.arguments;
if (!t.isIdentifier(argument)) {
throw path.get('arguments.0').buildCodeFrameError('First argument to IfAbruptRejectPromise should be an identifier');
}
if (!t.isIdentifier(capability)) {
throw path.get('arguments.1').buildCodeFrameError('Second argument to IfAbruptRejectPromise should be an identifier');
}
const binding = path.scope.getBinding(argument.name);
binding.path.parent.kind = 'let';
statementPath.insertBefore(macro.template({ ID: argument, CAPABILITY: capability }));
path.remove();
} else {
const id = statementPath.scope.generateUidIdentifier();
const replacement = {
ARGUMENT: argument,
ID: id,
};
if (macro === MACROS.X) {
replacement.SOURCE = t.stringLiteral(path.get('arguments.0').getSource());
}
statementPath.insertBefore(macro.template(replacement));
path.replaceWith(id);
}
}
} else if (macroName === 'Assert') {
path.node.arguments.push(t.stringLiteral(path.get('arguments.0').getSource()));
}
},
SwitchCase(path) {
const n = path.node.consequent[0];
if (t.isThrowStatement(n) && t.isNewExpression(n.argument) && n.argument.callee.name === 'OutOfRange') {
path.node.leadingComments = path.node.leadingComments || [];
path.node.leadingComments.push({ type: 'CommentBlock', value: 'istanbul ignore next' });
}
},
FunctionDeclaration(path) {
addSectionFromComments(path);
},
VariableDeclaration(path) {
if (path.get('declarations.0.init').isArrowFunctionExpression()) {
addSectionFromComments(path);
}
},
},
};
};
-32
View File
@@ -1,32 +0,0 @@
export * from './arguments-operations.mjs';
export * from './array-objects.mjs';
export * from './arraybuffer-objects.mjs';
export * from './async-function-operations.mjs';
export * from './async-generator-objects.mjs';
export * from './data-types-and-values.mjs';
export * from './dataview-objects.mjs';
export * from './date-objects.mjs';
export * from './execution-contexts.mjs';
export * from './function-operations.mjs';
export * from './generator-operations.mjs';
export * from './global-object.mjs';
export * from './immutable-prototype-objects.mjs';
export * from './integer-indexed-objects.mjs';
export * from './iterator-operations.mjs';
export * from './module-namespace-exotic-objects.mjs';
export * from './module-records.mjs';
export * from './notational-conventions.mjs';
export * from './object-operations.mjs';
export * from './objects.mjs';
export * from './promise-operations.mjs';
export * from './proxy-objects.mjs';
export * from './realms.mjs';
export * from './reference-operations.mjs';
export * from './regexp-objects.mjs';
export * from './spec-types.mjs';
export * from './string-objects.mjs';
export * from './symbol-objects.mjs';
export * from './testing-comparison.mjs';
export * from './type-conversion.mjs';
export * from './typedarray-objects.mjs';
export * from './weak-operations.mjs';
@@ -1,248 +0,0 @@
import { surroundingAgent } from '../engine.mjs';
import {
Descriptor,
Value,
wellKnownSymbols,
} from '../value.mjs';
import { BoundNames } from '../static-semantics/all.mjs';
import { Q, X } from '../completion.mjs';
import { ValueSet } from '../helpers.mjs';
import {
Assert,
CreateBuiltinFunction,
CreateDataProperty,
DefinePropertyOrThrow,
SetFunctionLength,
ToString,
SameValue,
MakeBasicObject,
OrdinaryObjectCreate,
OrdinaryGetOwnProperty,
OrdinaryDefineOwnProperty,
OrdinaryGet,
OrdinarySet,
OrdinaryDelete,
Get,
Set,
HasOwnProperty,
IsAccessorDescriptor,
IsDataDescriptor,
} from './all.mjs';
// This file covers abstract operations defined in
// 9.4.4 #sec-arguments-exotic-objects
function ArgumentsGetOwnProperty(P) {
const args = this;
const desc = OrdinaryGetOwnProperty(args, P);
if (desc === Value.undefined) {
return desc;
}
const map = args.ParameterMap;
const isMapped = X(HasOwnProperty(map, P));
if (isMapped === Value.true) {
desc.Value = Get(map, P);
}
return desc;
}
function ArgumentsDefineOwnProperty(P, Desc) {
const args = this;
const map = args.ParameterMap;
const isMapped = X(HasOwnProperty(map, P));
let newArgDesc = Desc;
if (isMapped === Value.true && IsDataDescriptor(Desc) === true) {
if (Desc.Value === undefined && Desc.Writable !== undefined && Desc.Writable === Value.false) {
newArgDesc = Descriptor({ ...Desc });
newArgDesc.Value = X(Get(map, P));
}
}
const allowed = Q(OrdinaryDefineOwnProperty(args, P, newArgDesc));
if (allowed === Value.false) {
return Value.false;
}
if (isMapped === Value.true) {
if (IsAccessorDescriptor(Desc) === true) {
map.Delete(P);
} else {
if (Desc.Value !== undefined) {
const setStatus = Set(map, P, Desc.Value, Value.false);
Assert(setStatus === Value.true);
}
if (Desc.Writable !== undefined && Desc.Writable === Value.false) {
map.Delete(P);
}
}
}
return Value.true;
}
function ArgumentsGet(P, Receiver) {
const args = this;
const map = args.ParameterMap;
const isMapped = X(HasOwnProperty(map, P));
if (isMapped === Value.false) {
return Q(OrdinaryGet(args, P, Receiver));
} else {
return Get(map, P);
}
}
function ArgumentsSet(P, V, Receiver) {
const args = this;
let isMapped;
let map;
if (SameValue(args, Receiver) === Value.false) {
isMapped = false;
} else {
map = args.ParameterMap;
isMapped = X(HasOwnProperty(map, P)) === Value.true;
}
if (isMapped) {
const setStatus = Set(map, P, V, Value.false);
Assert(setStatus === Value.true);
}
return Q(OrdinarySet(args, P, V, Receiver));
}
function ArgumentsDelete(P) {
const args = this;
const map = args.ParameterMap;
const isMapped = X(HasOwnProperty(map, P));
const result = Q(OrdinaryDelete(args, P));
if (result === Value.true && isMapped === Value.true) {
map.Delete(P);
}
return result;
}
// 9.4.4.6 #sec-createunmappedargumentsobject
export function CreateUnmappedArgumentsObject(argumentsList) {
const len = argumentsList.length;
const obj = OrdinaryObjectCreate(surroundingAgent.intrinsic('%Object.prototype%'), ['ParameterMap']);
obj.ParameterMap = Value.undefined;
DefinePropertyOrThrow(obj, new Value('length'), Descriptor({
Value: new Value(len),
Writable: Value.true,
Enumerable: Value.false,
Configurable: Value.true,
}));
let index = 0;
while (index < len) {
const val = argumentsList[index];
const idxStr = X(ToString(new Value(index)));
X(CreateDataProperty(obj, idxStr, val));
index += 1;
}
X(DefinePropertyOrThrow(obj, wellKnownSymbols.iterator, Descriptor({
Value: surroundingAgent.intrinsic('%Array.prototype.values%'),
Writable: Value.true,
Enumerable: Value.false,
Configurable: Value.true,
})));
X(DefinePropertyOrThrow(obj, new Value('callee'), Descriptor({
Get: surroundingAgent.intrinsic('%ThrowTypeError%'),
Set: surroundingAgent.intrinsic('%ThrowTypeError%'),
Enumerable: Value.false,
Configurable: Value.false,
})));
return obj;
}
function ArgGetterSteps() {
const f = this;
const name = f.Name;
const env = f.Env;
return env.GetBindingValue(name, Value.false);
}
// 9.4.4.7.1 #sec-makearggetter
function MakeArgGetter(name, env) {
const steps = ArgGetterSteps;
const getter = X(CreateBuiltinFunction(steps, ['Name', 'Env']));
getter.Name = name;
getter.Env = env;
return getter;
}
function ArgSetterSteps([value]) {
Assert(value !== undefined);
const f = this;
const name = f.Name;
const env = f.Env;
return env.SetMutableBinding(name, value, Value.false);
}
// 9.4.4.7.2 #sec-makeargsetter
function MakeArgSetter(name, env) {
const steps = ArgSetterSteps;
const setter = X(CreateBuiltinFunction(steps, ['Name', 'Env']));
SetFunctionLength(setter, new Value(1));
setter.Name = name;
setter.Env = env;
return setter;
}
// 9.4.4.7 #sec-createmappedargumentsobject
export function CreateMappedArgumentsObject(func, formals, argumentsList, env) {
// Assert: formals does not contain a rest parameter, any binding
// patterns, or any initializers. It may contain duplicate identifiers.
const len = argumentsList.length;
const obj = X(MakeBasicObject(['Prototype', 'Extensible', 'ParameterMap']));
obj.GetOwnProperty = ArgumentsGetOwnProperty;
obj.DefineOwnProperty = ArgumentsDefineOwnProperty;
obj.Get = ArgumentsGet;
obj.Set = ArgumentsSet;
obj.Delete = ArgumentsDelete;
obj.Prototype = surroundingAgent.intrinsic('%Object.prototype%');
const map = OrdinaryObjectCreate(Value.null);
obj.ParameterMap = map;
const parameterNames = BoundNames(formals);
const numberOfParameters = parameterNames.length;
let index = 0;
while (index < len) {
const val = argumentsList[index];
const idxStr = X(ToString(new Value(index)));
X(CreateDataProperty(obj, idxStr, val));
index += 1;
}
X(DefinePropertyOrThrow(obj, new Value('length'), Descriptor({
Value: new Value(len),
Writable: Value.true,
Enumerable: Value.false,
Configurable: Value.true,
})));
const mappedNames = new ValueSet();
index = numberOfParameters - 1;
while (index >= 0) {
const name = parameterNames[index];
if (!mappedNames.has(name)) {
mappedNames.add(name);
if (index < len) {
const g = MakeArgGetter(name, env);
const p = MakeArgSetter(name, env);
X(map.DefineOwnProperty(X(ToString(new Value(index))), Descriptor({
Set: p,
Get: g,
Enumerable: Value.false,
Configurable: Value.true,
})));
}
}
index -= 1;
}
X(DefinePropertyOrThrow(obj, wellKnownSymbols.iterator, Descriptor({
Value: surroundingAgent.intrinsic('%Array.prototype.values%'),
Writable: Value.true,
Enumerable: Value.false,
Configurable: Value.true,
})));
X(DefinePropertyOrThrow(obj, new Value('callee'), Descriptor({
Value: func,
Writable: Value.true,
Enumerable: Value.false,
Configurable: Value.true,
})));
return obj;
}
@@ -1,250 +0,0 @@
import { surroundingAgent } from '../engine.mjs';
import {
Descriptor,
Type,
Value,
wellKnownSymbols,
} from '../value.mjs';
import { Q, X } from '../completion.mjs';
import {
AbstractRelationalComparison,
Assert,
Call,
Construct,
Get,
GetFunctionRealm,
IsDataDescriptor,
IsArray,
IsConstructor,
OrdinaryDefineOwnProperty,
OrdinaryGetOwnProperty,
OrdinaryObjectCreate,
MakeBasicObject,
SameValue,
ToBoolean,
ToNumber,
ToString,
ToUint32,
IsPropertyKey,
IsNonNegativeInteger,
isArrayIndex,
} from './all.mjs';
// #sec-array-exotic-objects-defineownproperty-p-desc
function ArrayDefineOwnProperty(P, Desc) {
const A = this;
Assert(IsPropertyKey(P));
if (Type(P) === 'String' && P.stringValue() === 'length') {
return Q(ArraySetLength(A, Desc));
} else if (isArrayIndex(P)) {
const oldLenDesc = OrdinaryGetOwnProperty(A, new Value('length'));
Assert(X(IsDataDescriptor(oldLenDesc)));
Assert(oldLenDesc.Configurable === Value.false);
const oldLen = oldLenDesc.Value;
const index = X(ToUint32(P));
if (index.numberValue() >= oldLen.numberValue() && oldLenDesc.Writable === Value.false) {
return Value.false;
}
const succeeded = X(OrdinaryDefineOwnProperty(A, P, Desc));
if (succeeded === Value.false) {
return Value.false;
}
if (index.numberValue() >= oldLen.numberValue()) {
oldLenDesc.Value = new Value(index.numberValue() + 1);
const succeeded = OrdinaryDefineOwnProperty(A, new Value('length'), oldLenDesc); // eslint-disable-line no-shadow
Assert(succeeded === Value.true);
}
return Value.true;
}
return OrdinaryDefineOwnProperty(A, P, Desc);
}
export function isArrayExoticObject(O) {
return O.DefineOwnProperty === ArrayDefineOwnProperty;
}
// 9.4.2.2 #sec-arraycreate
export function ArrayCreate(length, proto) {
Assert(X(IsNonNegativeInteger(length)) === Value.true);
if (Object.is(length.numberValue(), -0)) {
length = new Value(0);
}
if (length.numberValue() > (2 ** 32) - 1) {
return surroundingAgent.Throw('RangeError', 'InvalidArrayLength', length);
}
if (proto === undefined) {
proto = surroundingAgent.intrinsic('%Array.prototype%');
}
const A = X(MakeBasicObject(['Prototype', 'Extensible']));
A.Prototype = proto;
A.DefineOwnProperty = ArrayDefineOwnProperty;
X(OrdinaryDefineOwnProperty(A, new Value('length'), Descriptor({
Value: length,
Writable: Value.true,
Enumerable: Value.false,
Configurable: Value.false,
})));
return A;
}
// 9.4.2.3 #sec-arrayspeciescreate
export function ArraySpeciesCreate(originalArray, length) {
Assert(Type(length) === 'Number' && Number.isInteger(length.numberValue()) && length.numberValue() >= 0);
if (Object.is(length.numberValue(), -0)) {
length = new Value(+0);
}
const isArray = Q(IsArray(originalArray));
if (isArray === Value.false) {
return Q(ArrayCreate(length));
}
let C = Q(Get(originalArray, new Value('constructor')));
if (IsConstructor(C) === Value.true) {
const thisRealm = surroundingAgent.currentRealmRecord;
const realmC = Q(GetFunctionRealm(C));
if (thisRealm !== realmC) {
if (SameValue(C, realmC.Intrinsics['%Array%']) === Value.true) {
C = Value.undefined;
}
}
}
if (Type(C) === 'Object') {
C = Q(Get(C, wellKnownSymbols.species));
if (C === Value.null) {
C = Value.undefined;
}
}
if (C === Value.undefined) {
return Q(ArrayCreate(length));
}
if (IsConstructor(C) === Value.false) {
return surroundingAgent.Throw('TypeError', 'NotAConstructor', C);
}
return Q(Construct(C, [length]));
}
// 9.4.2.4 #sec-arraysetlength
export function ArraySetLength(A, Desc) {
if (Desc.Value === undefined) {
return OrdinaryDefineOwnProperty(A, new Value('length'), Desc);
}
const newLenDesc = Descriptor({ ...Desc });
const newLen = Q(ToUint32(Desc.Value)).numberValue();
const numberLen = Q(ToNumber(Desc.Value)).numberValue();
if (newLen !== numberLen) {
return surroundingAgent.Throw('RangeError', 'InvalidArrayLength', Desc.Value);
}
newLenDesc.Value = new Value(newLen);
const oldLenDesc = OrdinaryGetOwnProperty(A, new Value('length'));
Assert(X(IsDataDescriptor(oldLenDesc)));
Assert(oldLenDesc.Configurable === Value.false);
const oldLen = oldLenDesc.Value.numberValue();
if (newLen >= oldLen) {
return OrdinaryDefineOwnProperty(A, new Value('length'), newLenDesc);
}
if (oldLenDesc.Writable === Value.false) {
return Value.false;
}
let newWritable;
if (newLenDesc.Writable === undefined || newLenDesc.Writable === Value.true) {
newWritable = true;
} else {
newWritable = false;
newLenDesc.Writable = Value.true;
}
const succeeded = X(OrdinaryDefineOwnProperty(A, new Value('length'), newLenDesc));
if (succeeded === Value.false) {
return Value.false;
}
const keys = [];
A.properties.forEach((value, key) => {
if (isArrayIndex(key) && Number(key.stringValue()) >= newLen) {
keys.push(key);
}
});
keys.sort((a, b) => Number(b.stringValue()) - Number(a.stringValue()));
for (const P of keys) {
const deleteSucceeded = X(A.Delete(P));
if (deleteSucceeded === Value.false) {
newLenDesc.Value = new Value(X(ToUint32(P)).numberValue() + 1);
if (newWritable === false) {
newLenDesc.Writable = Value.false;
}
X(OrdinaryDefineOwnProperty(A, new Value('length'), newLenDesc));
return Value.false;
}
}
if (newWritable === false) {
const s = OrdinaryDefineOwnProperty(A, new Value('length'), Descriptor({ Writable: Value.false }));
Assert(s === Value.true);
}
return Value.true;
}
// 22.1.3.1.1 #sec-isconcatspreadable
export function IsConcatSpreadable(O) {
if (Type(O) !== 'Object') {
return Value.false;
}
const spreadable = Q(Get(O, wellKnownSymbols.isConcatSpreadable));
if (spreadable !== Value.undefined) {
return ToBoolean(spreadable);
}
return Q(IsArray(O));
}
// 22.1.3.27.1 #sec-sortcompare
export function SortCompare(x, y, comparefn) {
if (x === Value.undefined && y === Value.undefined) {
return new Value(+0);
}
if (x === Value.undefined) {
return new Value(1);
}
if (y === Value.undefined) {
return new Value(-1);
}
if (comparefn !== Value.undefined) {
const callRes = Q(Call(comparefn, Value.undefined, [x, y]));
const v = Q(ToNumber(callRes));
if (v.isNaN()) {
return new Value(+0);
}
return v;
}
const xString = Q(ToString(x));
const yString = Q(ToString(y));
const xSmaller = AbstractRelationalComparison(xString, yString);
if (xSmaller === Value.true) {
return new Value(-1);
}
const ySmaller = AbstractRelationalComparison(yString, xString);
if (ySmaller === Value.true) {
return new Value(1);
}
return new Value(+0);
}
// 22.1.5.1 #sec-createarrayiterator
export function CreateArrayIterator(array, kind) {
// 1. Assert: Type(array) is Object.
Assert(Type(array) === 'Object');
// 2. Assert: kind is key+value, key, or value.
Assert(kind === 'key+value' || kind === 'key' || kind === 'value');
// 3. Let iterator be ObjectCreate(%ArrayIteratorPrototype%, « [[IteratedArrayLike]], [[ArrayLikeNextIndex]], [[ArrayLikeIterationKind]] »).
const iterator = OrdinaryObjectCreate(surroundingAgent.intrinsic('%ArrayIterator.prototype%'), [
'IteratedArrayLike',
'ArrayLikeNextIndex',
'ArrayLikeIterationKind',
]);
// 4. Set iterator.[[IteratedArrayLike]] to array.
iterator.IteratedArrayLike = array;
// 5. Set iterator.[[ArrayLikeNextIndex]] to 0.
iterator.ArrayLikeNextIndex = 0;
// 6. Set iterator.[[ArrayLikeIterationKind]] to kind.
iterator.ArrayLikeIterationKind = kind;
// 7. Return iterator.
return iterator;
}
@@ -1,213 +0,0 @@
import { surroundingAgent } from '../engine.mjs';
import { Type, Value } from '../value.mjs';
import { Q, X, NormalCompletion } from '../completion.mjs';
import {
Assert, OrdinaryCreateFromConstructor,
IsNonNegativeInteger, CreateByteDataBlock,
SameValue, IsConstructor, CopyDataBlockBytes,
typedArrayInfoByType,
} from './all.mjs';
// #sec-allocatearraybuffer
export function AllocateArrayBuffer(constructor, byteLength) {
// 1. Let obj be ? OrdinaryCreateFromConstructor(constructor, "%ArrayBuffer.prototype%", « [[ArrayBufferData]], [[ArrayBufferByteLength]], [[ArrayBufferDetachKey]] »).
const obj = Q(OrdinaryCreateFromConstructor(constructor, '%ArrayBuffer.prototype%', [
'ArrayBufferData', 'ArrayBufferByteLength', 'ArrayBufferDetachKey',
]));
// 2. Assert: ! IsNonNegativeInteger(byteLength) is true.
Assert(X(IsNonNegativeInteger(byteLength)) === Value.true);
// 3. Let block be ? CreateByteDataBlock(byteLength).
const block = Q(CreateByteDataBlock(byteLength));
// 4. Set obj.[[ArrayBufferData]] to block.
obj.ArrayBufferData = block;
// 5. Set obj.[[ArrayBufferByteLength]] to byteLength.
obj.ArrayBufferByteLength = byteLength;
// 6. Return obj.
return obj;
}
// #sec-isdetachedbuffer
export function IsDetachedBuffer(arrayBuffer) {
// 1. Assert: Type(arrayBuffer) is Object and it has an [[ArrayBufferData]] internal slot.
Assert(Type(arrayBuffer) === 'Object' && 'ArrayBufferData' in arrayBuffer);
// 2. If arrayBuffer.[[ArrayBufferData]] is null, return true.
if (arrayBuffer.ArrayBufferData === Value.null) {
return Value.true;
}
// 3. Return false.
return Value.false;
}
// #sec-detacharraybuffer
export function DetachArrayBuffer(arrayBuffer, key) {
// 1. Assert: Type(arrayBuffer) is Object and it has [[ArrayBufferData]], [[ArrayBufferByteLength]], and [[ArrayBufferDetachKey]] internal slots.
Assert(Type(arrayBuffer) === 'Object'
&& 'ArrayBufferData' in arrayBuffer
&& 'ArrayBufferByteLength' in arrayBuffer
&& 'ArrayBufferDetachKey' in arrayBuffer);
// 2. Assert: IsSharedArrayBuffer(arrayBuffer) is false.
Assert(IsSharedArrayBuffer(arrayBuffer) === Value.false);
// 3. If key is not present, set key to undefined.
if (key === undefined) {
key = Value.undefined;
}
// 4. If SameValue(arrayBuffer.[[ArrayBufferDetachKey]], key) is false, throw a TypeError exception.
if (SameValue(arrayBuffer.ArrayBufferDetachKey, key) === Value.false) {
return surroundingAgent.Throw('TypeError', 'BufferDetachKeyMismatch', key, arrayBuffer);
}
// 5. Set arrayBuffer.[[ArrayBufferData]] to null.
arrayBuffer.ArrayBufferData = Value.null;
// 6. Set arrayBuffer.[[ArrayBufferByteLength]] to 0.
arrayBuffer.ArrayBufferByteLength = new Value(0);
// 7. Return NormalCompletion(null).
return NormalCompletion(Value.null);
}
// #sec-issharedarraybuffer
export function IsSharedArrayBuffer(_obj) {
return Value.false;
}
export function CloneArrayBuffer(srcBuffer, srcByteOffset, srcLength, cloneConstructor) {
// 1. Assert: Type(srcBuffer) is Object and it has an [[ArrayBufferData]] internal slot.
Assert(Type(srcBuffer) === 'Object' && 'ArrayBufferData' in srcBuffer);
// 2. Assert: IsConstructor(cloneConstructor) is true.
Assert(IsConstructor(cloneConstructor) === Value.true);
// 3. Let targetBuffer be ? AllocateArrayBuffer(cloneConstructor, srcLength).
const targetBuffer = Q(AllocateArrayBuffer(cloneConstructor, srcLength));
// 4. If IsDetachedBuffer(srcBuffer) is true, throw a TypeError exception.
if (IsDetachedBuffer(srcBuffer) === Value.true) {
return surroundingAgent.Throw('TypeError', 'ArrayBufferDetached');
}
// 5. Let srcBlock be srcBuffer.[[ArrayBufferData]].
const srcBlock = srcBuffer.ArrayBufferData;
// 6. Let targetBlock be targetBuffer.[[ArrayBufferData]].
const targetBlock = targetBuffer.ArrayBufferData;
// 7. Perform CopyDataBlockBytes(targetBlock, 0, srcBlock, srcByteOffset, srcLength).
CopyDataBlockBytes(targetBlock, 0, srcBlock, srcByteOffset.numberValue(), srcLength.numberValue());
// 8. Return targetBuffer.
return targetBuffer;
}
// #sec-isbigintelementtype
export function IsBigIntElementType(type) {
// 1. If type is BigUint64 or BigInt64, return true.
if (type === 'BigUint64' || type === 'BigInt64') {
return Value.true;
}
// 2. Return false
return Value.false;
}
const throwawayBuffer = new ArrayBuffer(8);
const throwawayDataView = new DataView(throwawayBuffer);
const throwawayArray = new Uint8Array(throwawayBuffer);
// #sec-rawbytestonumeric
export function RawBytesToNumeric(type, rawBytes, isLittleEndian) {
// 1. Let elementSize be the Element Size value specified in Table 61 for Element Type type.
const elementSize = typedArrayInfoByType[type].ElementSize;
Assert(elementSize === rawBytes.length);
const dataViewType = type === 'Uint8C' ? 'Uint8' : type;
Object.assign(throwawayArray, rawBytes);
return new Value(throwawayDataView[`get${dataViewType}`](0, isLittleEndian === Value.true));
}
// #sec-getvaluefrombuffer
export function GetValueFromBuffer(arrayBuffer, byteIndex, type, isTypedArray, order, isLittleEndian) {
// 1. Assert: IsDetachedBuffer(arrayBuffer) is false.
Assert(IsDetachedBuffer(arrayBuffer) === Value.false);
// 2. Assert: There are sufficient bytes in arrayBuffer starting at byteIndex to represent a value of type.
// 3. Assert: ! IsNonNegativeInteger(byteIndex) is true.
Assert(X(IsNonNegativeInteger(byteIndex)) === Value.true);
// 4. Let block be arrayBuffer.[[ArrayBufferData]].
const block = arrayBuffer.ArrayBufferData;
// 5. Let elementSize be the Element Size value specified in Table 61 for Element Type type.
const elementSize = typedArrayInfoByType[type].ElementSize;
// 6. If IsSharedArrayBuffer(arrayBuffer) is true, then
if (IsSharedArrayBuffer(arrayBuffer) === Value.true) {
Assert(false);
}
// 7. Else, let rawValue be a List of elementSize containing, in order, the elementSize sequence of bytes starting with block[byteIndex].
const rawValue = [...block.subarray(byteIndex.numberValue(), byteIndex.numberValue() + elementSize)];
// 8. If isLittleEndian is not present, set isLittleEndian to the value of the [[LittleEndian]] field of the surrounding agent's Agent Record.
if (isLittleEndian === undefined) {
isLittleEndian = surroundingAgent.AgentRecord.LittleEndian;
}
// 9. Return RawBytesToNumeric(type, rawValue, isLittleEndian).
return RawBytesToNumeric(type, rawValue, isLittleEndian);
}
const float32NaNLE = Object.freeze([0, 0, 192, 127]);
const float32NaNBE = Object.freeze([127, 192, 0, 0]);
const float64NaNLE = Object.freeze([0, 0, 0, 0, 0, 0, 248, 127]);
const float64NaNBE = Object.freeze([127, 248, 0, 0, 0, 0, 0, 0]);
// #sec-numerictorawbytes
export function NumericToRawBytes(type, value, isLittleEndian) {
Assert(Type(isLittleEndian) === 'Boolean');
isLittleEndian = isLittleEndian === Value.true;
let rawBytes;
// One day, we will write our own IEEE 754 and two's complement encoder…
if (type === 'Float32') {
if (Number.isNaN(value.numberValue())) {
rawBytes = isLittleEndian ? [...float32NaNLE] : [...float32NaNBE];
} else {
throwawayDataView.setFloat32(0, value.numberValue(), isLittleEndian);
rawBytes = [...throwawayArray.subarray(0, 4)];
}
} else if (type === 'Float64') {
if (Number.isNaN(value.numberValue())) {
rawBytes = isLittleEndian ? [...float64NaNLE] : [...float64NaNBE];
} else {
throwawayDataView.setFloat64(0, value.numberValue(), isLittleEndian);
rawBytes = [...throwawayArray.subarray(0, 8)];
}
} else {
// a. Let n be the Element Size value specified in Table 61 for Element Type type.
const n = typedArrayInfoByType[type].ElementSize;
// b. Let convOp be the abstract operation named in the Conversion Operation column in Table 61 for Element Type type.
const convOp = typedArrayInfoByType[type].ConversionOperation;
// c. Let intValue be convOp(value) treated as a mathematical value, whether the result is a BigInt or Number.
const intValue = X(convOp(value));
const dataViewType = type === 'Uint8C' ? 'Uint8' : type;
throwawayDataView[`set${dataViewType}`](0, intValue.bigintValue ? intValue.bigintValue() : intValue.numberValue(), isLittleEndian);
rawBytes = [...throwawayArray.subarray(0, n)];
}
return rawBytes;
}
// #sec-setvalueinbuffer
export function SetValueInBuffer(arrayBuffer, byteIndex, type, value, isTypedArray, order, isLittleEndian) {
// 1. Assert: IsDetachedBuffer(arrayBuffer) is false.
Assert(IsDetachedBuffer(arrayBuffer) === Value.false);
// 2. Assert: There are sufficient bytes in arrayBuffer starting at byteIndex to represent a value of type.
// 3. Assert: ! IsNonNegativeInteger(byteIndex) is true.
Assert(X(IsNonNegativeInteger(byteIndex)) === Value.true);
// 4. Assert: Type(value) is BigInt if ! IsBigIntElementType(type) is true; otherwise, Type(value) is Number.
if (X(IsBigIntElementType(type)) === Value.true) {
Assert(Type(value) === 'BigInt');
} else {
Assert(Type(value) === 'Number');
}
// 5. Let block be arrayBuffer.[[ArrayBufferData]].
const block = arrayBuffer.ArrayBufferData;
// 6. Let elementSize be the Element Size value specified in Table 61 for Element Type type.
// const elementSize = typedArrayInfo[type].ElementSize;
// 7. If isLittleEndian is not present, set isLittleEndian to the value of the [[LittleEndian]] field of the surrounding agent's Agent Record.
if (isLittleEndian === undefined) {
isLittleEndian = surroundingAgent.AgentRecord.LittleEndian;
}
// 8. Let rawBytes be NumericToRawBytes(type, value, isLittleEndian).
const rawBytes = NumericToRawBytes(type, value, isLittleEndian);
// 9. If IsSharedArrayBuffer(arrayBuffer) is true, then
if (IsSharedArrayBuffer(arrayBuffer) === Value.true) {
Assert(false);
}
// 10. Else, store the individual bytes of rawBytes into block, in order, starting at block[byteIndex].
rawBytes.forEach((byte, i) => {
block[byteIndex.numberValue() + i] = byte;
});
// 11. Return NormalCompletion(undefined).
return NormalCompletion(Value.undefined);
}
@@ -1,42 +0,0 @@
import { EnsureCompletion, X } from '../completion.mjs';
import { surroundingAgent } from '../engine.mjs';
import { Evaluate } from '../evaluator.mjs';
import { Value } from '../value.mjs';
import { resume } from '../helpers.mjs';
import { Assert, Call } from './all.mjs';
// This file covers abstract operations defined in
// 25.7 #sec-async-function-objects
// https://tc39.es/proposal-top-level-await/#sec-asyncblockstart
export function AsyncBlockStart(promiseCapability, asyncBody, asyncContext) {
asyncContext.promiseCapability = promiseCapability;
const runningContext = surroundingAgent.runningExecutionContext;
asyncContext.codeEvaluationState = (function* resumer() {
const result = EnsureCompletion(yield* Evaluate(asyncBody));
// Assert: If we return here, the async function either threw an exception or performed an implicit or explicit return; all awaiting is done.
surroundingAgent.executionContextStack.pop(asyncContext);
if (result.Type === 'normal') {
X(Call(promiseCapability.Resolve, Value.undefined, [Value.undefined]));
} else if (result.Type === 'return') {
X(Call(promiseCapability.Resolve, Value.undefined, [result.Value]));
} else {
Assert(result.Type === 'throw');
X(Call(promiseCapability.Reject, Value.undefined, [result.Value]));
}
return Value.undefined;
}());
surroundingAgent.executionContextStack.push(asyncContext);
const result = EnsureCompletion(resume(asyncContext, undefined));
Assert(surroundingAgent.runningExecutionContext === runningContext);
Assert(result.Type === 'normal' && result.Value === Value.undefined);
return Value.undefined;
}
// 25.7.5.1 #sec-async-functions-abstract-operations-async-function-start
export function AsyncFunctionStart(promiseCapability, asyncFunctionBody) {
const runningContext = surroundingAgent.runningExecutionContext;
const asyncContext = runningContext.copy();
X(AsyncBlockStart(promiseCapability, asyncFunctionBody, asyncContext));
}
@@ -1,211 +0,0 @@
import { surroundingAgent } from '../engine.mjs';
import {
Q, X,
Await,
Completion,
EnsureCompletion,
NormalCompletion,
AbruptCompletion,
} from '../completion.mjs';
import { Evaluate } from '../evaluator.mjs';
import { Value, Type } from '../value.mjs';
import { resume, handleInResume } from '../helpers.mjs';
import {
Assert,
Call,
CreateBuiltinFunction,
CreateIterResultObject,
GetGeneratorKind,
NewPromiseCapability,
PerformPromiseThen,
PromiseResolve,
} from './all.mjs';
// This file covers abstract operations defined in
// 25.5 #sec-asyncgenerator-objects
// 25.5.3.1 #sec-asyncgeneratorrequest-records
class AsyncGeneratorRequestRecord {
constructor(completion, promiseCapability) {
this.Completion = completion;
this.Capability = promiseCapability;
}
}
// 25.5.3.2 #sec-asyncgeneratorstart
export function AsyncGeneratorStart(generator, generatorBody) {
// Assert: generator is an AsyncGenerator instance.
Assert(generator.AsyncGeneratorState === Value.undefined);
const genContext = surroundingAgent.runningExecutionContext;
genContext.Generator = generator;
genContext.codeEvaluationState = (function* resumer() {
const result = EnsureCompletion(yield* Evaluate(generatorBody));
// Assert: If we return here, the async generator either threw an exception or performed either an implicit or explicit return.
surroundingAgent.executionContextStack.pop(genContext);
generator.AsyncGeneratorState = 'completed';
let resultValue;
if (result instanceof NormalCompletion) {
resultValue = Value.undefined;
} else {
resultValue = result.Value;
if (result.Type !== 'return') {
return X(AsyncGeneratorReject(generator, resultValue));
}
}
return X(AsyncGeneratorResolve(generator, resultValue, Value.true));
}());
generator.AsyncGeneratorContext = genContext;
generator.AsyncGeneratorState = 'suspendedStart';
generator.AsyncGeneratorQueue = [];
return Value.undefined;
}
// 25.5.3.3 #sec-asyncgeneratorresolve
function AsyncGeneratorResolve(generator, value, done) {
// Assert: generator is an AsyncGenerator instance.
const queue = generator.AsyncGeneratorQueue;
Assert(queue.length > 0);
const next = queue.shift();
const promiseCapability = next.Capability;
const iteratorResult = X(CreateIterResultObject(value, done));
X(Call(promiseCapability.Resolve, Value.undefined, [iteratorResult]));
X(AsyncGeneratorResumeNext(generator));
return Value.undefined;
}
// 25.5.3.4 #sec-asyncgeneratorreject
function AsyncGeneratorReject(generator, exception) {
// Assert: generator is an AsyncGenerator instance.
const queue = generator.AsyncGeneratorQueue;
Assert(queue.length > 0);
const next = queue.shift();
const promiseCapability = next.Capability;
X(Call(promiseCapability.Reject, Value.undefined, [exception]));
X(AsyncGeneratorResumeNext(generator));
return Value.undefined;
}
// 25.5.3.5.1 #async-generator-resume-next-return-processor-fulfilled
function AsyncGeneratorResumeNextReturnProcessorFulfilledFunctions([value = Value.undefined]) {
const F = surroundingAgent.activeFunctionObject;
F.Generator.AsyncGeneratorState = 'completed';
return X(AsyncGeneratorResolve(F.Generator, value, Value.true));
}
// 25.5.3.5.2 #async-generator-resume-next-return-processor-rejected
function AsyncGeneratorResumeNextReturnProcessorRejectedFunctions([reason = Value.undefined]) {
const F = surroundingAgent.activeFunctionObject;
F.Generator.AsyncGeneratorState = 'completed';
return X(AsyncGeneratorReject(F.Generator, reason));
}
// 25.5.3.5 #sec-asyncgeneratorresumenext
function AsyncGeneratorResumeNext(generator) {
// Assert: generator is an AsyncGenerator instance.
let state = generator.AsyncGeneratorState;
Assert(state !== 'executing');
if (state === 'awaiting-return') {
return Value.undefined;
}
const queue = generator.AsyncGeneratorQueue;
if (queue.length === 0) {
return Value.undefined;
}
const next = queue[0];
Assert(next instanceof AsyncGeneratorRequestRecord);
const completion = next.Completion;
if (completion instanceof AbruptCompletion) {
if (state === 'suspendedStart') {
generator.AsyncGeneratorState = 'completed';
state = 'completed';
}
if (state === 'completed') {
if (completion.Type === 'return') {
generator.AsyncGeneratorState = 'awaiting-return';
const promise = Q(PromiseResolve(surroundingAgent.intrinsic('%Promise%'), completion.Value));
const stepsFulfilled = AsyncGeneratorResumeNextReturnProcessorFulfilledFunctions;
const onFulfilled = X(CreateBuiltinFunction(stepsFulfilled, ['Generator']));
onFulfilled.Generator = generator;
const stepsRejected = AsyncGeneratorResumeNextReturnProcessorRejectedFunctions;
const onRejected = X(CreateBuiltinFunction(stepsRejected, ['Generator']));
onRejected.Generator = generator;
X(PerformPromiseThen(promise, onFulfilled, onRejected));
return Value.undefined;
} else {
Assert(completion.Type === 'throw');
X(AsyncGeneratorReject(generator, completion.Value));
return Value.undefined;
}
}
} else if (state === 'completed') {
return X(AsyncGeneratorResolve(generator, Value.undefined, Value.true));
}
Assert(state === 'suspendedStart' || state === 'suspendedYield');
const genContext = generator.AsyncGeneratorContext;
const callerContext = surroundingAgent.runningExecutionContext;
// Suspend callerContext
generator.AsyncGeneratorState = 'executing';
surroundingAgent.executionContextStack.push(genContext);
const result = resume(genContext, completion);
Assert(!(result instanceof AbruptCompletion));
Assert(surroundingAgent.runningExecutionContext === callerContext);
return Value.undefined;
}
// 25.5.3.6 #sec-asyncgeneratorenqueue
export function AsyncGeneratorEnqueue(generator, completion) {
Assert(completion instanceof Completion);
const promiseCapability = X(NewPromiseCapability(surroundingAgent.intrinsic('%Promise%')));
if (Type(generator) !== 'Object' || !('AsyncGeneratorState' in generator)) {
const badGeneratorError = surroundingAgent.Throw('TypeError', 'NotATypeObject', 'AsyncGenerator', generator).Value;
X(Call(promiseCapability.Reject, Value.undefined, [badGeneratorError]));
return promiseCapability.Promise;
}
const queue = generator.AsyncGeneratorQueue;
const request = new AsyncGeneratorRequestRecord(completion, promiseCapability);
queue.push(request);
const state = generator.AsyncGeneratorState;
if (state !== 'executing') {
X(AsyncGeneratorResumeNext(generator));
}
return promiseCapability.Promise;
}
// #sec-asyncgeneratoryield
export function* AsyncGeneratorYield(value) {
// 1. Let genContext be the running execution context.
const genContext = surroundingAgent.runningExecutionContext;
// 2. Assert: genContext is the execution context of a generator.
Assert(genContext.Generator !== Value.undefined);
// 3. Let generator be the value of the Generator component of genContext.
const generator = genContext.Generator;
// 4. Assert: GetGeneratorKind() is async.
Assert(GetGeneratorKind() === 'async');
// 5. Set value to ? Await(value).
value = Q(yield* Await(value));
// 6. Set generator.[[AsyncGeneratorState]] to suspendedYield.
generator.AsyncGeneratorState = 'suspendedYield';
// 7. Remove genContext from the execution context stack and restore the execution context that is at the top of the execution context stack as the running execution context.
surroundingAgent.executionContextStack.pop(genContext);
// 8. Set the code evaluation state of genContext such that when evaluation is resumed with a Completion resumptionValue the following steps will be performed:
const resumptionValue = EnsureCompletion(yield handleInResume(AsyncGeneratorResolve, generator, value, Value.false));
// a. If resumptionValue.[[Type]] is not return, return Completion(resumptionValue).
if (resumptionValue.Type !== 'return') {
return Completion(resumptionValue);
}
// b. Let awaited be Await(resumptionValue.[[Value]]).
const awaited = EnsureCompletion(yield* Await(resumptionValue.Value));
// c. If awaited.[[Type]] is throw, return Completion(awaited).
if (awaited.Type === 'Throw') {
return Completion(awaited);
}
// d. Assert: awaited.[[Type]] is normal.
Assert(awaited.Type === 'normal');
// e. Return Completion { [[Type]]: return, [[Value]]: awaited.[[Value]], [[Target]]: empty }.
return new Completion({ Type: 'return', Value: awaited.Value, Target: undefined });
// f. NOTE: When one of the above steps returns, it returns to the evaluation of the YieldExpression production that originally called this abstract operation.
// 9. Return ! AsyncGeneratorResolve(generator, value, false).
// 10. NOTE: This returns to the evaluation of the operation that had most previously resumed evaluation of genContext.
}
@@ -1,39 +0,0 @@
import { Type, Value } from '../value.mjs';
import { X } from '../completion.mjs';
import { CanonicalNumericIndexString } from './all.mjs';
// This file covers predicates defined in
// 6 #sec-ecmascript-data-types-and-values
// 6.1.7 #integer-index
export function isIntegerIndex(V) {
if (Type(V) !== 'String') {
return false;
}
const numeric = X(CanonicalNumericIndexString(V));
if (numeric === Value.undefined) {
return false;
}
if (Object.is(numeric.numberValue(), +0)) {
return true;
}
return numeric.numberValue() > 0 && Number.isSafeInteger(numeric.numberValue());
}
// 6.1.7 #array-index
export function isArrayIndex(V) {
if (Type(V) !== 'String') {
return false;
}
const numeric = X(CanonicalNumericIndexString(V));
if (numeric === Value.undefined) {
return false;
}
if (!Number.isInteger(numeric.numberValue())) {
return false;
}
if (Object.is(numeric.numberValue(), +0)) {
return true;
}
return numeric.numberValue() > 0 && numeric.numberValue() < (2 ** 32) - 1;
}
@@ -1,91 +0,0 @@
import { Q, X } from '../completion.mjs';
import { surroundingAgent } from '../engine.mjs';
import { Value } from '../value.mjs';
import {
Assert,
GetValueFromBuffer,
IsDetachedBuffer,
IsBigIntElementType,
SetValueInBuffer,
ToBoolean,
ToIndex,
ToNumber,
ToBigInt,
RequireInternalSlot,
typedArrayInfoByType,
} from './all.mjs';
// This file covers abstract operations defined in
// 24.3 #sec-dataview-objects
// 24.3.1.1 #sec-getviewvalue
export function GetViewValue(view, requestIndex, isLittleEndian, type) {
// 1. Perform ? RequireInternalSlot(view, [[DataView]]).
Q(RequireInternalSlot(view, 'DataView'));
// 2. Assert: view has a [[ViewedArrayBuffer]] internal slot.
Assert('ViewedArrayBuffer' in view);
// 3. Let getIndex be ? ToIndex(requestIndex).
const getIndex = Q(ToIndex(requestIndex)).numberValue();
// 4. Set isLittleEndian to ! ToBoolean(isLittleEndian).
isLittleEndian = X(ToBoolean(isLittleEndian));
// 5. Let buffer be view.[[ViewedArrayBuffer]].
const buffer = view.ViewedArrayBuffer;
// 6. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
if (IsDetachedBuffer(buffer) === Value.true) {
return surroundingAgent.Throw('TypeError', 'ArrayBufferDetached');
}
// 7. Let viewOffset be view.[[ByteOffset]].
const viewOffset = view.ByteOffset.numberValue();
// 8. Let viewSize be view.[[ByteLength]].
const viewSize = view.ByteLength.numberValue();
// 9. Let elementSize be the Element Size value specified in Table 61 for Element Type type.
const elementSize = typedArrayInfoByType[type].ElementSize;
// 10. If getIndex + elementSize > viewSize, throw a RangeError exception.
if (getIndex + elementSize > viewSize) {
return surroundingAgent.Throw('RangeError', 'DataViewOOB');
}
// 11. Let bufferIndex be getIndex + viewOffset.
const bufferIndex = new Value(getIndex + viewOffset);
// 12. Return GetValueFromBuffer(buffer, bufferIndex, type, false, Unordered, isLittleEndian).
return GetValueFromBuffer(buffer, bufferIndex, type, Value.false, 'Unordered', isLittleEndian);
}
// 24.3.1.2 #sec-setviewvalue
export function SetViewValue(view, requestIndex, isLittleEndian, type, value) {
// 1. Perform ? RequireInternalSlot(view, [[DataView]]).
Q(RequireInternalSlot(view, 'DataView'));
// 2. Assert: view has a [[ViewedArrayBuffer]] internal slot.
Assert('ViewedArrayBuffer' in view);
// 3. Let getIndex be ? ToIndex(requestIndex).
const getIndex = Q(ToIndex(requestIndex)).numberValue();
// 4. If ! IsBigIntElementType(type) is true, let numberValue be ? ToBigInt(value).
// 5. Otherwise, let numberValue be ? ToNumber(value).
let numberValue;
if (X(IsBigIntElementType(type)) === Value.true) {
numberValue = Q(ToBigInt(value));
} else {
numberValue = Q(ToNumber(value));
}
// 6. Set isLittleEndian to ! ToBoolean(isLittleEndian).
isLittleEndian = X(ToBoolean(isLittleEndian));
// 7. Let buffer be view.[[ViewedArrayBuffer]].
const buffer = view.ViewedArrayBuffer;
// 8. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
if (IsDetachedBuffer(buffer) === Value.true) {
return surroundingAgent.Throw('TypeError', 'ArrayBufferDetached');
}
// 9. Let viewOffset be view.[[ByteOffset]].
const viewOffset = view.ByteOffset.numberValue();
// 10. Let viewSize be view.[[ByteLength]].
const viewSize = view.ByteLength.numberValue();
// 11. Let elementSize be the Element Size value specified in Table 61 for Element Type type.
const elementSize = typedArrayInfoByType[type].ElementSize;
// 12. If getIndex + elementSize > viewSize, throw a RangeError exception.
if (getIndex + elementSize > viewSize) {
return surroundingAgent.Throw('RangeError', 'DataViewOOB');
}
// 13. Let bufferIndex be getIndex + viewOffset.
const bufferIndex = new Value(getIndex + viewOffset);
// 14. Return SetValueInBuffer(buffer, bufferIndex, type, numberValue, false, Unordered, isLittleEndian).
return SetValueInBuffer(buffer, bufferIndex, type, numberValue, Value.false, 'Unordered', isLittleEndian);
}
-233
View File
@@ -1,233 +0,0 @@
// This file covers abstract operations defined in
// 20.3 #sec-date-objects
import {
Value,
} from '../value.mjs';
import { X } from '../completion.mjs';
import {
ToInteger,
} from './all.mjs';
const mod = (n, m) => {
const r = n % m;
return Math.floor(r >= 0 ? r : r + m);
};
export const HoursPerDay = 24;
export const MinutesPerHour = 60;
export const SecondsPerMinute = 60;
export const msPerSecond = 1000;
export const msPerMinute = msPerSecond * SecondsPerMinute;
export const msPerHour = msPerMinute * MinutesPerHour;
export const msPerDay = msPerHour * HoursPerDay;
// 20.3.1.2 #sec-day-number-and-time-within-day
export function Day(t) {
return new Value(Math.floor(t.numberValue() / msPerDay));
}
export function TimeWithinDay(t) {
return new Value(mod(t.numberValue(), msPerDay));
}
// 20.3.1.3 #sec-year-number
export function DaysInYear(y) {
y = y.numberValue();
if (mod(y, 4) !== 0) {
return new Value(365);
}
if (mod(y, 4) === 0 && mod(y, 100) !== 0) {
return new Value(366);
}
if (mod(y, 100) === 0 && mod(y, 400) !== 0) {
return new Value(365);
}
if (mod(y, 400) === 0) {
return new Value(366);
}
}
export function DayFromYear(y) {
y = y.numberValue();
return new Value(365 * (y - 1970) + Math.floor((y - 1969) / 4) - Math.floor((y - 1901) / 100) + Math.floor((y - 1601) / 400));
}
export function TimeFromYear(y) {
return new Value(msPerDay * DayFromYear(y).numberValue());
}
export const msPerAverageYear = 12 * 30.436875 * msPerDay;
export function YearFromTime(t) {
t = t.numberValue();
let year = Math.floor((t + msPerAverageYear / 2) / msPerAverageYear) + 1970;
if (TimeFromYear(new Value(year)).numberValue() > t) {
year -= 1;
}
return new Value(year);
}
export function InLeapYear(t) {
if (DaysInYear(YearFromTime(t)).numberValue() === 365) {
return new Value(0);
}
if (DaysInYear(YearFromTime(t)).numberValue() === 366) {
return new Value(1);
}
}
// 20.3.1.4 #sec-month-number
export function MonthFromTime(t) {
const dayWithinYear = DayWithinYear(t).numberValue();
const inLeapYear = InLeapYear(t).numberValue();
if (dayWithinYear >= 0 && dayWithinYear < 31) {
return new Value(0);
}
if (dayWithinYear >= 31 && dayWithinYear < 59 + inLeapYear) {
return new Value(1);
}
if (dayWithinYear >= 59 + inLeapYear && dayWithinYear < 90 + inLeapYear) {
return new Value(2);
}
if (dayWithinYear >= 90 + inLeapYear && dayWithinYear < 120 + inLeapYear) {
return new Value(3);
}
if (dayWithinYear >= 120 + inLeapYear && dayWithinYear < 151 + inLeapYear) {
return new Value(4);
}
if (dayWithinYear >= 151 + inLeapYear && dayWithinYear < 181 + inLeapYear) {
return new Value(5);
}
if (dayWithinYear >= 181 + inLeapYear && dayWithinYear < 212 + inLeapYear) {
return new Value(6);
}
if (dayWithinYear >= 212 + inLeapYear && dayWithinYear < 243 + inLeapYear) {
return new Value(7);
}
if (dayWithinYear >= 243 + inLeapYear && dayWithinYear < 273 + inLeapYear) {
return new Value(8);
}
if (dayWithinYear >= 273 + inLeapYear && dayWithinYear < 304 + inLeapYear) {
return new Value(9);
}
if (dayWithinYear >= 304 + inLeapYear && dayWithinYear < 334 + inLeapYear) {
return new Value(10);
}
if (dayWithinYear >= 334 + inLeapYear && dayWithinYear < 365 + inLeapYear) {
return new Value(11);
}
}
export function DayWithinYear(t) {
return new Value(Day(t).numberValue() - DayFromYear(YearFromTime(t)).numberValue());
}
// 20.3.1.5 #sec-date-number
export function DateFromTime(t) {
const dayWithinYear = DayWithinYear(t).numberValue();
const monthFromTime = MonthFromTime(t).numberValue();
const inLeapYear = InLeapYear(t).numberValue();
switch (monthFromTime) {
case 0: return new Value(dayWithinYear + 1);
case 1: return new Value(dayWithinYear - 30);
case 2: return new Value(dayWithinYear - 58 - inLeapYear);
case 3: return new Value(dayWithinYear - 89 - inLeapYear);
case 4: return new Value(dayWithinYear - 119 - inLeapYear);
case 5: return new Value(dayWithinYear - 150 - inLeapYear);
case 6: return new Value(dayWithinYear - 180 - inLeapYear);
case 7: return new Value(dayWithinYear - 211 - inLeapYear);
case 8: return new Value(dayWithinYear - 242 - inLeapYear);
case 9: return new Value(dayWithinYear - 272 - inLeapYear);
case 10: return new Value(dayWithinYear - 303 - inLeapYear);
case 11: return new Value(dayWithinYear - 333 - inLeapYear);
default: // Unreachable
}
}
// 20.3.1.6 #sec-week-day
export function WeekDay(t) {
return new Value(mod(Day(t).numberValue() + 4, 7));
}
// 20.3.1.7 #sec-local-time-zone-adjustment
export function LocalTZA(/* t, isUTC */) {
// TODO: implement this function properly.
return 0;
}
// 20.3.1.8 #sec-localtime
export function LocalTime(t) {
return new Value(t.numberValue() + LocalTZA(t, true));
}
// 20.3.1.9 #sec-utc-t
export function UTC(t) {
return new Value(t.numberValue() - LocalTZA(t, false));
}
// 20.3.1.10 #sec-hours-minutes-second-and-milliseconds
export function HourFromTime(t) {
return new Value(mod(Math.floor(t.numberValue() / msPerHour), HoursPerDay));
}
export function MinFromTime(t) {
return new Value(mod(Math.floor(t.numberValue() / msPerMinute), MinutesPerHour));
}
export function SecFromTime(t) {
return new Value(mod(Math.floor(t.numberValue() / msPerSecond), SecondsPerMinute));
}
export function msFromTime(t) {
return new Value(mod(t.numberValue(), msPerSecond));
}
// 20.3.1.11 #sec-maketime
export function MakeTime(hour, min, sec, ms) {
if (!Number.isFinite(hour.numberValue()) || !Number.isFinite(min.numberValue()) || !Number.isFinite(sec.numberValue()) || !Number.isFinite(ms.numberValue())) {
return new Value(NaN);
}
const h = X(ToInteger(hour)).numberValue();
const m = X(ToInteger(min)).numberValue();
const s = X(ToInteger(sec)).numberValue();
const milli = X(ToInteger(ms)).numberValue();
const t = h * msPerHour + m * msPerMinute + s * msPerSecond + milli;
return new Value(t);
}
const daysWithinYearToEndOfMonth = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365];
// 20.3.1.12 #sec-makeday
export function MakeDay(year, month, date) {
if (!Number.isFinite(year.numberValue()) || !Number.isFinite(month.numberValue()) || !Number.isFinite(date.numberValue())) {
return new Value(NaN);
}
const y = X(ToInteger(year)).numberValue();
const m = X(ToInteger(month)).numberValue();
const dt = X(ToInteger(date)).numberValue();
const ym = y + Math.floor(m / 12);
const mn = mod(m, 12);
const ymday = DayFromYear(new Value(ym + (mn > 1 ? 1 : 0))).numberValue() - 365 * (mn > 1 ? 1 : 0) + daysWithinYearToEndOfMonth[mn];
const t = new Value(ymday * msPerDay);
return new Value(Day(t).numberValue() + dt - 1);
}
// 20.3.1.13 #sec-makedate
export function MakeDate(day, time) {
if (!Number.isFinite(day.numberValue()) || !Number.isFinite(time.numberValue())) {
return new Value(NaN);
}
return new Value(day.numberValue() * msPerDay + time.numberValue());
}
// 20.3.1.14 #sec-timeclip
export function TimeClip(time) {
if (!Number.isFinite(time.numberValue())) {
return new Value(NaN);
}
if (Math.abs(time.numberValue()) > 8.64e15) {
return new Value(NaN);
}
return X(ToInteger(time));
}
@@ -1,76 +0,0 @@
import { Q } from '../completion.mjs';
import { surroundingAgent } from '../engine.mjs';
import {
GetIdentifierReference,
EnvironmentRecord,
} from '../environment.mjs';
import { Value } from '../value.mjs';
import { Assert } from './all.mjs';
// This file covers abstract operations defined in
// 8.3 #sec-execution-contexts
// 8.3.1 #sec-getactivescriptormodule
export function GetActiveScriptOrModule() {
for (let i = surroundingAgent.executionContextStack.length - 1; i >= 0; i -= 1) {
const e = surroundingAgent.executionContextStack[i];
if (e.ScriptOrModule !== Value.null) {
return e.ScriptOrModule;
}
}
return Value.null;
}
// 8.3.2 #sec-resolvebinding
export function ResolveBinding(name, env, strict) {
// 1. If env is not present or if env is undefined, then
if (env === undefined || env === Value.undefined) {
// a. Set env to the running execution context's LexicalEnvironment.
env = surroundingAgent.runningExecutionContext.LexicalEnvironment;
}
// 2. Assert: env is an Environment Record.
Assert(env instanceof EnvironmentRecord);
// 3. If the code matching the syntactic production that is being evaluated is contained in strict mode code, let strict be true; else let strict be false.
// 4. Return ? GetIdentifierReference(env, name, strict).
return GetIdentifierReference(env, name, strict ? Value.true : Value.false);
}
// #sec-getthisenvironment
export function GetThisEnvironment() {
// 1. Let env be the running execution context's LexicalEnvironment.
let env = surroundingAgent.runningExecutionContext.LexicalEnvironment;
// 2. Repeat,
while (true) {
// a. Let exists be env.HasThisBinding().
const exists = env.HasThisBinding();
// b. If exists is true, return envRec.
if (exists === Value.true) {
return env;
}
// c. Let outer be env.[[OuterEnv]].
const outer = env.OuterEnv;
// d. Assert: outer is not null.
Assert(outer !== Value.null);
// e. Set env to outer.
env = outer;
}
}
// 8.3.4 #sec-resolvethisbinding
export function ResolveThisBinding() {
const envRec = GetThisEnvironment();
return Q(envRec.GetThisBinding());
}
// 8.3.5 #sec-getnewtarget
export function GetNewTarget() {
const envRec = GetThisEnvironment();
Assert('NewTarget' in envRec);
return envRec.NewTarget;
}
// 8.3.6 #sec-getglobalobject
export function GetGlobalObject() {
const currentRealm = surroundingAgent.currentRealmRecord;
return currentRealm.GlobalObject;
}
@@ -1,449 +0,0 @@
import {
surroundingAgent,
ExecutionContext,
} from '../engine.mjs';
import {
Descriptor,
Type,
Value,
} from '../value.mjs';
import {
EnsureCompletion,
NormalCompletion,
ReturnIfAbrupt,
Q, X,
} from '../completion.mjs';
import { ExpectedArgumentCount } from '../static-semantics/all.mjs';
import { EvaluateBody } from '../runtime-semantics/all.mjs';
import {
FunctionEnvironmentRecord,
GlobalEnvironmentRecord,
NewFunctionEnvironment,
} from '../environment.mjs';
import { unwind } from '../helpers.mjs';
import {
Assert,
DefinePropertyOrThrow,
GetActiveScriptOrModule,
HasOwnProperty,
IsConstructor,
IsExtensible,
IsInteger,
MakeBasicObject,
OrdinaryObjectCreate,
OrdinaryCreateFromConstructor,
ToObject,
isStrictModeCode,
Realm,
} from './all.mjs';
// This file covers abstract operations defined in
// 9.2 #sec-ecmascript-function-objects
// 9.3 #sec-built-in-function-objects
// and
// 14.9 #sec-tail-position-calls
export function isECMAScriptFunctionObject(O) {
return 'ECMAScriptCode' in O;
}
export function isFunctionObject(O) {
return 'Call' in O;
}
// #sec-prepareforordinarycall
export function PrepareForOrdinaryCall(F, newTarget) {
// 1. Assert: Type(newTarget) is Undefined or Object.
Assert(Type(newTarget) === 'Undefined' || Type(newTarget) === 'Object');
// 2. Let callerContext be the running execution context.
// const callerContext = surroundingAgent.runningExecutionContext;
// 3. Let calleeContext be a new ECMAScript code execution context.
const calleeContext = new ExecutionContext();
// 4. Set the Function of calleeContext to F.
calleeContext.Function = F;
// 5. Let calleeRealm be F.[[Realm]].
const calleeRealm = F.Realm;
// 6. Set the Realm of calleeContext to calleeRealm.
calleeContext.Realm = calleeRealm;
// 7. Set the ScriptOrModule of calleeContext to F.[[ScriptOrModule]].
calleeContext.ScriptOrModule = F.ScriptOrModule;
// 8. Let localEnv be NewFunctionEnvironment(F, newTarget).
const localEnv = NewFunctionEnvironment(F, newTarget);
// 9. Set the LexicalEnvironment of calleeContext to localEnv.
calleeContext.LexicalEnvironment = localEnv;
// 10. Set the VariableEnvironment of calleeContext to localEnv.
calleeContext.VariableEnvironment = localEnv;
// 11. Set the VariableEnvironment of calleeContext to localEnv.
// 12. Push calleeContext onto the execution context stack; calleeContext is now the running execution context.
surroundingAgent.executionContextStack.push(calleeContext);
// 13. NOTE: Any exception objects produced after this point are associated with calleeRealm.
// 14. Return calleeContext.
return calleeContext;
}
// #sec-ordinarycallbindthis
export function OrdinaryCallBindThis(F, calleeContext, thisArgument) {
// 1. Let thisMode be F.[[ThisMode]].
const thisMode = F.ThisMode;
// 2. If thisMode is lexical, return NormalCompletion(undefined).
if (thisMode === 'lexical') {
return NormalCompletion(Value.undefined);
}
// 3. Let calleeRealm be F.[[Realm]].
const calleeRealm = F.Realm;
// 4. Let localEnv be the LexicalEnvironment of calleeContext.
const localEnv = calleeContext.LexicalEnvironment;
let thisValue;
// 5. If thisMode is strict, let thisValue be thisArgument.
if (thisMode === 'strict') {
thisValue = thisArgument;
} else { // 6. Else,
// a. If thisArgument is undefined or null, then
if (thisArgument === Value.undefined || thisArgument === Value.null) {
// i. Let globalEnv be calleeRealm.[[GlobalEnv]].
const globalEnv = calleeRealm.GlobalEnv;
// ii. Assert: globalEnv is a global Environment Record.
Assert(globalEnv instanceof GlobalEnvironmentRecord);
// iii. Let thisValue be globalEnv.[[GlobalThisValue]].
thisValue = globalEnv.GlobalThisValue;
} else { // b. Else,
// i. Let thisValue be ! ToObject(thisArgument).
thisValue = X(ToObject(thisArgument));
// ii. NOTE: ToObject produces wrapper objects using calleeRealm.
}
}
// 7. Assert: localEnv is a function Environment Record.
Assert(localEnv instanceof FunctionEnvironmentRecord);
// 8. Assert: The next step never returns an abrupt completion because localEnv.[[ThisBindingStatus]] is not initialized.
Assert(localEnv.ThisBindingStatus !== 'initialized');
// 10. Return localEnv.BindThisValue(thisValue).
return localEnv.BindThisValue(thisValue);
}
// #sec-ordinarycallevaluatebody
export function OrdinaryCallEvaluateBody(F, argumentsList) {
// 1. Return the result of EvaluateBody of the parsed code that is F.[[ECMAScriptCode]] passing F and argumentsList as the arguments.
return EnsureCompletion(unwind(EvaluateBody(F.ECMAScriptCode, F, argumentsList)));
}
// #sec-ecmascript-function-objects-call-thisargument-argumentslist
function FunctionCallSlot(thisArgument, argumentsList) {
const F = this;
// 1. Assert: F is an ECMAScript function object.
Assert(isECMAScriptFunctionObject(F));
// 2. If F.[[IsClassConstructor]] is true, throw a TypeError exception.
if (F.IsClassConstructor === Value.true) {
return surroundingAgent.Throw('TypeError', 'ConstructorNonCallable', F);
}
// 3. Let callerContext be the running execution context.
// 4. Let calleeContext be PrepareForOrdinaryCall(F, undefined).
const calleeContext = PrepareForOrdinaryCall(F, Value.undefined);
// 5. Assert: calleeContext is now the running execution context.
Assert(surroundingAgent.runningExecutionContext === calleeContext);
// 6. Perform OrdinaryCallBindThis(F, calleeContext, thisArgument).
OrdinaryCallBindThis(F, calleeContext, thisArgument);
// 7. Let result be OrdinaryCallEvaluateBody(F, argumentsList).
const result = OrdinaryCallEvaluateBody(F, argumentsList);
// 8. Remove calleeContext from the execution context stack and restore callerContext as the running execution context.
surroundingAgent.executionContextStack.pop(calleeContext);
// 9. If result.[[Type]] is return, return NormalCompletion(result.[[Value]]).
if (result.Type === 'return') {
return NormalCompletion(result.Value);
}
// 10. ReturnIfAbrupt(result).
ReturnIfAbrupt(result);
// 11. Return NormalCompletion(undefined).
return NormalCompletion(Value.undefined);
}
// 9.2.2 #sec-ecmascript-function-objects-construct-argumentslist-newtarget
function FunctionConstructSlot(argumentsList, newTarget) {
const F = this;
// 1. Assert: F is an ECMAScript function object.
Assert(isECMAScriptFunctionObject(F));
// 2. Assert: Type(newTarget) is Object.
Assert(Type(newTarget) === 'Object');
// 3. Let callerContext be the running execution context.
// 4. Let kind be F.[[ConstructorKind]].
const kind = F.ConstructorKind;
let thisArgument;
// 5. If kind is base, then
if (kind === 'base') {
// a. Let thisArgument be ? OrdinaryCreateFromConstructor(newTarget, "%Object.prototype%").
thisArgument = Q(OrdinaryCreateFromConstructor(newTarget, '%Object.prototype%'));
}
// 6. Let calleeContext be PrepareForOrdinaryCall(F, newTarget).
const calleeContext = PrepareForOrdinaryCall(F, newTarget);
// 7. Assert: calleeContext is now the running execution context.
Assert(surroundingAgent.runningExecutionContext === calleeContext);
surroundingAgent.runningExecutionContext.callSite.constructCall = true;
// 8. If kind is base, perform OrdinaryCallBindThis(F, calleeContext, thisArgument).
if (kind === 'base') {
OrdinaryCallBindThis(F, calleeContext, thisArgument);
}
// 9. Let constructorEnv be the LexicalEnvironment of calleeContext.
const constructorEnv = calleeContext.LexicalEnvironment;
// 10. Let result be OrdinaryCallEvaluateBody(F, argumentsList).
const result = OrdinaryCallEvaluateBody(F, argumentsList);
// 11. Remove calleeContext from the execution context stack and restore callerContext as the running execution context.
surroundingAgent.executionContextStack.pop(calleeContext);
// 12. If result.[[Type]] is return, then
if (result.Type === 'return') {
// a. If Type(result.[[Value]]) is Object, return NormalCompletion(result.[[Value]]).
if (Type(result.Value) === 'Object') {
return NormalCompletion(result.Value);
}
// b. If kind is base, return NormalCompletion(thisArgument).
if (kind === 'base') {
return NormalCompletion(thisArgument);
}
// c. If result.[[Value]] is not undefined, throw a TypeError exception.
if (result.Value !== Value.undefined) {
return surroundingAgent.Throw('TypeError', 'DerivedConstructorReturnedNonObject');
}
} else { // 13. Else, ReturnIfAbrupt(result).
ReturnIfAbrupt(result);
}
// 14. Return ? constructorEnv.GetThisBinding().
return Q(constructorEnv.GetThisBinding());
}
// 9.2.3 #sec-functionallocate
export function OrdinaryFunctionCreate(functionPrototype, sourceText, ParameterList, Body, thisMode, Scope) {
Assert(Type(functionPrototype) === 'Object');
const internalSlotsList = [
'Environment',
'FormalParameters',
'ECMAScriptCode',
'ConstructorKind',
'Realm',
'ScriptOrModule',
'ThisMode',
'Strict',
'HomeObject',
'SourceText',
'IsClassConstructor',
];
const F = X(OrdinaryObjectCreate(functionPrototype, internalSlotsList));
F.Call = surroundingAgent.hostDefinedOptions.boost
? surroundingAgent.hostDefinedOptions.boost.callFunction
: FunctionCallSlot;
F.SourceText = sourceText;
F.Environment = Scope;
F.FormalParameters = ParameterList;
F.ECMAScriptCode = Body;
const Strict = isStrictModeCode(Body);
F.Strict = Strict;
if (thisMode === 'lexical-this') {
F.ThisMode = 'lexical';
} else if (Strict) {
F.ThisMode = 'strict';
} else {
F.ThisMode = 'global';
}
F.IsClassConstructor = Value.false;
F.Environment = Scope;
F.ScriptOrModule = GetActiveScriptOrModule();
F.Realm = surroundingAgent.currentRealmRecord;
F.HomeObject = Value.undefined;
const len = ExpectedArgumentCount(ParameterList);
X(SetFunctionLength(F, new Value(len)));
return F;
}
// 9.2.10 #sec-makeconstructor
export function MakeConstructor(F, writablePrototype, prototype) {
Assert(isECMAScriptFunctionObject(F));
Assert(IsConstructor(F) === Value.false);
Assert(X(IsExtensible(F)) === Value.true && X(HasOwnProperty(F, new Value('prototype'))) === Value.false);
F.Construct = surroundingAgent.hostDefinedOptions.boost
? surroundingAgent.hostDefinedOptions.boost.constructFunction
: FunctionConstructSlot;
F.ConstructorKind = 'base';
if (writablePrototype === undefined) {
writablePrototype = Value.true;
}
if (prototype === undefined) {
prototype = OrdinaryObjectCreate(surroundingAgent.intrinsic('%Object.prototype%'));
X(DefinePropertyOrThrow(prototype, new Value('constructor'), Descriptor({
Value: F,
Writable: writablePrototype,
Enumerable: Value.false,
Configurable: Value.true,
})));
}
X(DefinePropertyOrThrow(F, new Value('prototype'), Descriptor({
Value: prototype,
Writable: writablePrototype,
Enumerable: Value.false,
Configurable: Value.false,
})));
return NormalCompletion(Value.undefined);
}
// 9.2.11 #sec-makeclassconstructor
export function MakeClassConstructor(F) {
Assert(isECMAScriptFunctionObject(F));
Assert(F.IsClassConstructor === Value.false);
F.IsClassConstructor = Value.true;
return NormalCompletion(Value.undefined);
}
// 9.2.12 #sec-makemethod
export function MakeMethod(F, homeObject) {
Assert(isECMAScriptFunctionObject(F));
Assert(Type(homeObject) === 'Object');
F.HomeObject = homeObject;
return NormalCompletion(Value.undefined);
}
// #sec-setfunctionname
export function SetFunctionName(F, name, prefix) {
// 1. Assert: F is an extensible object that does not have a "name" own property.
Assert(IsExtensible(F) === Value.true && HasOwnProperty(F, new Value('name')) === Value.false);
// 2. Assert: Type(name) is either Symbol or String.
Assert(Type(name) === 'Symbol' || Type(name) === 'String');
// 3. Assert: If prefix is present, then Type(prefix) is String.
Assert(!prefix || Type(prefix) === 'String');
// 4. If Type(name) is Symbol, then
if (Type(name) === 'Symbol') {
// a. Let description be name's [[Description]] value.
const description = name.Description;
// b. If description is undefined, set name to the empty String.
if (description === Value.undefined) {
name = new Value('');
} else {
// c. Else, set name to the string-concatenation of "[", description, and "]".
name = new Value(`[${description.stringValue()}]`);
}
}
// 5. If F has an [[InitialName]] internal slot, then
if ('InitialName' in F) {
// a. Set F.[[InitialName]] to name.
F.InitialName = name;
}
// 6. If prefix is present, then
if (prefix !== undefined) {
// a. Set name to the string-concatenation of prefix, the code unit 0x0020 (SPACE), and name.
name = new Value(`${prefix.stringValue()} ${name.stringValue()}`);
// b. If F has an [[InitialName]] internal slot, then
if ('InitialName' in F) {
// i. Optionally, set F.[[InitialName]] to name.
}
}
// 7. Return ! DefinePropertyOrThrow(F, "name", PropertyDescriptor { [[Value]]: name, [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }).
return X(DefinePropertyOrThrow(F, new Value('name'), Descriptor({
Value: name,
Writable: Value.false,
Enumerable: Value.false,
Configurable: Value.true,
})));
}
// 9.2.14 #sec-setfunctionlength
export function SetFunctionLength(F, length) {
Assert(IsExtensible(F) === Value.true && HasOwnProperty(F, new Value('length')) === Value.false);
Assert(Type(length) === 'Number');
Assert(length.numberValue() >= 0 && X(IsInteger(length)) === Value.true);
return X(DefinePropertyOrThrow(F, new Value('length'), Descriptor({
Value: length,
Writable: Value.false,
Enumerable: Value.false,
Configurable: Value.true,
})));
}
function nativeCall(F, argumentsList, thisArgument, newTarget) {
return F.nativeFunction(argumentsList, {
thisValue: thisArgument || Value.undefined,
NewTarget: newTarget || Value.undefined,
});
}
function BuiltinFunctionCall(thisArgument, argumentsList) {
const F = this;
// const callerContext = surroundingAgent.runningExecutionContext;
// If callerContext is not already suspended, suspend callerContext.
const calleeContext = new ExecutionContext();
calleeContext.Function = F;
const calleeRealm = F.Realm;
calleeContext.Realm = calleeRealm;
calleeContext.ScriptOrModule = F.ScriptOrModule;
// 8. Perform any necessary implementation-defined initialization of calleeContext.
surroundingAgent.executionContextStack.push(calleeContext);
const result = nativeCall(F, argumentsList, thisArgument, Value.undefined);
// Remove calleeContext from the execution context stack and
// restore callerContext as the running execution context.
surroundingAgent.executionContextStack.pop(calleeContext);
return result;
}
function BuiltinFunctionConstruct(argumentsList, newTarget) {
const F = this;
// const callerContext = surroundingAgent.runningExecutionContext;
// If callerContext is not already suspended, suspend callerContext.
const calleeContext = new ExecutionContext();
calleeContext.Function = F;
const calleeRealm = F.Realm;
calleeContext.Realm = calleeRealm;
calleeContext.ScriptOrModule = F.ScriptOrModule;
// 8. Perform any necessary implementation-defined initialization of calleeContext.
surroundingAgent.executionContextStack.push(calleeContext);
surroundingAgent.runningExecutionContext.callSite.constructCall = true;
const result = nativeCall(F, argumentsList, undefined, newTarget);
// Remove calleeContext from the execution context stack and
// restore callerContext as the running execution context.
surroundingAgent.executionContextStack.pop(calleeContext);
return result;
}
// 9.3.3 #sec-createbuiltinfunction
export function CreateBuiltinFunction(steps, internalSlotsList, realm, prototype, isConstructor = Value.false) {
// 1. Assert: steps is either a set of algorithm steps or other definition of a function's behaviour provided in this specification.
Assert(typeof steps === 'function');
// 2. If realm is not present, set realm to the current Realm Record.
if (realm === undefined) {
realm = surroundingAgent.currentRealmRecord;
}
// 3. Assert: realm is a Realm Record.
Assert(realm instanceof Realm);
// 4. If prototype is not present, set prototype to realm.[[Intrinsics]].[[%Function.prototype%]].
if (prototype === undefined) {
prototype = realm.Intrinsics['%Function.prototype%'];
}
// 5. Let func be a new built-in function object that when called performs the action described by steps. The new function object has internal slots whose names are the elements of internalSlotsList.
const func = X(MakeBasicObject(['Prototype', 'Extensible', 'Realm', 'ScriptOrModule', 'InitialName'].concat(internalSlotsList)));
func.Call = BuiltinFunctionCall;
if (isConstructor === Value.true) {
func.Construct = BuiltinFunctionConstruct;
}
func.nativeFunction = steps;
// 6. Set func.[[Realm]] to realm.
func.Realm = realm;
// 7. Set func.[[Prototype]] to prototype.
func.Prototype = prototype;
// 8. Set func.[[Extensible]] to true.
func.Extensible = Value.true;
// 9. Set func.[[ScriptOrModule]] to null.
func.ScriptOrModule = Value.null;
// 10. Set func.[[InitialName]] to null.
func.InitialName = Value.null;
// 11. Return func.
return func;
}
// 14.9.3 #sec-preparefortailcall
export function PrepareForTailCall() {
// 1. Let leafContext be the running execution context.
const leafContext = surroundingAgent.runningExecutionContext;
// 2. Suspend leafContext.
// 3. Pop leafContext from the execution context stack. The execution context now on the top of the stack becomes the running execution context.
surroundingAgent.executionContextStack.pop(leafContext);
// 4. Assert: leafContext has no further use. It will never be activated as the running execution context.
leafContext.poppedForTailCall = true;
}
@@ -1,200 +0,0 @@
import {
Completion,
NormalCompletion,
Q, X,
EnsureCompletion,
} from '../completion.mjs';
import { surroundingAgent } from '../engine.mjs';
import { Value } from '../value.mjs';
import { Evaluate } from '../evaluator.mjs';
import { resume } from '../helpers.mjs';
import {
Assert,
CreateIterResultObject,
RequireInternalSlot,
} from './all.mjs';
// This file covers abstract operations defined in #sec-generator-objects
// #sec-generatorstart
export function GeneratorStart(generator, generatorBody) {
// 1. Assert: The value of generator.[[GeneratorState]] is undefined.
Assert(generator.GeneratorState === Value.undefined);
// 2. Let genContext be the running execution context.
const genContext = surroundingAgent.runningExecutionContext;
// 3. Set the Generator component of genContext to generator.
genContext.Generator = generator;
// 4. Set the code evaluation state of genContext such that when evaluation is resumed
// for that execution context the following steps will be performed:
genContext.codeEvaluationState = (function* resumer() {
// a. Let result be the result of evaluating generatorBody.
const result = EnsureCompletion(yield* Evaluate(generatorBody));
// b. Assert: If we return here, the generator either threw an exception or
// performed either an implicit or explicit return.
// c. Remove genContext from the execution context stack and restore the execution context
// that is at the top of the execution context stack as the running execution context.
surroundingAgent.executionContextStack.pop(genContext);
// d. Set generator.[[GeneratorState]] to completed.
generator.GeneratorState = 'completed';
// e. Once a generator enters the completed state it never leaves it and its
// associated execution context is never resumed. Any execution state associated
// with generator can be discarded at this point.
genContext.codeEvaluationState = null;
// f. If result.[[Type]] is normal, let resultValue be undefined.
let resultValue;
if (result.Type === 'normal') {
resultValue = Value.undefined;
} else if (result.Type === 'return') {
// g. Else if result.[[Type]] is return, let resultValue be result.[[Value]].
resultValue = result.Value;
} else {
// i. Assert: result.[[Type]] is throw.
Assert(result.Type === 'throw');
// ii. Return Completion(result).
return Completion(result);
}
// i. Return CreateIterResultObject(resultValue, true).
return X(CreateIterResultObject(resultValue, Value.true));
}());
// 5. Set generator.[[GeneratorContext]] to genContext.
generator.GeneratorContext = genContext;
// 6. Set generator.[[GeneratorState]] to suspendedStart.
generator.GeneratorState = 'suspendedStart';
// 7. Return NormalCompletion(undefined).
return NormalCompletion(Value.undefined);
}
// #sec-generatorvalidate
export function GeneratorValidate(generator) {
// 1. Perform ? RequireInternalSlot(generator, [[GeneratorState]]).
Q(RequireInternalSlot(generator, 'GeneratorState'));
// 2. Assert: generator also has a [[GeneratorContext]] internal slot.
Assert('GeneratorContext' in generator);
// 3. Let state be generator.[[GeneratorState]].
const state = generator.GeneratorState;
// 4. If state is executing, throw a TypeError exception.
if (state === 'executing') {
return surroundingAgent.Throw('TypeError', 'GeneratorRunning');
}
// 5. Return state.
return state;
}
// #sec-generatorresume
export function GeneratorResume(generator, value) {
// 1. Let state be ? GeneratorValidate(generator).
const state = Q(GeneratorValidate(generator));
// 2. If state is completed, return CreateIterResultObject(undefined, true).
if (state === 'completed') {
return X(CreateIterResultObject(Value.undefined, Value.true));
}
// 3. Assert: state is either suspendedStart or suspendedYield.
Assert(state === 'suspendedStart' || state === 'suspendedYield');
// 4. Let genContext be generator.[[GeneratorContext]].
const genContext = generator.GeneratorContext;
// 5. Let methodContext be the running execution context.
// 6. Suspend methodContext.
const methodContext = surroundingAgent.runningExecutionContext;
// 7. Set generator.[[GeneratorState]] to executing.
generator.GeneratorState = 'executing';
// 8. Push genContext onto the execution context stack.
surroundingAgent.executionContextStack.push(genContext);
// 9. Resume the suspended evaluation of genContext using NormalCompletion(value) as
// the result of the operation that suspended it. Let result be the value returned by
// the resumed computation.
const result = EnsureCompletion(resume(genContext, NormalCompletion(value)));
// 10. Assert: When we return here, genContext has already been removed from the execution
// context stack and methodContext is the currently running execution context.
Assert(surroundingAgent.runningExecutionContext === methodContext);
// 11. Return Completion(result).
return Completion(result);
}
// #sec-generatorresumeabrupt
export function GeneratorResumeAbrupt(generator, abruptCompletion) {
// 1. Let state be ? GeneratorValidate(generator).
let state = Q(GeneratorValidate(generator));
// 2. If state is suspendedStart, then
if (state === 'suspendedStart') {
// a. Set generator.[[GeneratorState]] to completed.
generator.GeneratorState = 'completed';
// b. Once a generator enters the completed state it never leaves it and its
// associated execution context is never resumed. Any execution state associate
// with generator can be discarded at this point.
generator.GeneratorContext = null;
// c. Set state to completed.
state = 'completed';
}
// 3. If state is completed, then
if (state === 'completed') {
// a. If abruptCompletion.[[Type]] is return, then
if (abruptCompletion.Type === 'return') {
// i. Return CreateIterResultObject(abruptCompletion.[[Value]], true).
return X(CreateIterResultObject(abruptCompletion.Value, Value.true));
}
// b. Return Completion(abruptCompletion).
return Completion(abruptCompletion);
}
// 4. Assert: state is suspendedYield.
Assert(state === 'suspendedYield');
// 5. Let genContext be generator.[[GeneratorContext]].
const genContext = generator.GeneratorContext;
// 6. Let methodContext be the running execution context.
// 7. Suspend methodContext.
const methodContext = surroundingAgent.runningExecutionContext;
// 8. Set generator.[[GeneratorState]] to executing.
generator.GeneratorState = 'executing';
// 9. Push genContext onto the execution context stack.
surroundingAgent.executionContextStack.push(genContext);
// 10. Resume the suspended evaluation of genContext using abruptCompletion as the
// result of the operation that suspended it. Let result be the completion record
// returned by the resumed computation.
const result = EnsureCompletion(resume(genContext, abruptCompletion));
// 11. Assert: When we return here, genContext has already been removed from the
// execution context stack and methodContext is the currently running execution context.
Assert(surroundingAgent.runningExecutionContext === methodContext);
// 12. Return Completion(result).
return Completion(result);
}
// #sec-getgeneratorkind
export function GetGeneratorKind() {
// 1. Let genContext be the running execution context.
const genContext = surroundingAgent.runningExecutionContext;
// 2. If genContext does not have a Generator component, return non-generator.
if (!genContext.Generator) {
return 'non-generator';
}
// 3. Let generator be the Generator component of genContext.
const generator = genContext.Generator;
// 4. If generator has an [[AsyncGeneratorState]] internal slot, return async.
if ('AsyncGeneratorState' in generator) {
return 'async';
}
// 5. Else, return sync.
return 'sync';
}
// #sec-generatoryield
export function* GeneratorYield(iterNextObj) {
// 1. Assert: iterNextObj is an Object that implements the IteratorResult interface.
// 2. Let genContext be the running execution context.
const genContext = surroundingAgent.runningExecutionContext;
// 3. Assert: genContext is the execution context of a generator.
Assert(genContext.Generator !== undefined);
// 4. Let generator be the value of the Generator component of genContext.
const generator = genContext.Generator;
// 5. Assert: GetGeneratorKind is sync.
Assert(GetGeneratorKind() === 'sync');
// 6. Set generator.GeneratorState to suspendedYield.
generator.GeneratorState = 'suspendedYield';
// 7. Remove genContext from the execution context stack.
surroundingAgent.executionContextStack.pop(genContext);
// 8. Set the code evaluation state of genContext such that when evaluation is resumed with
// a Completion resumptionValue the following steps will be performed:
// a. Return resumptionValue
const resumptionValue = yield NormalCompletion(iterNextObj);
// 9. Return NormalCompletion(iterNextObj).
return resumptionValue;
// 10. NOTE: this returns to the evaluation of the operation that had most previously resumed evaluation of genContext.
}
@@ -1,332 +0,0 @@
import { ExecutionContext, HostEnsureCanCompileStrings, surroundingAgent } from '../engine.mjs';
import { Type, Value } from '../value.mjs';
import { InstantiateFunctionObject } from '../runtime-semantics/all.mjs';
import {
IsStrict,
VarDeclaredNames,
VarScopedDeclarations,
LexicallyScopedDeclarations,
BoundNames,
IsConstantDeclaration,
} from '../static-semantics/all.mjs';
import {
Completion,
AbruptCompletion,
NormalCompletion,
EnsureCompletion,
Q, X,
} from '../completion.mjs';
import { wrappedParse } from '../parse.mjs';
import {
NewDeclarativeEnvironment,
FunctionEnvironmentRecord,
GlobalEnvironmentRecord,
ObjectEnvironmentRecord,
} from '../environment.mjs';
import { Evaluate } from '../evaluator.mjs';
import { unwind, ValueSet } from '../helpers.mjs';
import { Assert, GetThisEnvironment } from './all.mjs';
// This file covers abstract operations defined in
// 18 #sec-global-object
// 2.1.1 #sec-performeval
export function PerformEval(x, callerRealm, strictCaller, direct) {
// 1. Assert: If direct is false, then strictCaller is also false.
if (direct === false) {
Assert(strictCaller === false);
}
// 2. If Type(x) is not String, return x.
if (Type(x) !== 'String') {
return x;
}
// 3. Let evalRealm be the current Realm Record.
const evalRealm = surroundingAgent.currentRealmRecord;
// 4. Perform ? HostEnsureCanCompileStrings(callerRealm, evalRealm).
Q(HostEnsureCanCompileStrings(callerRealm, evalRealm));
// 5. Let inFunction be false.
let inFunction = false;
// 6. Let inMethod be false.
let inMethod = false;
// 7. Let inDerivedConstructor be false.
let inDerivedConstructor = false;
// 8. If direct is true, then
if (direct === true) {
// a. Let thisEnv be ! GetThisEnvironment().
const thisEnv = X(GetThisEnvironment());
// b. If thisEnv is a function Environment Record, then
if (thisEnv instanceof FunctionEnvironmentRecord) {
// i. Let F be thisEnv.[[FunctionObject]].
const F = thisEnv.FunctionObject;
// ii. Let inFunction be true.
inFunction = true;
// iii. Let inMethod be thisEnv.HasSuperBinding().
inMethod = thisEnv.HasSuperBinding() === Value.true;
// iv. If F.[[ConstructorKind]] is derived, set inDerivedConstructor to true.
if (F.ConstructorKind === 'derived') {
inDerivedConstructor = true;
}
}
}
// 9. Perform the following substeps in an implementation-dependent order, possibly interleaving parsing and error detection:
// a. Let script be the ECMAScript code that is the result of parsing ! UTF16DecodeString(x), for the goal symbol Script.
// b. If script Contains ScriptBody is false, return undefined.
// c. Let body be the ScriptBody of script.
// d. If inFunction is false, and body Contains NewTarget, throw a SyntaxError exception.
// e. If inMethod is false, and body Contains SuperProperty, throw a SyntaxError exception.
// f. If inDerivedConstructor is false, and body Contains SuperCall, throw a SyntaxError exception.
const script = wrappedParse({ source: x.stringValue() }, (parser) => parser.scope.with({
strict: strictCaller,
newTarget: inFunction,
superProperty: inMethod,
superCall: inDerivedConstructor,
}, () => parser.parseScript()));
if (Array.isArray(script)) {
return surroundingAgent.Throw(script[0]);
}
if (!script.ScriptBody) {
return Value.undefined;
}
const body = script.ScriptBody;
// 10. If strictCaller is true, let strictEval be true.
// 11. Else, let strictEval be IsStrict of script.
let strictEval;
if (strictCaller === true) {
strictEval = true;
} else {
strictEval = IsStrict(script);
}
// 12. Let runningContext be the running execution context.
const runningContext = surroundingAgent.runningExecutionContext;
let lexEnv;
let varEnv;
// 13. NOTE: If direct is true, runningContext will be the execution context that performed the direct eval.
// If direct is false, runningContext will be the execution context for the invocation of the eval function.
// 14. If direct is true, then
if (direct === true) {
// a. Let lexEnv be NewDeclarativeEnvironment(runningContext's LexicalEnvironment).
lexEnv = NewDeclarativeEnvironment(runningContext.LexicalEnvironment);
// b. Let varEnv be runningContext's VariableEnvironment.
varEnv = runningContext.VariableEnvironment;
} else { // 15. Else,
// a. Let lexEnv be NewDeclarativeEnvironment(evalRealm.[[GlobalEnv]]).
lexEnv = NewDeclarativeEnvironment(evalRealm.GlobalEnv);
// b. Let varEnv be evalRealm.[[GlobalEnv]].
varEnv = evalRealm.GlobalEnv;
}
// 16. If strictEval is true, set varEnv to lexEnv.
if (strictEval === true) {
varEnv = lexEnv;
}
// 17. If runningContext is not already suspended, suspend runningContext.
// 18. Let evalContext be a new ECMAScript code execution context.
const evalContext = new ExecutionContext();
// 19. Set evalContext's Function to null.
evalContext.Function = Value.null;
// 20. Set evalContext's Realm to evalRealm.
evalContext.Realm = evalRealm;
// 21. Set evalContext's ScriptOrModule to runningContext's ScriptOrModule.
evalContext.ScriptOrModule = runningContext.ScriptOrModule;
// 22. Set evalContext's VariableEnvironment to varEnv.
evalContext.VariableEnvironment = varEnv;
// 23. Set evalContext's LexicalEnvironment to lexEnv.
evalContext.LexicalEnvironment = lexEnv;
// 24. Push evalContext onto the execution context stack.
surroundingAgent.executionContextStack.push(evalContext);
// 25. Let result be EvalDeclarationInstantiation(body, varEnv, lexEnv, strictEval).
let result = EnsureCompletion(EvalDeclarationInstantiation(body, varEnv, lexEnv, strictEval));
// 26. If result.[[Type]] is normal, then
if (result.Type === 'normal') {
// a. Set result to the result of evaluating body.
result = EnsureCompletion(unwind(Evaluate(body)));
}
// 27. If result.[[Type]] is normal and result.[[Value]] is empty, then
if (result.Type === 'normal' && result.Value === undefined) {
// a. Set result to NormalCompletion(undefined).
result = NormalCompletion(Value.undefined);
}
// 28. Suspend evalContext and remove it from the execution context stack.
// 29. Resume the context that is now on the top of the execution context stack as the running execution context.
surroundingAgent.executionContextStack.pop(evalContext);
// 30. Return Completion(result).
return Completion(result);
}
// 18.2.1.3 #sec-evaldeclarationinstantiation
function EvalDeclarationInstantiation(body, varEnv, lexEnv, strict) {
// 1. Let varNames be the VarDeclaredNames of body.
const varNames = VarDeclaredNames(body);
// 2. Let varDeclarations be the VarScopedDeclarations of body.
const varDeclarations = VarScopedDeclarations(body);
// 3. If strict is false, then
if (strict === false) {
// a. If varEnv is a global Environment Record, then
if (varEnv instanceof GlobalEnvironmentRecord) {
// i. For each name in varNames, do
for (const name of varNames) {
// 1. If varEnv.HasLexicalDeclaration(name) is true, throw a SyntaxError exception.
if (varEnv.HasLexicalDeclaration(name) === Value.true) {
return surroundingAgent.Throw('SyntaxError', 'AlreadyDeclared', name);
}
// 2. NOTE: eval will not create a global var declaration that would be shadowed by a global lexical declaration.
}
}
// b. Let thisLex be lexEnv.
let thisEnv = lexEnv;
// c. Assert: The following loop will terminate.
// d. Repeat, while thisEnv is not the same as varEnv,
while (thisEnv !== varEnv) {
// i. If thisEnv is not an object Environment Record, then
if (!(thisEnv instanceof ObjectEnvironmentRecord)) {
// 1. NOTE: The environment of with statements cannot contain any lexical declaration so it doesn't need to be checked for var/let hoisting conflicts.
// 2. For each name in varNames, do
for (const name of varNames) {
// a. If thisEnv.HasBinding(name) is true, then
if (thisEnv.HasBinding(name) === Value.true) {
// i. Throw a SyntaxError exception.
return surroundingAgent.Throw('SyntaxError', 'AlreadyDeclared', name);
// ii. NOTE: Annex B.3.5 defines alternate semantics for the above step.
}
// b. NOTE: A direct eval will not hoist var declaration over a like-named lexical declaration
}
}
// ii. Set thisEnv to thisEnv.[[OuterEnv]].
thisEnv = thisEnv.OuterEnv;
}
}
// 4. Let functionsToInitialize be a new empty List.
const functionsToInitialize = [];
// 5. Let declaredFunctionNames be a new empty List.
const declaredFunctionNames = new ValueSet();
// 6. For each d in varDeclarations, in reverse list order, do
for (const d of [...varDeclarations].reverse()) {
// a. If d is neither a VariableDeclaration nor a ForBinding nor a BindingIdentifier, then
if (d.type !== 'VariableDeclaration'
&& d.type !== 'ForBinding'
&& d.type !== 'BindingIdentifier') {
// i. Assert: d is either a FunctionDeclaration, a GeneratorDeclaration, an AsyncFunctionDeclaration, or an AsyncGeneratorDeclaration.
Assert(d.type === 'FunctionDeclaration'
|| d.type === 'GeneratorDeclaration'
|| d.type === 'AsyncFunctionDeclaration'
|| d.type === 'AsyncGeneratorDeclaration');
// ii. NOTE: If there are multiple function declarations for the same name, the last declaration is used.
// iii. Let fn be the sole element of the BoundNames of d.
const fn = BoundNames(d)[0];
// iv. If fn is not an element of declaredFunctionNames, then
if (!declaredFunctionNames.has(fn)) {
// 1. If varEnv is a global Environment Record, then
if (varEnv instanceof GlobalEnvironmentRecord) {
// a. Let fnDefinable be ? varEnv.CanDeclareGlobalFunction(fn).
const fnDefinable = Q(varEnv.CanDeclareGlobalFunction(fn));
// b. Let fnDefinable be ? varEnv.CanDeclareGlobalFunction(fn).
if (fnDefinable === Value.false) {
return surroundingAgent.Throw('TypeError', 'AlreadyDeclared', fn);
}
}
// 2. Append fn to declaredFunctionNames.
declaredFunctionNames.add(fn);
// 3. Insert d as the first element of functionsToInitialize.
functionsToInitialize.unshift(d);
}
}
}
// 7. NOTE: Annex B.3.3.3 adds additional steps at this point.
// 8. Let declaredVarNames be a new empty List.
const declaredVarNames = new ValueSet();
// 9. For each d in varDeclarations, do
for (const d of varDeclarations) {
// a. If d is a VariableDeclaration, a ForBinding, or a BindingIdentifier, then
if (d.type === 'VariableDeclaration'
|| d.type === 'ForBinding'
|| d.type === 'BindingIdentifier') {
// i. For each String vn in the BoundNames of d, do
for (const vn of BoundNames(d)) {
// 1. If vn is not an element of declaredFunctionNames, then
if (!declaredFunctionNames.has(vn)) {
// a. If varEnv is a global Environment Record, then
if (varEnv instanceof GlobalEnvironmentRecord) {
// i. Let vnDefinable be ? varEnv.CanDeclareGlobalVar(vn).
const vnDefinable = Q(varEnv.CanDeclareGlobalVar(vn));
// ii. If vnDefinable is false, throw a TypeError exception.
if (vnDefinable === Value.false) {
return surroundingAgent.Throw('TypeError', 'AlreadyDeclared', vn);
}
}
// b. If vn is not an element of declaredVarNames, then
if (!declaredVarNames.has(vn)) {
// i. Append vn to declaredVarNames.
declaredVarNames.add(vn);
}
}
}
}
}
// 10. NOTE: No abnormal terminations occur after this algorithm step unless
// varEnv is a global Environment Record and the global object is a Proxy exotic object.
// 11. Let lexDeclarations be the LexicallyScopedDeclarations of body.
const lexDeclarations = LexicallyScopedDeclarations(body);
// 12. For each element d in lexDeclarations, do
for (const d of lexDeclarations) {
// a. NOTE: Lexically declared names are only instantiated here but not initialized.
// b. For each element dn of the BoundNames of d, do
for (const dn of BoundNames(d)) {
// i. If IsConstantDeclaration of d is true, then
if (IsConstantDeclaration(d)) {
// 1. Perform ? lexEnv.CreateImmutableBinding(dn, true).
Q(lexEnv.CreateImmutableBinding(dn, Value.true));
} else { // ii. Else,
// 1. Perform ? lexEnv.CreateMutableBinding(dn, false).
Q(lexEnv.CreateMutableBinding(dn, Value.false));
}
}
}
// 13. For each Parse Node f in functionsToInitialize, do
for (const f of functionsToInitialize) {
// a. Let fn be the sole element of the BoundNames of f.
const fn = BoundNames(f)[0];
// b. Let fn be the sole element of the BoundNames of f.
const fo = InstantiateFunctionObject(f, lexEnv);
// c. If varEnv is a global Environment Record, then
if (varEnv instanceof GlobalEnvironmentRecord) {
// i. Perform ? varEnv.CreateGlobalFunctionBinding(fn, fo, true).
Q(varEnv.CreateGlobalFunctionBinding(fn, fo, Value.true));
} else { // d. Else,
// i. Let bindingExists be varEnv.HasBinding(fn).
const bindingExists = varEnv.HasBinding(fn);
// ii. If bindingExists is false, then
if (bindingExists === Value.false) {
// 1. Let status be ! varEnv.CreateMutableBinding(fn, true).
const status = X(varEnv.CreateMutableBinding(fn, Value.true));
// 2. Assert: status is not an abrupt completion because of validation preceding step 12.
Assert(!(status instanceof AbruptCompletion));
// 3. Perform ! varEnv.InitializeBinding(fn, fo).
X(varEnv.InitializeBinding(fn, fo));
} else { // iii. Else,
// 1. Perform ! varEnv.SetMutableBinding(fn, fo, false).
X(varEnv.SetMutableBinding(fn, fo, Value.false));
}
}
}
// 14. For each String vn in declaredVarNames, in list order, do
for (const vn of declaredVarNames) {
// a. If varEnv is a global Environment Record, then
if (varEnv instanceof GlobalEnvironmentRecord) {
// i. Perform ? varEnv.CreateGlobalVarBinding(vn, true).
Q(varEnv.CreateGlobalVarBinding(vn, Value.true));
} else { // b. Else,
// i. Let bindingExists be varEnv.HasBinding(vn).
const bindingExists = varEnv.HasBinding(vn);
// ii. If bindingExists is false, then
if (bindingExists === Value.false) {
// 1. Let status be ! varEnv.CreateMutableBinding(vn, true).
const status = X(varEnv.CreateMutableBinding(vn, Value.true));
// 2. Assert: status is not an abrupt completion because of validation preceding step 12.
Assert(!(status instanceof AbruptCompletion));
// 3. Perform ! varEnv.InitializeBinding(vn, undefined).
X(varEnv.InitializeBinding(vn, Value.undefined));
}
}
}
// 15. Return NormalCompletion(empty).
return NormalCompletion(undefined);
}
@@ -1,17 +0,0 @@
import { Type, Value } from '../value.mjs';
import { Q } from '../completion.mjs';
import { Assert, SameValue } from './all.mjs';
// #sec-set-immutable-prototype
export function SetImmutablePrototype(O, V) {
// 1. Assert: Either Type(V) is Object or Type(V) is Null.
Assert(Type(V) === 'Object' || Type(V) === 'Null');
// 2. Let current be ? O.[[GetPrototypeOf]]().
const current = Q(O.GetPrototypeOf());
// 3. If SameValue(V, current) is true, return true.
if (SameValue(V, current) === Value.true) {
return Value.true;
}
// 4. Return false.
return Value.false;
}
@@ -1,314 +0,0 @@
import { surroundingAgent } from '../engine.mjs';
import { Value, Type, Descriptor } from '../value.mjs';
import { Q, X } from '../completion.mjs';
import {
Assert,
CanonicalNumericIndexString,
IsAccessorDescriptor,
IsDetachedBuffer,
IsPropertyKey,
IsValidIntegerIndex,
MakeBasicObject,
OrdinaryGetOwnProperty,
OrdinaryHasProperty,
OrdinaryDefineOwnProperty,
OrdinaryGet,
OrdinarySet,
GetValueFromBuffer,
SetValueInBuffer,
ToString,
ToNumber,
ToBigInt,
isIntegerIndex,
typedArrayInfoByName,
} from './all.mjs';
export function isIntegerIndexedExoticObject(O) {
return O.GetOwnProperty === IntegerIndexedGetOwnProperty;
}
// 9.4.5.1 #sec-integer-indexed-exotic-objects-getownproperty-p
export function IntegerIndexedGetOwnProperty(P) {
const O = this;
// 1. Assert: IsPropertyKey(P) is true.
Assert(IsPropertyKey(P));
// 2. Assert: O is an Integer-Indexed exotic object.
Assert(isIntegerIndexedExoticObject(O));
// 3. If Type(P) is String, then
if (Type(P) === 'String') {
// a. Let numericIndex be ! CanonicalNumericIndexString(P).
const numericIndex = X(CanonicalNumericIndexString(P));
// b. If numericIndex is not undefined, then
if (numericIndex !== Value.undefined) {
// i. Let value be ? IntegerIndexedElementGet(O, numericIndex).
const value = Q(IntegerIndexedElementGet(O, numericIndex));
// ii. If value is undefined, return undefined.
if (value === Value.undefined) {
return Value.undefined;
}
// iii. Return the PropertyDescriptor { [[Value]]: value, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: false }.
return Descriptor({
Value: value,
Writable: Value.true,
Enumerable: Value.true,
Configurable: Value.false,
});
}
}
// 4. Return OrdinaryGetOwnProperty(O, P).
return OrdinaryGetOwnProperty(O, P);
}
// 9.4.5.2 #sec-integer-indexed-exotic-objects-hasproperty-p
export function IntegerIndexedHasProperty(P) {
const O = this;
// 1. Assert: IsPropertyKey(P) is true.
Assert(IsPropertyKey(P));
// 2. Assert: O is an Integer-Indexed exotic object.
Assert(isIntegerIndexedExoticObject(O));
// 3. If Type(P) is String, then
if (Type(P) === 'String') {
// a. Let numericIndex be ! CanonicalNumericIndexString(P).
const numericIndex = X(CanonicalNumericIndexString(P));
// b. If numericIndex is not undefined, then
if (numericIndex !== Value.undefined) {
// i. Let buffer be O.[[ViewedArrayBuffer]].
const buffer = O.ViewedArrayBuffer;
// ii. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
if (IsDetachedBuffer(buffer) === Value.true) {
return surroundingAgent.Throw('TypeError', 'ArrayBufferDetached');
}
// iii. If ! IsValidIntegerIndex(O, numericIndex) is false, return false.
if (IsValidIntegerIndex(O, numericIndex) === Value.false) {
return Value.false;
}
// iv. Return true.
return Value.true;
}
}
// 4. Return ? OrdinaryHasProperty(O, P)
return Q(OrdinaryHasProperty(O, P));
}
// #sec-integer-indexed-exotic-objects-defineownproperty-p-desc
export function IntegerIndexedDefineOwnProperty(P, Desc) {
const O = this;
// 1. Assert: IsPropertyKey(P) is true.
Assert(IsPropertyKey(P));
// 2. Assert: O is an Integer-Indexed exotic object.
Assert(isIntegerIndexedExoticObject(O));
// 3. If Type(P) is String, then
if (Type(P) === 'String') {
// a. Let numericIndex be ! CanonicalNumericIndexString(P).
const numericIndex = X(CanonicalNumericIndexString(P));
// b. If numericIndex is not undefined, then
if (numericIndex !== Value.undefined) {
// i. If ! IsValidIntegerIndex(O, numericIndex) is false, return false.
if (IsValidIntegerIndex(O, numericIndex) === Value.false) {
return Value.false;
}
// ii. If IsAccessorDescriptor(Desc) is true, return false.
if (IsAccessorDescriptor(Desc)) {
return Value.false;
}
// iii. If Desc has a [[Configurable]] field and if Desc.[[Configurable]] is true, return false.
if (Desc.Configurable === Value.true) {
return Value.false;
}
// iv. If Desc has an [[Enumerable]] field and if Desc.[[Enumerable]] is false, return false.
if (Desc.Enumerable === Value.false) {
return Value.false;
}
// v. If Desc has a [[Writable]] field and if Desc.[[Writable]] is false, return false.
if (Desc.Writable === Value.false) {
return Value.false;
}
// vi. If Desc has a [[Value]] field, then
if (Desc.Value !== undefined) {
// 1. Let value be Desc.[[Value]].
const value = Desc.Value;
// 2. Return ? IntegerIndexedElementSet(O, numericIndex, value).
return Q(IntegerIndexedElementSet(O, numericIndex, value));
}
// vii. Return true.
return Value.true;
}
}
// 4. Return ! OrdinaryDefineOwnProperty(O, P, Desc).
return Q(OrdinaryDefineOwnProperty(O, P, Desc));
}
// 9.4.5.4 #sec-integer-indexed-exotic-objects-get-p-receiver
export function IntegerIndexedGet(P, Receiver) {
const O = this;
// 1. Assert: IsPropertykey(P) is true.
Assert(IsPropertyKey(P));
// 2. If Type(P) is String, then
if (Type(P) === 'String') {
// a. Let numericIndex be ! CanonicalNumericIndexString(P).
const numericIndex = X(CanonicalNumericIndexString(P));
// b. If numericIndex is not undefined, then
if (numericIndex !== Value.undefined) {
// i. Return ? IntegerIndexedElementGet(O, numericIndex).
return Q(IntegerIndexedElementGet(O, numericIndex));
}
}
// 3. Return ? OrdinaryGet(O, P, Receiver).
return Q(OrdinaryGet(O, P, Receiver));
}
// 9.4.5.5 #sec-integer-indexed-exotic-objects-set-p-v-receiver
export function IntegerIndexedSet(P, V, Receiver) {
const O = this;
// 1. Assert: IsPropertyKey(P) is true.
Assert(IsPropertyKey(P));
// 2. If Type(P) is String, then
if (Type(P) === 'String') {
// a. Let numericIndex be ! CanonicalNumericIndexString(P).
const numericIndex = X(CanonicalNumericIndexString(P));
// b. If numericIndex is not undefined, then
if (numericIndex !== Value.undefined) {
// i. Return ? IntegerIndexedElementSet(O, numericIndex, V).
return Q(IntegerIndexedElementSet(O, numericIndex, V));
}
}
// 3. Return ? OrdinarySet(O, P, V, Receiver).
return Q(OrdinarySet(O, P, V, Receiver));
}
// 9.4.5.6 #sec-integer-indexed-exotic-objects-ownpropertykeys
export function IntegerIndexedOwnPropertyKeys() {
const O = this;
// 1. Let keys be a new empty List.
const keys = [];
// 2. Assert: O is an Integer-Indexed exotic object.
Assert(isIntegerIndexedExoticObject(O));
// 3. Let len be O.[[ArrayLength]].
const len = O.ArrayLength.numberValue();
// 4. For each integer i starting with 0 such that i < len, in ascending order, do
for (let i = 0; i < len; i += 1) {
// a. Add ! ToString(i) as the last element of keys.
keys.push(X(ToString(new Value(i))));
}
// 5. For each own property key P of O such that Type(P) is String and P is not an integer index, in ascending chronological order of property creation, do
for (const P of O.properties.keys()) {
if (Type(P) === 'String') {
if (!isIntegerIndex(P)) {
// a. Add P as the last element of keys.
keys.push(P);
}
}
}
// 6. For each own property key P of O such that Type(P) is Symbol, in ascending chronological order of property creation, do
for (const P of O.properties.keys()) {
if (Type(P) === 'Symbol') {
// a. Add P as the last element of keys.
keys.push(P);
}
}
// 7. Return keys.
return keys;
}
// #sec-integerindexedelementget
export function IntegerIndexedElementGet(O, index) {
// 1. Assert: O is an Integer-Indexed exotic object.
Assert(isIntegerIndexedExoticObject(O));
// 2. Assert: Type(index) is Number.
Assert(Type(index) === 'Number');
// 3. Let buffer be O.[[ViewedArrayBuffer]].
const buffer = O.ViewedArrayBuffer;
// 4. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
if (IsDetachedBuffer(buffer) === Value.true) {
return surroundingAgent.Throw('TypeError', 'ArrayBufferDetached');
}
// 5. If ! IsValidIntegerIndex(O, index) is false, return undefined.
if (IsValidIntegerIndex(O, index) === Value.false) {
return Value.undefined;
}
// 6. Let offset be O.[[ByteOffset]].
const offset = O.ByteOffset;
// 7. Let arrayTypeName be the String value of O.[[TypedArrayName]].
const arrayTypeName = O.TypedArrayName.stringValue();
// 8. Let elementSize be the Element Size value specified in Table 61 for arrayTypeName.
const elementSize = typedArrayInfoByName[arrayTypeName].ElementSize;
// 9. Let indexedPosition be (index × elementSize) + offset.
const indexedPosition = new Value((index.numberValue() * elementSize) + offset.numberValue());
// 10. Let elementType be the Element Type value in Table 61 for arrayTypeName.
const elementType = typedArrayInfoByName[arrayTypeName].ElementType;
// 11. Return GetValueFromBuffer(buffer, indexedPosition, elementType, true, Unordered).
return GetValueFromBuffer(buffer, indexedPosition, elementType, Value.true, 'Unordered');
}
// #sec-integerindexedelementset
export function IntegerIndexedElementSet(O, index, value) {
// 1. Assert: O is an Integer-Indexed exotic object.
Assert(isIntegerIndexedExoticObject(O));
// 2. Assert: Type(index) is Number.
Assert(Type(index) === 'Number');
// 3. If O.[[ContentType]] is BigInt, let numValue be ? ToBigInt(value).
// 4. Otherwise, let numValue be ? ToNumber(value).
let numValue;
if (O.ContentType === 'BigInt') {
numValue = Q(ToBigInt(value));
} else {
numValue = Q(ToNumber(value));
}
// 5. Let buffer be O.[[ViewedArrayBuffer]].
const buffer = O.ViewedArrayBuffer;
// 6. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
if (IsDetachedBuffer(buffer) === Value.true) {
return surroundingAgent.Throw('TypeError', 'ArrayBufferDetached');
}
// 7. If ! IsValidIntegerIndex(O, index) is false, return false.
if (IsValidIntegerIndex(O, index) === Value.false) {
return Value.false;
}
// 8. Let offset be O.[[ByteOffset]].
const offset = O.ByteOffset;
// 9. Let arrayTypeName be the String value of O.[[TypedArrayName]].
const arrayTypeName = O.TypedArrayName.stringValue();
// 10. Let elementSize be the Element Size value specified in Table 61 for arrayTypeName.
const elementSize = typedArrayInfoByName[arrayTypeName].ElementSize;
// 11. Let indexedPosition be (index × elementSize) + offset.
const indexedPosition = new Value((index.numberValue() * elementSize) + offset.numberValue());
// 12. Let elementType be the Element Type value in Table 61 for arrayTypeName.
const elementType = typedArrayInfoByName[arrayTypeName].ElementType;
// 13. Perform SetValueInBuffer(buffer, indexedPosition, elementType, numValue, true, Unordered).
X(SetValueInBuffer(buffer, indexedPosition, elementType, numValue, Value.true, 'Unordered'));
// 14. Return true.
return Value.true;
}
// #sec-integerindexedobjectcreate
export function IntegerIndexedObjectCreate(prototype) {
// 1. Let internalSlotsList be « [[Prototype]], [[Extensible]], [[ViewedArrayBuffer]], [[TypedArrayName]], [[ContentType]], [[ByteLength]], [[ByteOffset]], [[ArrayLength]] ».
const internalSlotsList = [
'Prototype',
'Extensible',
'ViewedArrayBuffer',
'TypedArrayName',
'ContentType',
'ByteLength',
'ByteOffset',
'ArrayLength',
];
// 2. Let A be ! MakeBasicObject(internalSlotsList).
const A = X(MakeBasicObject(internalSlotsList));
// 3. Set A.[[GetOwnProperty]] as specified in 9.4.5.1.
A.GetOwnProperty = IntegerIndexedGetOwnProperty;
// 4. Set A.[[HasProperty]] as specified in 9.4.5.2.
A.HasProperty = IntegerIndexedHasProperty;
// 5. Set A.[[DefineOwnProperty]] as specified in 9.4.5.3.
A.DefineOwnProperty = IntegerIndexedDefineOwnProperty;
// 6. Set A.[[Get]] as specified in 9.4.5.4.
A.Get = IntegerIndexedGet;
// 7. Set A.[[Set]] as specified in 9.4.5.5.
A.Set = IntegerIndexedSet;
// 8. Set A.[[OwnPropertyKeys]] as specified in 9.4.5.6.
A.OwnPropertyKeys = IntegerIndexedOwnPropertyKeys;
// 9. Set A.[[Prototype]] to prototype.
A.Prototype = prototype;
// 10. Return A.
return A;
}
@@ -1,255 +0,0 @@
import { surroundingAgent } from '../engine.mjs';
import {
Type,
Value,
wellKnownSymbols,
} from '../value.mjs';
import {
Completion,
EnsureCompletion,
IfAbruptRejectPromise,
Q, X,
Await,
} from '../completion.mjs';
import {
Assert,
Call,
CreateBuiltinFunction,
CreateDataProperty,
Get,
GetMethod,
GetV,
PromiseResolve,
OrdinaryObjectCreate,
PerformPromiseThen,
ToBoolean,
} from './all.mjs';
// This file covers abstract operations defined in
// 7.4 #sec-operations-on-iterator-objects
// and
// 25.1 #sec-iteration
// 7.4.1 #sec-getiterator
export function GetIterator(obj, hint, method) {
if (!hint) {
hint = 'sync';
}
Assert(hint === 'sync' || hint === 'async');
if (!method) {
if (hint === 'async') {
method = Q(GetMethod(obj, wellKnownSymbols.asyncIterator));
if (method === Value.undefined) {
const syncMethod = Q(GetMethod(obj, wellKnownSymbols.iterator));
const syncIteratorRecord = Q(GetIterator(obj, 'sync', syncMethod));
return Q(CreateAsyncFromSyncIterator(syncIteratorRecord));
}
} else {
method = Q(GetMethod(obj, wellKnownSymbols.iterator));
}
}
const iterator = Q(Call(method, obj));
if (Type(iterator) !== 'Object') {
return surroundingAgent.Throw('TypeError', 'NotAnObject', iterator);
}
const nextMethod = Q(GetV(iterator, new Value('next')));
const iteratorRecord = {
Iterator: iterator,
NextMethod: nextMethod,
Done: Value.false,
};
return EnsureCompletion(iteratorRecord);
}
// 7.4.2 #sec-iteratornext
export function IteratorNext(iteratorRecord, value) {
let result;
if (!value) {
result = Q(Call(iteratorRecord.NextMethod, iteratorRecord.Iterator));
} else {
result = Q(Call(iteratorRecord.NextMethod, iteratorRecord.Iterator, [value]));
}
if (Type(result) !== 'Object') {
return surroundingAgent.Throw('TypeError', 'NotAnObject', result);
}
return EnsureCompletion(result);
}
// 7.4.3 #sec-iteratorcomplete
export function IteratorComplete(iterResult) {
Assert(Type(iterResult) === 'Object');
return EnsureCompletion(ToBoolean(Q(Get(iterResult, new Value('done')))));
}
// 7.4.4 #sec-iteratorvalue
export function IteratorValue(iterResult) {
Assert(Type(iterResult) === 'Object');
return EnsureCompletion(Q(Get(iterResult, new Value('value'))));
}
// 7.4.5 #sec-iteratorstep
export function IteratorStep(iteratorRecord) {
const result = Q(IteratorNext(iteratorRecord));
const done = Q(IteratorComplete(result));
if (done === Value.true) {
return EnsureCompletion(Value.false);
}
return EnsureCompletion(result);
}
// #sec-iteratorclose
export function IteratorClose(iteratorRecord, completion) {
// 1. Assert: Type(iteratorRecord.[[Iterator]]) is Object.
Assert(Type(iteratorRecord.Iterator) === 'Object');
// 2. Assert: completion is a Completion Record.
// TODO: completion should be a Completion Record so this should not be necessary
completion = EnsureCompletion(completion);
Assert(completion instanceof Completion);
// 3. Let iterator be iteratorRecord.[[Iterator]].
const iterator = iteratorRecord.Iterator;
// 4. Let innerResult be GetMethod(iterator, "return").
let innerResult = EnsureCompletion(GetMethod(iterator, new Value('return')));
// 5. If innerResult.[[Type]] is normal, then
if (innerResult.Type === 'normal') {
// a. Let return be innerResult.[[Value]].
const ret = innerResult.Value;
// b. If return is undefined, return Completion(completion).
if (ret === Value.undefined) {
return Completion(completion);
}
// c. Set innerResult to Call(return, iterator).
innerResult = Call(ret, iterator);
}
// 6. If completion.[[Type]] is throw, return Completion(completion).
if (completion.Type === 'throw') {
return Completion(completion);
}
// 7. If innerResult.[[Type]] is throw, return Completion(innerResult).
if (innerResult.Type === 'throw') {
return Completion(innerResult);
}
// 8. If Type(innerResult.[[Value]]) is not Object, throw a TypeError exception.
if (Type(innerResult.Value) !== 'Object') {
return surroundingAgent.Throw('TypeError', 'NotAnObject', innerResult.Value);
}
// 9. Return Completion(completion).
return Completion(completion);
}
// #sec-asynciteratorclose
export function* AsyncIteratorClose(iteratorRecord, completion) {
// 1. Assert: Type(iteratorRecord.[[Iterator]]) is Object.
Assert(Type(iteratorRecord.Iterator) === 'Object');
// 2. Assert: completion is a Completion Record.
Assert(completion instanceof Completion);
// 3. Let iterator be iteratorRecord.[[Iterator]].
const iterator = iteratorRecord.Iterator;
// 4. Let innerResult be GetMethod(iterator, "return").
let innerResult = EnsureCompletion(GetMethod(iterator, new Value('return')));
// 5. If innerResult.[[Type]] is normal, then
if (innerResult.Type === 'normal') {
// a. Let return be innerResult.[[Value]].
const ret = innerResult.Value;
// b. If return is undefined, return Completion(completion).
if (ret === Value.undefined) {
return Completion(completion);
}
// c. Set innerResult to Call(return, iterator).
innerResult = Call(ret, iterator);
// d. If innerResult.[[Type]] is normal, set innerResult to Await(innerResult.[[Value]]).
if (innerResult.Type === 'normal') {
innerResult = EnsureCompletion(yield* Await(innerResult.Value));
}
}
// 6. If completion.[[Type]] is throw, return Completion(completion).
if (completion.Type === 'throw') {
return Completion(completion);
}
// 7. If innerResult.[[Type]] is throw, return Completion(innerResult).
if (innerResult.Type === 'throw') {
return Completion(innerResult);
}
// 8. If Type(innerResult.[[Value]]) is not Object, throw a TypeError exception.
if (Type(innerResult.Value) !== 'Object') {
return surroundingAgent.Throw('TypeError', 'NotAnObject', innerResult.Value);
}
// 9. Return Completion(completion).
return Completion(completion);
}
// 7.4.8 #sec-createiterresultobject
export function CreateIterResultObject(value, done) {
Assert(Type(done) === 'Boolean');
const obj = OrdinaryObjectCreate(surroundingAgent.intrinsic('%Object.prototype%'));
X(CreateDataProperty(obj, new Value('value'), value));
X(CreateDataProperty(obj, new Value('done'), done));
return obj;
}
// 7.4.9 #sec-createlistiteratorRecord
export function CreateListIteratorRecord(list) {
const iterator = OrdinaryObjectCreate(surroundingAgent.intrinsic('%IteratorPrototype%'), [
'IteratedList',
'ListNextIndex',
]);
iterator.IteratedList = list;
iterator.ListNextIndex = 0;
const steps = ListIteratorNextSteps;
const next = X(CreateBuiltinFunction(steps, []));
return {
Iterator: iterator,
NextMethod: next,
Done: Value.false,
};
}
// 7.4.9.1 #sec-listiterator-next
function ListIteratorNextSteps(args, { thisValue }) {
const O = thisValue;
Assert(Type(O) === 'Object');
Assert('IteratedList' in O);
const list = O.IteratedList;
const index = O.ListNextIndex;
const len = list.length;
if (index >= len) {
return CreateIterResultObject(Value.undefined, Value.true);
}
O.ListNextIndex += 1;
return CreateIterResultObject(list[index], Value.false);
}
// 25.1.4.1 #sec-createasyncfromsynciterator
export function CreateAsyncFromSyncIterator(syncIteratorRecord) {
const asyncIterator = X(OrdinaryObjectCreate(surroundingAgent.intrinsic('%AsyncFromSyncIteratorPrototype%'), [
'SyncIteratorRecord',
]));
asyncIterator.SyncIteratorRecord = syncIteratorRecord;
const nextMethod = X(Get(asyncIterator, new Value('next')));
return {
Iterator: asyncIterator,
NextMethod: nextMethod,
Done: Value.false,
};
}
// 25.1.4.2.4 #sec-async-from-sync-iterator-value-unwrap-functions
function AsyncFromSyncIteratorValueUnwrapFunctions([value = Value.undefined]) {
const F = this;
return X(CreateIterResultObject(value, F.Done));
}
// 25.1.4.4 #sec-asyncfromsynciteratorcontinuation
export function AsyncFromSyncIteratorContinuation(result, promiseCapability) {
const done = IteratorComplete(result);
IfAbruptRejectPromise(done, promiseCapability);
const value = IteratorValue(result);
IfAbruptRejectPromise(value, promiseCapability);
const valueWrapper = PromiseResolve(surroundingAgent.intrinsic('%Promise%'), value);
IfAbruptRejectPromise(valueWrapper, promiseCapability);
const steps = AsyncFromSyncIteratorValueUnwrapFunctions;
const onFulfilled = X(CreateBuiltinFunction(steps, ['Done']));
onFulfilled.Done = done;
X(PerformPromiseThen(valueWrapper, onFulfilled, Value.undefined, promiseCapability));
return promiseCapability.Promise;
}
@@ -1,218 +0,0 @@
import { surroundingAgent } from '../engine.mjs';
import { Q, X } from '../completion.mjs';
import { AbstractModuleRecord, ResolvedBindingRecord } from '../modules.mjs';
import {
Type,
Value,
Descriptor,
wellKnownSymbols,
} from '../value.mjs';
import { ValueSet } from '../helpers.mjs';
import {
Assert,
SortCompare,
SameValue,
MakeBasicObject,
IsPropertyKey,
IsAccessorDescriptor,
SetImmutablePrototype,
OrdinaryGetOwnProperty,
OrdinaryDefineOwnProperty,
OrdinaryHasProperty,
OrdinaryGet,
OrdinaryDelete,
OrdinaryOwnPropertyKeys,
GetModuleNamespace,
} from './all.mjs';
function ModuleNamespaceSetPrototypeOf(V) {
const O = this;
return Q(SetImmutablePrototype(O, V));
}
function ModuleNamespaceIsExtensible() {
return Value.false;
}
function ModuleNamespacePreventExtensions() {
return Value.true;
}
function ModuleNamespaceGetOwnProperty(P) {
const O = this;
if (Type(P) === 'Symbol') {
return OrdinaryGetOwnProperty(O, P);
}
const exports = O.Exports;
if (!exports.has(P)) {
return Value.undefined;
}
const value = Q(O.Get(P, O));
return Descriptor({
Value: value,
Writable: Value.true,
Enumerable: Value.true,
Configurable: Value.false,
});
}
function ModuleNamespaceDefineOwnProperty(P, Desc) {
const O = this;
if (Type(P) === 'Symbol') {
return OrdinaryDefineOwnProperty(O, P, Desc);
}
const current = Q(O.GetOwnProperty(P));
if (current === Value.undefined) {
return Value.false;
}
if (IsAccessorDescriptor(Desc)) {
return Value.false;
}
if (Desc.Writable !== undefined && Desc.Writable === Value.false) {
return Value.false;
}
if (Desc.Enumerable !== undefined && Desc.Enumerable === Value.false) {
return Value.false;
}
if (Desc.Configurable !== undefined && Desc.Configurable === Value.true) {
return Value.false;
}
if (Desc.Value !== undefined) {
return SameValue(Desc.Value, current.Value);
}
return Value.true;
}
function ModuleNamespaceHasProperty(P) {
const O = this;
if (Type(P) === 'Symbol') {
return OrdinaryHasProperty(O, P);
}
const exports = O.Exports;
if (exports.has(P)) {
return Value.true;
}
return Value.false;
}
// #sec-module-namespace-exotic-objects-get-p-receiver
function ModuleNamespaceGet(P, Receiver) {
const O = this;
// 1. Assert: IsPropertyKey(P) is true.
Assert(IsPropertyKey(P));
// 2. If Type(P) is Symbol, then
if (Type(P) === 'Symbol') {
// a. Return ? OrdinaryGet(O, P, Receiver).
return OrdinaryGet(O, P, Receiver);
}
// 3. Let exports be O.[[Exports]].
const exports = O.Exports;
// 4. If P is not an element of exports, return undefined.
if (!exports.has(P)) {
return Value.undefined;
}
// 5. Let m be O.[[Module]].
const m = O.Module;
// 6. Let binding be ! m.ResolveExport(P).
const binding = m.ResolveExport(P);
// 7. Assert: binding is a ResolvedBinding Record.
Assert(binding instanceof ResolvedBindingRecord);
// 8. Let targetModule be binding.[[Module]].
const targetModule = binding.Module;
// 9. Assert: targetModule is not undefined.
Assert(targetModule !== Value.undefined);
// 10. If binding.[[BindingName]] is ~namespace~, then
if (binding.BindingName === 'namespace') {
// a. Return ? GetModuleNamespace(targetModule).
return Q(GetModuleNamespace(targetModule));
}
// 11. Let targetEnv be targetModule.[[Environment]].
const targetEnv = targetModule.Environment;
// 12. If targetEnv is undefined, throw a ReferenceError exception.
if (targetEnv === Value.undefined) {
return surroundingAgent.Throw('ReferenceError', 'NotDefined', P);
}
// 13. Return ? targetEnv.GetBindingValue(binding.[[BindingName]], true).
return Q(targetEnv.GetBindingValue(binding.BindingName, Value.true));
}
function ModuleNamespaceSet() {
return Value.false;
}
function ModuleNamespaceDelete(P) {
const O = this;
Assert(IsPropertyKey(P));
if (Type(P) === 'Symbol') {
return Q(OrdinaryDelete(O, P));
}
const exports = O.Exports;
if (exports.has(P)) {
return Value.false;
}
return Value.true;
}
function ModuleNamespaceOwnPropertyKeys() {
const O = this;
const exports = [...O.Exports];
const symbolKeys = X(OrdinaryOwnPropertyKeys(O));
exports.push(...symbolKeys);
return exports;
}
// 9.4.6.11 #sec-modulenamespacecreate
export function ModuleNamespaceCreate(module, exports) {
// 1. Assert: module is a Module Record.
Assert(module instanceof AbstractModuleRecord);
// 2. Assert: module.[[Namespace]] is undefined.
Assert(module.Namespace === Value.undefined);
// 3. Assert: exports is a List of String values.
Assert(Array.isArray(exports));
// 4. Let internalSlotsList be the internal slots listed in Table 31.
const internalSlotsList = ['Module', 'Exports', 'Prototype'];
// 5. Let M be ! MakeBasicObject(internalSlotsList).
const M = X(MakeBasicObject(internalSlotsList));
// 6. Set M's essential internal methods to the definitions specified in #sec-module-namespace-exotic-objects
M.SetPrototypeOf = ModuleNamespaceSetPrototypeOf;
M.IsExtensible = ModuleNamespaceIsExtensible;
M.PreventExtensions = ModuleNamespacePreventExtensions;
M.GetOwnProperty = ModuleNamespaceGetOwnProperty;
M.DefineOwnProperty = ModuleNamespaceDefineOwnProperty;
M.HasProperty = ModuleNamespaceHasProperty;
M.Get = ModuleNamespaceGet;
M.Set = ModuleNamespaceSet;
M.Delete = ModuleNamespaceDelete;
M.OwnPropertyKeys = ModuleNamespaceOwnPropertyKeys;
// 7. Set M.[[Prototype]] to null.
M.Prototype = Value.null;
// 8. Set M.[[Module]] to module.
M.Module = module;
// 9. Let sortedExports be a new List containing the same values as the list exports where the values are ordered as if an Array of the same values had been sorted using Array.prototype.sort using undefined as comparefn.
const sortedExports = [...exports].sort((x, y) => {
const result = X(SortCompare(x, y, Value.undefined));
return result.numberValue();
});
// 10. Set M.[[Exports]] to sortedExports.
M.Exports = new ValueSet(sortedExports);
// 11. Create own properties of M corresponding to the definitions in 26.3.
M.properties.set(wellKnownSymbols.toStringTag, Descriptor({
Writable: Value.false,
Enumerable: Value.false,
Configurable: Value.false,
Value: new Value('Module'),
}));
// 12. Set module.[[Namespace]] to M.
module.Namespace = M;
// 13. Return M;
return M;
}
@@ -1,263 +0,0 @@
import { surroundingAgent, HostResolveImportedModule } from '../engine.mjs';
import {
AbstractModuleRecord,
CyclicModuleRecord,
ResolvedBindingRecord,
} from '../modules.mjs';
import { Value } from '../value.mjs';
import {
Q, X, NormalCompletion, ThrowCompletion,
} from '../completion.mjs';
import {
Assert,
ModuleNamespaceCreate,
NewPromiseCapability,
PerformPromiseThen,
CreateBuiltinFunction,
Call,
} from './all.mjs';
// 15.2.1.16.1.1 #sec-InnerModuleLinking
export function InnerModuleLinking(module, stack, index) {
if (!(module instanceof CyclicModuleRecord)) {
Q(module.Link());
return index;
}
if (module.Status === 'linking' || module.Status === 'linked' || module.Status === 'evaluated') {
return index;
}
Assert(module.Status === 'unlinked');
module.Status = 'linking';
module.DFSIndex = index;
module.DFSAncestorIndex = index;
index += 1;
stack.push(module);
for (const required of module.RequestedModules) {
const requiredModule = Q(HostResolveImportedModule(module, required));
index = Q(InnerModuleLinking(requiredModule, stack, index));
if (requiredModule instanceof CyclicModuleRecord) {
Assert(requiredModule.Status === 'linking' || requiredModule.Status === 'linked' || requiredModule.Status === 'evaluated');
Assert((requiredModule.Status === 'linking') === stack.includes(requiredModule));
if (requiredModule.Status === 'linking') {
module.DFSAncestorIndex = Math.min(module.DFSAncestorIndex, requiredModule.DFSAncestorIndex);
}
}
}
Q(module.InitializeEnvironment());
Assert(stack.indexOf(module) === stack.lastIndexOf(module));
Assert(module.DFSAncestorIndex <= module.DFSIndex);
if (module.DFSAncestorIndex === module.DFSIndex) {
let done = false;
while (done === false) {
const requiredModule = stack.pop();
Assert(requiredModule instanceof CyclicModuleRecord);
requiredModule.Status = 'linked';
if (requiredModule === module) {
done = true;
}
}
}
return index;
}
// 15.2.1.16.2.1 #sec-innermoduleevaluation
export function InnerModuleEvaluation(module, stack, index) {
if (!(module instanceof CyclicModuleRecord)) {
Q(module.Evaluate());
return index;
}
if (module.Status === 'evaluated') {
if (module.EvaluationError === Value.undefined) {
return index;
} else {
return module.EvaluationError;
}
}
if (module.Status === 'evaluating') {
return index;
}
Assert(module.Status === 'linked');
module.Status = 'evaluating';
module.DFSIndex = index;
module.DFSAncestorIndex = index;
module.PendingAsyncDependencies = 0;
module.AsyncParentModules = [];
index += 1;
stack.push(module);
for (const required of module.RequestedModules) {
let requiredModule = X(HostResolveImportedModule(module, required));
index = Q(InnerModuleEvaluation(requiredModule, stack, index));
if (requiredModule instanceof CyclicModuleRecord) {
Assert(requiredModule.Status === 'evaluating' || requiredModule.Status === 'evaluated');
if (stack.includes(requiredModule)) {
Assert(requiredModule.Status === 'evaluating');
}
if (requiredModule.Status === 'evaluating') {
module.DFSAncestorIndex = Math.min(module.DFSAncestorIndex, requiredModule.DFSAncestorIndex);
} else {
requiredModule = GetAsyncCycleRoot(requiredModule);
Assert(requiredModule.Status === 'evaluated');
if (requiredModule.EvaluationError !== Value.undefined) {
return module.EvaluationError;
}
}
if (requiredModule.AsyncEvaluating === Value.true) {
module.PendingAsyncDependencies += 1;
requiredModule.AsyncParentModules.push(module);
}
}
}
if (module.PendingAsyncDependencies > 0) {
module.AsyncEvaluating = Value.true;
} else if (module.Async === Value.true) {
X(ExecuteAsyncModule(module));
} else {
Q(module.ExecuteModule());
}
Assert(stack.indexOf(module) === stack.lastIndexOf(module));
Assert(module.DFSAncestorIndex <= module.DFSIndex);
if (module.DFSAncestorIndex === module.DFSIndex) {
let done = false;
while (done === false) {
const requiredModule = stack.pop();
Assert(requiredModule instanceof CyclicModuleRecord);
requiredModule.Status = 'evaluated';
if (requiredModule === module) {
done = true;
}
}
}
return index;
}
// https://tc39.es/proposal-top-level-await/#sec-execute-async-module
function ExecuteAsyncModule(module) {
Assert(module.Status === 'evaluating' || module.Status === 'evaluated');
Assert(module.Async === Value.true);
module.AsyncEvaluating = Value.true;
const capability = X(NewPromiseCapability(surroundingAgent.intrinsic('%Promise%')));
const stepsFulfilled = CallAsyncModuleFulfilled;
const onFulfilled = CreateBuiltinFunction(stepsFulfilled, ['Module']);
onFulfilled.Module = module;
const stepsRejected = CallAsyncModuleRejected;
const onRejected = CreateBuiltinFunction(stepsRejected, ['Module']);
onRejected.Module = module;
X(PerformPromiseThen(capability.Promise, onFulfilled, onRejected));
X(module.ExecuteModule(capability));
return Value.undefined;
}
// https://tc39.es/proposal-top-level-await/#sec-execute-async-module
function CallAsyncModuleFulfilled() {
const f = surroundingAgent.activeFunctionObject;
const module = f.Module;
X(AsyncModuleExecutionFulfilled(module));
return Value.undefined;
}
// https://tc39.es/proposal-top-level-await/#sec-execute-async-module
function CallAsyncModuleRejected([error = Value.undefined]) {
const f = surroundingAgent.activeFunctionObject;
const module = f.Module;
X(AsyncModuleExecutionRejected(module, error));
return Value.undefined;
}
// https://tc39.es/proposal-top-level-await/#sec-getcycleroot
export function GetAsyncCycleRoot(module) {
Assert(module.Status === 'evaluated');
if (module.AsyncParentModules.length === 0) {
return module;
}
while (module.DFSIndex > module.DFSAncestorIndex) {
Assert(module.AsyncParentModules.length > 0);
const nextCycleModule = module.AsyncParentModules[0];
Assert(nextCycleModule.DFSAncestorIndex === module.DFSAncestorIndex);
module = nextCycleModule;
}
Assert(module.DFSIndex === module.DFSAncestorIndex);
return module;
}
// https://tc39.es/proposal-top-level-await/#sec-asyncmodulexecutionfulfilled
function AsyncModuleExecutionFulfilled(module) {
Assert(module.Status === 'evaluated');
if (module.AsyncEvaluating === Value.false) {
Assert(module.EvaluationError !== Value.undefined);
return Value.undefined;
}
Assert(module.EvaluationError === Value.undefined);
module.AsyncEvaluating = Value.false;
for (const m of module.AsyncParentModules) {
if (module.DFSIndex !== module.DFSAncestorIndex) {
Assert(m.DFSAncestorIndex === module.DFSAncestorIndex);
}
m.PendingAsyncDependencies -= 1;
if (m.PendingAsyncDependencies === 0 && m.EvaluationError === Value.undefined) {
Assert(m.AsyncEvaluating === Value.true);
const cycleRoot = X(GetAsyncCycleRoot(m));
if (cycleRoot.EvaluationError !== Value.undefined) {
return Value.undefined;
}
if (m.Async === Value.true) {
X(ExecuteAsyncModule(m));
} else {
const result = m.ExecuteModule();
if (result instanceof NormalCompletion) {
X(AsyncModuleExecutionFulfilled(m));
} else {
X(AsyncModuleExecutionRejected(m, result.Value));
}
}
}
}
if (module.TopLevelCapability !== Value.undefined) {
Assert(module.DFSIndex === module.DFSAncestorIndex);
X(Call(module.TopLevelCapability.Resolve, Value.undefined, [Value.undefined]));
}
return Value.undefined;
}
// https://tc39.es/proposal-top-level-await/#sec-AsyncModuleExecutionRejected
function AsyncModuleExecutionRejected(module, error) {
Assert(module.Status === 'evaluated');
if (module.AsyncEvaluating === Value.false) {
Assert(module.EvaluationError !== Value.undefined);
return Value.undefined;
}
Assert(module.EvaluationError === Value.undefined);
module.EvaluationError = ThrowCompletion(error);
module.AsyncEvaluating = Value.false;
for (const m of module.AsyncParentModules) {
if (module.DFSIndex !== module.DFSAncestorIndex) {
Assert(m.DFSAncestorIndex === module.DFSAncestorIndex);
}
X(AsyncModuleExecutionRejected(m, error));
}
if (module.TopLevelCapability !== Value.undefined) {
Assert(module.DFSIndex === module.DFSAncestorIndex);
X(Call(module.TopLevelCapability.Reject, Value.undefined, [error]));
}
return Value.undefined;
}
// 15.2.1.21 #sec-getmodulenamespace
export function GetModuleNamespace(module) {
Assert(module instanceof AbstractModuleRecord);
if (module instanceof CyclicModuleRecord) {
Assert(module.Status !== 'unlinked');
}
let namespace = module.Namespace;
if (namespace === Value.undefined) {
const exportedNames = Q(module.GetExportedNames());
const unambiguousNames = [];
for (const name of exportedNames) {
const resolution = Q(module.ResolveExport(name));
if (resolution instanceof ResolvedBindingRecord) {
unambiguousNames.push(name);
}
}
namespace = ModuleNamespaceCreate(module, unambiguousNames);
}
return namespace;
}
@@ -1,48 +0,0 @@
import { surroundingAgent } from '../engine.mjs';
import { Type } from '../value.mjs';
export function Assert(invariant, source) {
/* istanbul ignore next */
if (!invariant) {
throw new TypeError(`Assert failed${source ? `: ${source}` : ''}`.trim());
}
}
// 9.1.15 #sec-requireinternalslot
export function RequireInternalSlot(O, internalSlot) {
if (Type(O) !== 'Object') {
return surroundingAgent.Throw('TypeError', 'NotAnObject', O);
}
if (!(internalSlot in O)) {
return surroundingAgent.Throw('TypeError', 'InternalSlotMissing', O, internalSlot);
}
}
export function sourceTextMatchedBy(node) {
return node.sourceText();
}
// An ECMAScript Script syntactic unit may be processed using either unrestricted or strict mode syntax and semantics.
// Code is interpreted as strict mode code in the following situations:
//
// - Global code is strict mode code if it begins with a Directive Prologue that contains a Use Strict Directive.
//
// - Module code is always strict mode code.
//
// - All parts of a ClassDeclaration or a ClassExpression are strict mode code.
//
// - Eval code is strict mode code if it begins with a Directive Prologue that contains a Use Strict Directive or
// if the call to eval is a direct eval that is contained in strict mode code.
//
// - Function code is strict mode code if the associated FunctionDeclaration, FunctionExpression, GeneratorDeclaration,
// GeneratorExpression, AsyncFunctionDeclaration, AsyncFunctionExpression, AsyncGeneratorDeclaration,
// AsyncGeneratorExpression, MethodDefinition, ArrowFunction, or AsyncArrowFunction is contained in strict mode code
// or if the code that produces the value of the function's [[ECMAScriptCode]] internal slot begins with a Directive
// Prologue that contains a Use Strict Directive.
//
// - Function code that is supplied as the arguments to the built-in Function, Generator, AsyncFunction, and
// AsyncGenerator constructors is strict mode code if the last argument is a String that when processed is a
// FunctionBody that begins with a Directive Prologue that contains a Use Strict Directive.
export function isStrictModeCode(node) {
return node.strict;
}
@@ -1,433 +0,0 @@
import {
Descriptor,
Type,
Value,
ObjectValue,
wellKnownSymbols,
} from '../value.mjs';
import {
surroundingAgent,
} from '../engine.mjs';
import { InstanceofOperator } from '../runtime-semantics/all.mjs';
import {
NormalCompletion,
EnsureCompletion,
Q, X,
} from '../completion.mjs';
import {
ArrayCreate,
Assert,
IsAccessorDescriptor,
IsCallable,
IsConstructor,
IsDataDescriptor,
IsExtensible,
IsPropertyKey,
SameValue,
ToLength,
ToObject,
ToString,
isProxyExoticObject,
} from './all.mjs';
// This file covers abstract operations defined in
// 7.3 #sec-operations-on-objects
// #sec-makebasicobject
export function MakeBasicObject(internalSlotsList) {
// 1. Assert: internalSlotsList is a List of internal slot names.
Assert(Array.isArray(internalSlotsList));
// 2. Let obj be a newly created object with an internal slot for each name in internalSlotsList.
// 3. Set obj's essential internal methods to the default ordinary object definitions specified in 9.1.
const obj = new ObjectValue(internalSlotsList);
internalSlotsList.forEach((s) => {
obj[s] = Value.undefined;
});
// 4. Assert: If the caller will not be overriding both obj's [[GetPrototypeOf]] and [[SetPrototypeOf]] essential internal methods, then internalSlotsList contains [[Prototype]].
// 5. Assert: If the caller will not be overriding all of obj's [[SetPrototypeOf]], [[IsExtensible]], and [[PreventExtensions]] essential internal methods, then internalSlotsList contains [[Extensible]].
// 6. If internalSlotsList contains [[Extensible]], then set obj.[[Extensible]] to true.
if (internalSlotsList.includes('Extensible')) {
obj.Extensible = Value.true;
}
// 7. Return obj.
return obj;
}
// 7.3.1 #sec-get-o-p
export function Get(O, P) {
Assert(Type(O) === 'Object');
Assert(IsPropertyKey(P));
// TODO: This should just return Q(O.Get(P, O))
return NormalCompletion(Q(O.Get(P, O)));
}
// 7.3.2 #sec-getv
export function GetV(V, P) {
Assert(IsPropertyKey(P));
const O = Q(ToObject(V));
return Q(O.Get(P, V));
}
// 7.3.3 #sec-set-o-p-v-throw
export function Set(O, P, V, Throw) {
Assert(Type(O) === 'Object');
Assert(IsPropertyKey(P));
Assert(Type(Throw) === 'Boolean');
const success = Q(O.Set(P, V, O));
if (success === Value.false && Throw === Value.true) {
return surroundingAgent.Throw('TypeError', 'CannotSetProperty', P, O);
}
return success;
}
// 7.3.4 #sec-createdataproperty
export function CreateDataProperty(O, P, V) {
Assert(Type(O) === 'Object');
Assert(IsPropertyKey(P));
const newDesc = Descriptor({
Value: V,
Writable: Value.true,
Enumerable: Value.true,
Configurable: Value.true,
});
return Q(O.DefineOwnProperty(P, newDesc));
}
// 7.3.5 #sec-createmethodproperty
export function CreateMethodProperty(O, P, V) {
Assert(Type(O) === 'Object');
Assert(IsPropertyKey(P));
const newDesc = Descriptor({
Value: V,
Writable: Value.true,
Enumerable: Value.false,
Configurable: Value.true,
});
return Q(O.DefineOwnProperty(P, newDesc));
}
// 7.3.6 #sec-createdatapropertyorthrow
export function CreateDataPropertyOrThrow(O, P, V) {
Assert(Type(O) === 'Object');
Assert(IsPropertyKey(P));
const success = Q(CreateDataProperty(O, P, V));
if (success === Value.false) {
return surroundingAgent.Throw('TypeError', 'CannotDefineProperty', P);
}
return success;
}
// 7.3.7 #sec-definepropertyorthrow
export function DefinePropertyOrThrow(O, P, desc) {
Assert(Type(O) === 'Object');
Assert(IsPropertyKey(P));
const success = Q(O.DefineOwnProperty(P, desc));
if (success === Value.false) {
return surroundingAgent.Throw('TypeError', 'CannotDefineProperty', P);
}
return success;
}
// 7.3.8 #sec-deletepropertyorthrow
export function DeletePropertyOrThrow(O, P) {
Assert(Type(O) === 'Object');
Assert(IsPropertyKey(P));
const success = Q(O.Delete(P));
if (success === Value.false) {
return surroundingAgent.Throw('TypeError', 'CannotDeleteProperty', P);
}
return success;
}
// 7.3.9 #sec-getmethod
export function GetMethod(V, P) {
Assert(IsPropertyKey(P));
const func = Q(GetV(V, P));
if (func === Value.null || func === Value.undefined) {
return Value.undefined;
}
if (IsCallable(func) === Value.false) {
return surroundingAgent.Throw('TypeError', 'NotAFunction', func);
}
return func;
}
// 7.3.10 #sec-hasproperty
export function HasProperty(O, P) {
Assert(Type(O) === 'Object');
Assert(IsPropertyKey(P));
return Q(O.HasProperty(P));
}
// 7.3.11 #sec-hasownproperty
export function HasOwnProperty(O, P) {
Assert(Type(O) === 'Object');
Assert(IsPropertyKey(P));
const desc = Q(O.GetOwnProperty(P));
if (desc === Value.undefined) {
return Value.false;
}
return Value.true;
}
// 7.3.12 #sec-call
export function Call(F, V, argumentsList) {
if (!argumentsList) {
argumentsList = [];
}
Assert(argumentsList.every((a) => a instanceof Value));
if (IsCallable(F) === Value.false) {
return surroundingAgent.Throw('TypeError', 'NotAFunction', F);
}
return EnsureCompletion(Q(F.Call(V, argumentsList)));
}
// 7.3.13 #sec-construct
export function Construct(F, argumentsList, newTarget) {
if (!newTarget) {
newTarget = F;
}
if (!argumentsList) {
argumentsList = [];
}
Assert(IsConstructor(F) === Value.true);
Assert(IsConstructor(newTarget) === Value.true);
return Q(F.Construct(argumentsList, newTarget));
}
// 7.3.14 #sec-setintegritylevel
export function SetIntegrityLevel(O, level) {
Assert(Type(O) === 'Object');
Assert(level === 'sealed' || level === 'frozen');
const status = Q(O.PreventExtensions());
if (status === Value.false) {
return Value.false;
}
const keys = Q(O.OwnPropertyKeys());
if (level === 'sealed') {
for (const k of keys) {
Q(DefinePropertyOrThrow(O, k, Descriptor({ Configurable: Value.false })));
}
} else if (level === 'frozen') {
for (const k of keys) {
const currentDesc = Q(O.GetOwnProperty(k));
if (currentDesc !== Value.undefined) {
let desc;
if (IsAccessorDescriptor(currentDesc) === true) {
desc = Descriptor({ Configurable: Value.false });
} else {
desc = Descriptor({ Configurable: Value.false, Writable: Value.false });
}
Q(DefinePropertyOrThrow(O, k, desc));
}
}
}
return Value.true;
}
// 7.3.15 #sec-testintegritylevel
export function TestIntegrityLevel(O, level) {
Assert(Type(O) === 'Object');
Assert(level === 'sealed' || level === 'frozen');
const extensible = Q(IsExtensible(O));
if (extensible === Value.true) {
return Value.false;
}
const keys = Q(O.OwnPropertyKeys());
for (const k of keys) {
const currentDesc = Q(O.GetOwnProperty(k));
if (currentDesc !== Value.undefined) {
if (currentDesc.Configurable === Value.true) {
return Value.false;
}
if (level === 'frozen' && IsDataDescriptor(currentDesc)) {
if (currentDesc.Writable === Value.true) {
return Value.false;
}
}
}
}
return Value.true;
}
// 7.3.16 #sec-createarrayfromlist
export function CreateArrayFromList(elements) {
Assert(elements.every((e) => e instanceof Value));
const array = X(ArrayCreate(new Value(0)));
let n = 0;
for (const e of elements) {
const nStr = X(ToString(new Value(n)));
const status = X(CreateDataProperty(array, nStr, e));
Assert(status === Value.true);
n += 1;
}
return array;
}
// 7.3.17 #sec-lengthofarraylike
export function LengthOfArrayLike(obj) {
Assert(Type(obj) === 'Object');
return Q(ToLength(Q(Get(obj, new Value('length')))));
}
// 7.3.17 #sec-createlistfromarraylike
export function CreateListFromArrayLike(obj, elementTypes) {
if (!elementTypes) {
elementTypes = ['Undefined', 'Null', 'Boolean', 'String', 'Symbol', 'Number', 'Object'];
}
if (Type(obj) !== 'Object') {
return surroundingAgent.Throw('TypeError', 'NotAnObject', obj);
}
const len = Q(LengthOfArrayLike(obj)).numberValue();
const list = [];
let index = 0;
while (index < len) {
const indexName = X(ToString(new Value(index)));
const next = Q(Get(obj, indexName));
if (!elementTypes.includes(Type(next))) {
return surroundingAgent.Throw('TypeError', 'NotPropertyName', next);
}
list.push(next);
index += 1;
}
return list;
}
// 7.3.18 #sec-invoke
export function Invoke(V, P, argumentsList) {
Assert(IsPropertyKey(P));
if (!argumentsList) {
argumentsList = [];
}
const func = Q(GetV(V, P));
return Q(Call(func, V, argumentsList));
}
// 7.3.19 #sec-ordinaryhasinstance
export function OrdinaryHasInstance(C, O) {
if (IsCallable(C) === Value.false) {
return Value.false;
}
if ('BoundTargetFunction' in C) {
const BC = C.BoundTargetFunction;
return Q(InstanceofOperator(O, BC));
}
if (Type(O) !== 'Object') {
return Value.false;
}
const P = Q(Get(C, new Value('prototype')));
if (Type(P) !== 'Object') {
return surroundingAgent.Throw('TypeError', 'NotAnObject', P);
}
while (true) {
O = Q(O.GetPrototypeOf());
if (O === Value.null) {
return Value.false;
}
if (SameValue(P, O) === Value.true) {
return Value.true;
}
}
}
// 7.3.20 #sec-speciesconstructor
export function SpeciesConstructor(O, defaultConstructor) {
Assert(Type(O) === 'Object');
const C = Q(Get(O, new Value('constructor')));
if (C === Value.undefined) {
return defaultConstructor;
}
if (Type(C) !== 'Object') {
return surroundingAgent.Throw('TypeError', 'NotAnObject', C);
}
const S = Q(Get(C, wellKnownSymbols.species));
if (S === Value.undefined || S === Value.null) {
return defaultConstructor;
}
if (IsConstructor(S) === Value.true) {
return S;
}
return surroundingAgent.Throw('TypeError', 'SpeciesNotConstructor');
}
// 7.3.21 #sec-enumerableownpropertynames
export function EnumerableOwnPropertyNames(O, kind) {
Assert(Type(O) === 'Object');
const ownKeys = Q(O.OwnPropertyKeys());
const properties = [];
for (const key of ownKeys) {
if (Type(key) === 'String') {
const desc = Q(O.GetOwnProperty(key));
if (desc !== Value.undefined && desc.Enumerable === Value.true) {
if (kind === 'key') {
properties.push(key);
} else {
const value = Q(Get(O, key));
if (kind === 'value') {
properties.push(value);
} else {
Assert(kind === 'key+value');
const entry = X(CreateArrayFromList([key, value]));
properties.push(entry);
}
}
}
}
}
return properties;
}
// 7.3.22 #sec-getfunctionrealm
export function GetFunctionRealm(obj) {
Assert(X(IsCallable(obj)) === Value.true);
if ('Realm' in obj) {
return obj.Realm;
}
if ('BoundTargetFunction' in obj) {
const target = obj.BoundTargetFunction;
return Q(GetFunctionRealm(target));
}
if (isProxyExoticObject(obj)) {
if (obj.ProxyHandler === Value.null) {
return surroundingAgent.Throw('TypeError', 'ProxyRevoked', 'GetFunctionRealm');
}
const proxyTarget = obj.ProxyTarget;
return Q(GetFunctionRealm(proxyTarget));
}
return surroundingAgent.currentRealmRecord;
}
// 7.3.23 #sec-copydataproperties
export function CopyDataProperties(target, source, excludedItems) {
Assert(Type(target) === 'Object');
Assert(excludedItems.every((i) => IsPropertyKey(i)));
if (source === Value.undefined || source === Value.null) {
return target;
}
const from = X(ToObject(source));
const keys = Q(from.OwnPropertyKeys());
for (const nextKey of keys) {
let excluded = false;
for (const e of excludedItems) {
if (SameValue(e, nextKey) === Value.true) {
excluded = true;
}
}
if (excluded === false) {
const desc = Q(from.GetOwnProperty(nextKey));
if (desc !== Value.undefined && desc.Enumerable === Value.true) {
const propValue = Q(Get(from, nextKey));
X(CreateDataProperty(target, nextKey, propValue));
}
}
}
return target;
}
-405
View File
@@ -1,405 +0,0 @@
import {
Descriptor,
ObjectValue,
Type,
Value,
} from '../value.mjs';
import { Q, X } from '../completion.mjs';
import {
Assert,
Call,
CreateDataProperty,
Get,
GetFunctionRealm,
IsAccessorDescriptor,
IsCallable,
IsDataDescriptor,
IsExtensible,
IsGenericDescriptor,
IsPropertyKey,
SameValue,
MakeBasicObject,
isArrayIndex,
} from './all.mjs';
// 9.1.1.1 OrdinaryGetPrototypeOf
export function OrdinaryGetPrototypeOf(O) {
return O.Prototype;
}
// 9.1.2.1 OrdinarySetPrototypeOf
export function OrdinarySetPrototypeOf(O, V) {
Assert(Type(V) === 'Object' || Type(V) === 'Null');
const current = O.Prototype;
if (SameValue(V, current) === Value.true) {
return Value.true;
}
const extensible = O.Extensible;
if (extensible === Value.false) {
return Value.false;
}
let p = V;
let done = false;
while (done === false) {
if (p === Value.null) {
done = true;
} else if (SameValue(p, O) === Value.true) {
return Value.false;
} else if (p.GetPrototypeOf !== ObjectValue.prototype.GetPrototypeOf) {
done = true;
} else {
p = p.Prototype;
}
}
O.Prototype = V;
return Value.true;
}
// 9.1.3.1 OrdinaryIsExtensible
export function OrdinaryIsExtensible(O) {
return O.Extensible;
}
// 9.1.4.1 OrdinaryPreventExtensions
export function OrdinaryPreventExtensions(O) {
O.Extensible = Value.false;
return Value.true;
}
// 9.1.5.1 OrdinaryGetOwnProperty
export function OrdinaryGetOwnProperty(O, P) {
Assert(IsPropertyKey(P));
if (!O.properties.has(P)) {
return Value.undefined;
}
const D = Descriptor({});
const x = O.properties.get(P);
if (IsDataDescriptor(x)) {
D.Value = x.Value;
D.Writable = x.Writable;
} else if (IsAccessorDescriptor(x)) {
D.Get = x.Get;
D.Set = x.Set;
}
D.Enumerable = x.Enumerable;
D.Configurable = x.Configurable;
return D;
}
// 9.1.6.1 OrdinaryDefineOwnProperty
export function OrdinaryDefineOwnProperty(O, P, Desc) {
const current = Q(O.GetOwnProperty(P));
const extensible = Q(IsExtensible(O));
return ValidateAndApplyPropertyDescriptor(O, P, extensible, Desc, current);
}
// 9.1.6.2 #sec-iscompatiblepropertydescriptor
export function IsCompatiblePropertyDescriptor(Extensible, Desc, Current) {
return ValidateAndApplyPropertyDescriptor(
Value.undefined, Value.undefined, Extensible, Desc, Current,
);
}
// 9.1.6.3 ValidateAndApplyPropertyDescriptor
export function ValidateAndApplyPropertyDescriptor(O, P, extensible, Desc, current) {
Assert(O === Value.undefined || IsPropertyKey(P));
if (current === Value.undefined) {
if (extensible === Value.false) {
return Value.false;
}
Assert(extensible === Value.true);
if (IsGenericDescriptor(Desc) || IsDataDescriptor(Desc)) {
if (Type(O) !== 'Undefined') {
O.properties.set(P, Descriptor({
Value: Desc.Value === undefined ? Value.undefined : Desc.Value,
Writable: Desc.Writable === undefined ? Value.false : Desc.Writable,
Enumerable: Desc.Enumerable === undefined ? Value.false : Desc.Enumerable,
Configurable: Desc.Configurable === undefined ? Value.false : Desc.Configurable,
}));
}
} else {
Assert(IsAccessorDescriptor(Desc));
if (Type(O) !== 'Undefined') {
O.properties.set(P, Descriptor({
Get: Desc.Get === undefined ? Value.undefined : Desc.Get,
Set: Desc.Set === undefined ? Value.undefined : Desc.Set,
Enumerable: Desc.Enumerable === undefined ? Value.false : Desc.Enumerable,
Configurable: Desc.Configurable === undefined ? Value.false : Desc.Configurable,
}));
}
}
return Value.true;
}
if (Desc.everyFieldIsAbsent()) {
return Value.true;
}
if (current.Configurable === Value.false) {
if (Desc.Configurable !== undefined && Desc.Configurable === Value.true) {
return Value.false;
}
if (Desc.Enumerable !== undefined && Desc.Enumerable !== current.Enumerable) {
return Value.false;
}
}
if (IsGenericDescriptor(Desc)) {
// No further validation is required.
} else if (IsDataDescriptor(current) !== IsDataDescriptor(Desc)) {
if (current.Configurable === Value.false) {
return Value.false;
}
if (IsDataDescriptor(current)) {
if (Type(O) !== 'Undefined') {
const entry = O.properties.get(P);
entry.Value = undefined;
entry.Writable = undefined;
entry.Get = Value.undefined;
entry.Set = Value.undefined;
}
} else {
if (Type(O) !== 'Undefined') {
const entry = O.properties.get(P);
entry.Get = undefined;
entry.Set = undefined;
entry.Value = Value.undefined;
entry.Writable = Value.false;
}
}
} else if (IsDataDescriptor(current) && IsDataDescriptor(Desc)) {
if (current.Configurable === Value.false && current.Writable === Value.false) {
if (Desc.Writable !== undefined && Desc.Writable === Value.true) {
return Value.false;
}
if (Desc.Value !== undefined && SameValue(Desc.Value, current.Value) === Value.false) {
return Value.false;
}
return Value.true;
}
} else {
Assert(IsAccessorDescriptor(current) && IsAccessorDescriptor(Desc));
if (current.Configurable === Value.false) {
if (Desc.Set !== undefined && SameValue(Desc.Set, current.Set) === Value.false) {
return Value.false;
}
if (Desc.Get !== undefined && SameValue(Desc.Get, current.Get) === Value.false) {
return Value.false;
}
return Value.true;
}
}
if (Type(O) !== 'Undefined') {
const target = O.properties.get(P);
if (Desc.Value !== undefined) {
target.Value = Desc.Value;
}
if (Desc.Writable !== undefined) {
target.Writable = Desc.Writable;
}
if (Desc.Get !== undefined) {
target.Get = Desc.Get;
}
if (Desc.Set !== undefined) {
target.Set = Desc.Set;
}
if (Desc.Enumerable !== undefined) {
target.Enumerable = Desc.Enumerable;
}
if (Desc.Configurable !== undefined) {
target.Configurable = Desc.Configurable;
}
}
return Value.true;
}
// 9.1.7.1 OrdinaryHasProperty
export function OrdinaryHasProperty(O, P) {
Assert(IsPropertyKey(P));
const hasOwn = Q(O.GetOwnProperty(P));
if (Type(hasOwn) !== 'Undefined') {
return Value.true;
}
const parent = Q(O.GetPrototypeOf());
if (Type(parent) !== 'Null') {
return Q(parent.HasProperty(P));
}
return Value.false;
}
// 9.1.8.1
export function OrdinaryGet(O, P, Receiver) {
Assert(IsPropertyKey(P));
const desc = Q(O.GetOwnProperty(P));
if (Type(desc) === 'Undefined') {
const parent = Q(O.GetPrototypeOf());
if (Type(parent) === 'Null') {
return Value.undefined;
}
return Q(parent.Get(P, Receiver));
}
if (IsDataDescriptor(desc)) {
return desc.Value;
}
Assert(IsAccessorDescriptor(desc));
const getter = desc.Get;
if (Type(getter) === 'Undefined') {
return Value.undefined;
}
return Q(Call(getter, Receiver));
}
// 9.1.9.1 OrdinarySet
export function OrdinarySet(O, P, V, Receiver) {
Assert(IsPropertyKey(P));
const ownDesc = Q(O.GetOwnProperty(P));
return OrdinarySetWithOwnDescriptor(O, P, V, Receiver, ownDesc);
}
// 9.1.9.2 OrdinarySetWithOwnDescriptor
export function OrdinarySetWithOwnDescriptor(O, P, V, Receiver, ownDesc) {
Assert(IsPropertyKey(P));
if (Type(ownDesc) === 'Undefined') {
const parent = Q(O.GetPrototypeOf());
if (Type(parent) !== 'Null') {
return Q(parent.Set(P, V, Receiver));
}
ownDesc = Descriptor({
Value: Value.undefined,
Writable: Value.true,
Enumerable: Value.true,
Configurable: Value.true,
});
}
if (IsDataDescriptor(ownDesc)) {
if (ownDesc.Writable !== undefined && ownDesc.Writable === Value.false) {
return Value.false;
}
if (Type(Receiver) !== 'Object') {
return Value.false;
}
const existingDescriptor = Q(Receiver.GetOwnProperty(P));
if (Type(existingDescriptor) !== 'Undefined') {
if (IsAccessorDescriptor(existingDescriptor)) {
return Value.false;
}
if (existingDescriptor.Writable === Value.false) {
return Value.false;
}
const valueDesc = Descriptor({ Value: V });
return Q(Receiver.DefineOwnProperty(P, valueDesc));
}
return CreateDataProperty(Receiver, P, V);
}
Assert(IsAccessorDescriptor(ownDesc));
const setter = ownDesc.Set;
if (setter === undefined || Type(setter) === 'Undefined') {
return Value.false;
}
Q(Call(setter, Receiver, [V]));
return Value.true;
}
// 9.1.10.1 OrdinaryDelete
export function OrdinaryDelete(O, P) {
Assert(IsPropertyKey(P));
const desc = Q(O.GetOwnProperty(P));
if (Type(desc) === 'Undefined') {
return Value.true;
}
if (desc.Configurable === Value.true) {
O.properties.delete(P);
return Value.true;
}
return Value.false;
}
// 9.1.11.1
export function OrdinaryOwnPropertyKeys(O) {
const keys = [];
// For each own property key P of O that is an array index, in ascending numeric index order, do
// Add P as the last element of keys.
for (const P of O.properties.keys()) {
if (isArrayIndex(P)) {
keys.push(P);
}
}
keys.sort((a, b) => Number.parseInt(a.stringValue(), 10) - Number.parseInt(b.stringValue(), 10));
// For each own property key P of O such that Type(P) is String and
// P is not an array index, in ascending chronological order of property creation, do
// Add P as the last element of keys.
for (const P of O.properties.keys()) {
if (Type(P) === 'String' && isArrayIndex(P) === false) {
keys.push(P);
}
}
// For each own property key P of O such that Type(P) is Symbol,
// in ascending chronological order of property creation, do
// Add P as the last element of keys.
for (const P of O.properties.keys()) {
if (Type(P) === 'Symbol') {
keys.push(P);
}
}
return keys;
}
// #sec-ordinaryobjectcreate
export function OrdinaryObjectCreate(proto, additionalInternalSlotsList) {
// 1. Let internalSlotsList be « [[Prototype]], [[Extensible]] ».
const internalSlotsList = ['Prototype', 'Extensible'];
// 2. If additionalInternalSlotsList is present, append each of its elements to internalSlotsList.
if (additionalInternalSlotsList !== undefined) {
internalSlotsList.push(...additionalInternalSlotsList);
}
// 3. Let O be ! MakeBasicObject(internalSlotsList).
const O = X(MakeBasicObject(internalSlotsList));
// 4. Set O.[[Prototype]] to proto.
O.Prototype = proto;
// 5. Return O.
return O;
}
// 9.1.13 OrdinaryCreateFromConstructor
export function OrdinaryCreateFromConstructor(constructor, intrinsicDefaultProto, internalSlotsList) {
// Assert: intrinsicDefaultProto is a String value that is this specification's name of an intrinsic object.
const proto = Q(GetPrototypeFromConstructor(constructor, intrinsicDefaultProto));
return OrdinaryObjectCreate(proto, internalSlotsList);
}
// 9.1.14 GetPrototypeFromConstructor
export function GetPrototypeFromConstructor(constructor, intrinsicDefaultProto) {
// Assert: intrinsicDefaultProto is a String value that
// is this specification's name of an intrinsic object.
Assert(IsCallable(constructor) === Value.true);
let proto = Q(Get(constructor, new Value('prototype')));
if (Type(proto) !== 'Object') {
const realm = Q(GetFunctionRealm(constructor));
proto = realm.Intrinsics[intrinsicDefaultProto];
}
return proto;
}
@@ -1,423 +0,0 @@
import {
HostMakeJobCallback,
HostCallJobCallback,
HostEnqueuePromiseJob,
HostPromiseRejectionTracker,
surroundingAgent,
} from '../engine.mjs';
import { Type, Value } from '../value.mjs';
import {
Completion,
AbruptCompletion,
NormalCompletion,
Q,
X,
ThrowCompletion,
} from '../completion.mjs';
import {
Assert,
Call,
Construct,
CreateBuiltinFunction,
Get,
IsCallable,
IsConstructor,
SameValue,
SetFunctionLength,
SetFunctionName,
GetFunctionRealm,
isFunctionObject,
} from './all.mjs';
// This file covers abstract operations defined in
// 25.6 #sec-promise-objects
// 25.6.1.1 #sec-promisecapability-records
export class PromiseCapabilityRecord {
constructor() {
this.Promise = Value.undefined;
this.Resolve = Value.undefined;
this.Reject = Value.undefined;
}
}
// 25.6.1.2 #sec-promisereaction-records
export class PromiseReactionRecord {
constructor(O) {
Assert(O.Capability instanceof PromiseCapabilityRecord
|| O.Capability === Value.undefined);
Assert(O.Type === 'Fulfill' || O.Type === 'Reject');
Assert(O.Handler === undefined
|| isFunctionObject(O.Handler.Callback));
this.Capability = O.Capability;
this.Type = O.Type;
this.Handler = O.Handler;
}
}
// 25.6.1.3 #sec-createresolvingfunctions
export function CreateResolvingFunctions(promise) {
const alreadyResolved = { Value: false };
const stepsResolve = PromiseResolveFunctions;
const resolve = X(CreateBuiltinFunction(stepsResolve, ['Promise', 'AlreadyResolved']));
SetFunctionLength(resolve, new Value(1));
SetFunctionName(resolve, new Value(''));
resolve.Promise = promise;
resolve.AlreadyResolved = alreadyResolved;
const stepsReject = PromiseRejectFunctions;
const reject = X(CreateBuiltinFunction(stepsReject, ['Promise', 'AlreadyResolved']));
SetFunctionLength(reject, new Value(1));
SetFunctionName(reject, new Value(''));
reject.Promise = promise;
reject.AlreadyResolved = alreadyResolved;
return {
Resolve: resolve,
Reject: reject,
};
}
// 25.6.1.3.1 #sec-promise-reject-functions
function PromiseRejectFunctions([reason = Value.undefined]) {
const F = this;
Assert('Promise' in F && Type(F.Promise) === 'Object');
const promise = F.Promise;
const alreadyResolved = F.AlreadyResolved;
if (alreadyResolved.Value === true) {
return Value.undefined;
}
alreadyResolved.Value = true;
return RejectPromise(promise, reason);
}
// #sec-newpromiseresolvethenablejob
function NewPromiseResolveThenableJob(promiseToResolve, thenable, then) {
// 1. Let job be a new Job abstract closure with no parameters that captures
// promiseToResolve, thenable, and then and performs the following steps when called:
const job = () => {
// a. Let resolvingFunctions be CreateResolvingFunctions(promiseToResolve).
const resolvingFunctions = CreateResolvingFunctions(promiseToResolve);
// b. Let thenCallResult be HostCallJobCallback(then, thenable, « resolvingFunctions.[[Resolve]], resolvingFunctions.[[Reject]] »).
const thenCallResult = HostCallJobCallback(then, thenable, [resolvingFunctions.Resolve, resolvingFunctions.Reject]);
// c. If thenCallResult is an abrupt completion, then
if (thenCallResult instanceof AbruptCompletion) {
// i .Let status be Call(resolvingFunctions.[[Reject]], undefined, « thenCallResult.[[Value]] »).
const status = Call(resolvingFunctions.Reject, Value.undefined, [thenCallResult.Value]);
// ii. Return Completion(status).
return Completion(status);
}
// d. Return Completion(thenCallResult).
return Completion(thenCallResult);
};
// 2. Let getThenRealmResult be GetFunctionRealm(then.[[Callback]]).
const getThenRealmResult = GetFunctionRealm(then.Callback);
// 3. If getThenRealmResult is a normal completion, then let thenRealm be getThenRealmResult.[[Value]].
let thenRealm;
if (getThenRealmResult instanceof NormalCompletion) {
thenRealm = getThenRealmResult.Value;
} else {
// 4. Else, let _thenRealm_ be the current Realm Record.
thenRealm = surroundingAgent.currentRealmRecord;
}
// 5. NOTE: _thenRealm_ is never *null*. When _then_.[[Callback]] is a revoked Proxy and no code runs, _thenRealm_ is used to create error objects.
// 6. Return { [[Job]]: job, [[Realm]]: thenRealm }.
return { Job: job, Realm: thenRealm };
}
// 25.6.1.3.2 #sec-promise-resolve-functions
function PromiseResolveFunctions([resolution = Value.undefined]) {
// 1. Let F be the active function object.
const F = this;
// 2. Assert: F has a [[Promise]] internal slot whose value is an Object.
Assert('Promise' in F && Type(F.Promise) === 'Object');
// 3. Let promise be F.[[Promise]].
const promise = F.Promise;
// 4. Let alreadyResolved be F.[[AlreadyResolved]].
const alreadyResolved = F.AlreadyResolved;
// 5. If alreadyResolved.[[Value]] is true, return undefined.
if (alreadyResolved.Value === true) {
return Value.undefined;
}
// 6. Set alreadyResolved.[[Value]] to true.
alreadyResolved.Value = true;
// 7. If SameValue(resolution, promise) is true, then
if (SameValue(resolution, promise) === Value.true) {
// a. Let selfResolutionError be a newly created TypeError object.
const selfResolutionError = surroundingAgent.Throw('TypeError', 'CannotResolvePromiseWithItself').Value;
// b. Return RejectPromise(promise, selfResolutionError).
return RejectPromise(promise, selfResolutionError);
}
// 8. If Type(resolution) is not Object, then
if (Type(resolution) !== 'Object') {
// a. Return FulfillPromise(promise, resolution).
return FulfillPromise(promise, resolution);
}
// 9. Let then be Get(resolution, "then").
const then = Get(resolution, new Value('then'));
// 10. If then is an abrupt completion, then
if (then instanceof AbruptCompletion) {
// a. Return RejectPromise(promise, then.[[Value]]).
return RejectPromise(promise, then.Value);
}
// 11. Let thenAction be then.[[Value]].
const thenAction = then.Value;
// 12. If IsCallable(thenAction) is false, then
if (IsCallable(thenAction) === Value.false) {
// a. Return FulfillPromise(promise, resolution).
return FulfillPromise(promise, resolution);
}
// 13. Let thenJobCallback be HostMakeJobCallback(thenAction).
const thenJobCallback = HostMakeJobCallback(thenAction);
// 14. Let job be NewPromiseResolveThenableJob(promise, resolution, thenJobCallback).
const job = NewPromiseResolveThenableJob(promise, resolution, thenJobCallback);
// 15. Perform HostEnqueuePromiseJob(job.[[Job]], job.[[Realm]]).
HostEnqueuePromiseJob(job.Job, job.Realm);
// 16. Return undefined.
return Value.undefined;
}
// 25.6.1.4 #sec-fulfillpromise
function FulfillPromise(promise, value) {
Assert(promise.PromiseState === 'pending');
const reactions = promise.PromiseFulfillReactions;
promise.PromiseResult = value;
promise.PromiseFulfillReactions = undefined;
promise.PromiseRejectReactions = undefined;
promise.PromiseState = 'fulfilled';
return TriggerPromiseReactions(reactions, value);
}
// 25.6.1.5 #sec-newpromisecapability
export function NewPromiseCapability(C) {
if (IsConstructor(C) === Value.false) {
return surroundingAgent.Throw('TypeError', 'NotAConstructor', C);
}
const promiseCapability = new PromiseCapabilityRecord();
const steps = GetCapabilitiesExecutorFunctions;
const executor = X(CreateBuiltinFunction(steps, ['Capability']));
SetFunctionLength(executor, new Value(2));
SetFunctionName(executor, new Value(''));
executor.Capability = promiseCapability;
const promise = Q(Construct(C, [executor]));
if (IsCallable(promiseCapability.Resolve) === Value.false) {
return surroundingAgent.Throw('TypeError', 'PromiseResolveFunction', promiseCapability.Resolve);
}
if (IsCallable(promiseCapability.Reject) === Value.false) {
return surroundingAgent.Throw('TypeError', 'PromiseRejectFunction', promiseCapability.Reject);
}
promiseCapability.Promise = promise;
return promiseCapability;
}
// 25.6.1.5.1 #sec-getcapabilitiesexecutor-functions
function GetCapabilitiesExecutorFunctions([resolve = Value.undefined, reject = Value.undefined]) {
const F = this;
const promiseCapability = F.Capability;
if (Type(promiseCapability.Resolve) !== 'Undefined') {
return surroundingAgent.Throw('TypeError', 'PromiseCapabilityFunctionAlreadySet', 'resolve');
}
if (Type(promiseCapability.Reject) !== 'Undefined') {
return surroundingAgent.Throw('TypeError', 'PromiseCapabilityFunctionAlreadySet', 'reject');
}
promiseCapability.Resolve = resolve;
promiseCapability.Reject = reject;
return Value.undefined;
}
// 25.6.1.6 #sec-ispromise
export function IsPromise(x) {
if (Type(x) !== 'Object') {
return Value.false;
}
if (!('PromiseState' in x)) {
return Value.false;
}
return Value.true;
}
// 25.6.1.7 #sec-rejectpromise
function RejectPromise(promise, reason) {
Assert(promise.PromiseState === 'pending');
const reactions = promise.PromiseRejectReactions;
promise.PromiseResult = reason;
promise.PromiseFulfillReactions = undefined;
promise.PromiseRejectReactions = undefined;
promise.PromiseState = 'rejected';
if (promise.PromiseIsHandled === Value.false) {
HostPromiseRejectionTracker(promise, 'reject');
}
return TriggerPromiseReactions(reactions, reason);
}
// #sec-triggerpromisereactions
function TriggerPromiseReactions(reactions, argument) {
// 1. For each reaction in reactions, in original insertion order, do
reactions.forEach((reaction) => {
// a. Let job be NewPromiseReactionJob(reaction, argument).
const job = NewPromiseReactionJob(reaction, argument);
// b. Perform HostEnqueuePromiseJob(job.[[Job]], job.[[Realm]]).
HostEnqueuePromiseJob(job.Job, job.Realm);
});
// 2. Return undefined.
return Value.undefined;
}
// 25.6.4.5.1 #sec-promise-resolve
export function PromiseResolve(C, x) {
Assert(Type(C) === 'Object');
if (IsPromise(x) === Value.true) {
const xConstructor = Q(Get(x, new Value('constructor')));
if (SameValue(xConstructor, C) === Value.true) {
return x;
}
}
const promiseCapability = Q(NewPromiseCapability(C));
Q(Call(promiseCapability.Resolve, Value.undefined, [x]));
return promiseCapability.Promise;
}
// #sec-newpromisereactionjob
function NewPromiseReactionJob(reaction, argument) {
// 1. Let job be a new Job abstract closure with no parameters that captures
// reaction and argument and performs the following steps when called:
const job = () => {
// a. Assert: reaction is a PromiseReaction Record.
Assert(reaction instanceof PromiseReactionRecord);
// b. Let promiseCapability be reaction.[[Capability]].
const promiseCapability = reaction.Capability;
// c. Let type be reaction.[[Type]].
const type = reaction.Type;
// d. Let handler be reaction.[[Handler]].
const handler = reaction.Handler;
let handlerResult;
// e. If handler is empty, then
if (handler === undefined) {
// i. If type is Fulfill, let handlerResult be NormalCompletion(argument).
if (type === 'Fulfill') {
handlerResult = NormalCompletion(argument);
} else {
// 1. Assert: type is Reject.
Assert(type === 'Reject');
// 2. Let handlerResult be ThrowCompletion(argument).
handlerResult = ThrowCompletion(argument);
}
} else {
// f. Else, let handlerResult be HostCallJobCallback(handler, undefined, « argument »).
handlerResult = HostCallJobCallback(handler, Value.undefined, [argument]);
}
// g. If promiseCapability is undefined, then
if (promiseCapability === Value.undefined) {
// i. Assert: handlerResult is not an abrupt completion.
Assert(!(handlerResult instanceof AbruptCompletion));
// ii. Return NormalCompletion(empty).
return NormalCompletion(undefined);
}
let status;
// h. If handlerResult is an abrupt completion, then
if (handlerResult instanceof AbruptCompletion) {
// i. Let status be Call(promiseCapability.[[Reject]], undefined, « handlerResult.[[Value]] »).
status = Call(promiseCapability.Reject, Value.undefined, [handlerResult.Value]);
} else {
// ii. Let status be Call(promiseCapability.[[Resolve]], undefined, « handlerResult.[[Value]] »).
status = Call(promiseCapability.Resolve, Value.undefined, [handlerResult.Value]);
}
// j. Return Completion(status).
return Completion(status);
};
// 2. Let handlerRealm be null.
let handlerRealm = Value.null;
// 3. If reaction.[[Handler]] is not empty, then
if (reaction.Handler !== undefined) {
// a. Let getHandlerRealmResult be GetFunctionRealm(reaction.[[Handler]].[[Callback]]).
const getHandlerRealmResult = GetFunctionRealm(reaction.Handler.Callback);
// b. If getHandlerRealmResult is a normal completion, then set handlerRealm to getHandlerRealmResult.[[Value]].
if (getHandlerRealmResult instanceof NormalCompletion) {
handlerRealm = getHandlerRealmResult.Value;
} else {
// c. Else, set _handlerRealm_ to the current Realm Record.
handlerRealm = surroundingAgent.currentRealmRecord;
}
// d. NOTE: _handlerRealm_ is never *null* unless the handler is *undefined*. When the handler
// is a revoked Proxy and no ECMAScript code runs, _handlerRealm_ is used to create error objects.
}
// 4. Return { [[Job]]: job, [[Realm]]: handlerRealm }.
return { Job: job, Realm: handlerRealm };
}
// 25.6.5.4.1 #sec-performpromisethen
export function PerformPromiseThen(promise, onFulfilled, onRejected, resultCapability) {
// 1. Assert: IsPromise(promise) is true.
Assert(IsPromise(promise) === Value.true);
// 2. If resultCapability is not present, then
if (resultCapability === undefined) {
// a. Set resultCapability to undefined.
resultCapability = Value.undefined;
}
let onFulfilledJobCallback;
// 3. If IsCallable(onFulfilled) is false, then
if (IsCallable(onFulfilled) === Value.false) {
// a. Let onFulfilledJobCallback be empty.
onFulfilledJobCallback = undefined;
} else { // 4. Else,
// a. Let onFulfilledJobCallback be HostMakeJobCallback(onFulfilled).
onFulfilledJobCallback = HostMakeJobCallback(onFulfilled);
}
let onRejectedJobCallback;
// 5. If IsCallable(onRejected) is false, then
if (IsCallable(onRejected) === Value.false) {
// a. Let onRejectedJobCallback be empty.
onRejectedJobCallback = undefined;
} else { // 6. Else,
onRejectedJobCallback = HostMakeJobCallback(onRejected);
}
// 7. Let fulfillReaction be the PromiseReaction { [[Capability]]: resultCapability, [[Type]]: Fulfill, [[Handler]]: onFulfilled }.
const fulfillReaction = new PromiseReactionRecord({
Capability: resultCapability,
Type: 'Fulfill',
Handler: onFulfilledJobCallback,
});
// 8. Let rejectReaction be the PromiseReaction { [[Capability]]: resultCapability, [[Type]]: Reject, [[Handler]]: onRejected }.
const rejectReaction = new PromiseReactionRecord({
Capability: resultCapability,
Type: 'Reject',
Handler: onRejectedJobCallback,
});
// 9. If promise.[[PromiseState]] is pending, then
if (promise.PromiseState === 'pending') {
// a. Append fulfillReaction as the last element of the List that is promise.[[PromiseFulfillReactions]].
promise.PromiseFulfillReactions.push(fulfillReaction);
// b. Append rejectReaction as the last element of the List that is promise.[[PromiseRejectReactions]].
promise.PromiseRejectReactions.push(rejectReaction);
} else if (promise.PromiseState === 'fulfilled') {
// a. Let value be promise.[[PromiseResult]].
const value = promise.PromiseResult;
// b. Let fulfillJob be NewPromiseReactionJob(fulfillReaction, value).
const fulfillJob = NewPromiseReactionJob(fulfillReaction, value);
// c. Perform HostEnqueuePromiseJob(fulfillJob.[[Job]], fulfillJob.[[Realm]]).
HostEnqueuePromiseJob(fulfillJob.Job, fulfillJob.Realm);
} else {
// a. Assert: The value of promise.[[PromiseState]] is rejected.
Assert(promise.PromiseState === 'rejected');
// b. Let reason be promise.[[PromiseResult]].
const reason = promise.PromiseResult;
// c. If promise.[[PromiseIsHandled]] is false, perform HostPromiseRejectionTracker(promise, "handle").
if (promise.PromiseIsHandled === Value.false) {
HostPromiseRejectionTracker(promise, 'handle');
}
// d. Let rejectJob be NewPromiseReactionJob(rejectReaction, reason).
const rejectJob = NewPromiseReactionJob(rejectReaction, reason);
// e. Perform HostEnqueuePromiseJob(rejectJob.[[Job]], rejectJob.[[Realm]]).
HostEnqueuePromiseJob(rejectJob.Job, rejectJob.Realm);
}
// 12. Set promise.[[PromiseIsHandled]] to true.
promise.PromiseIsHandled = Value.true;
// 13. If resultCapability is undefined, then
if (resultCapability === Value.undefined) {
// a. Return undefined.
return Value.undefined;
} else { // 14. Else,
// a. Return resultCapability.[[Promise]].
return resultCapability.Promise;
}
}
@@ -1,579 +0,0 @@
import { surroundingAgent } from '../engine.mjs';
import { Type, Value } from '../value.mjs';
import { Q, X } from '../completion.mjs';
import { ValueSet } from '../helpers.mjs';
import {
Assert,
MakeBasicObject,
IsConstructor,
IsCallable,
Call,
Construct,
GetMethod,
CreateArrayFromList,
CreateListFromArrayLike,
IsExtensible,
IsPropertyKey,
SameValue,
ToBoolean,
ToPropertyDescriptor,
FromPropertyDescriptor,
CompletePropertyDescriptor,
IsCompatiblePropertyDescriptor,
IsDataDescriptor,
IsAccessorDescriptor,
} from './all.mjs';
// #sec-proxy-object-internal-methods-and-internal-slots-getprototypeof
function ProxyGetPrototypeOf() {
const O = this;
const handler = O.ProxyHandler;
if (handler === Value.null) {
return surroundingAgent.Throw('TypeError', 'ProxyRevoked', 'getPrototypeOf');
}
Assert(Type(handler) === 'Object');
const target = O.ProxyTarget;
const trap = Q(GetMethod(handler, new Value('getPrototypeOf')));
if (trap === Value.undefined) {
return Q(target.GetPrototypeOf());
}
const handlerProto = Q(Call(trap, handler, [target]));
if (Type(handlerProto) !== 'Object' && Type(handlerProto) !== 'Null') {
return surroundingAgent.Throw('TypeError', 'ProxyGetPrototypeOfInvalid');
}
const extensibleTarget = Q(IsExtensible(target));
if (extensibleTarget === Value.true) {
return handlerProto;
}
const targetProto = Q(target.GetPrototypeOf());
if (SameValue(handlerProto, targetProto) === Value.false) {
return surroundingAgent.Throw('TypeError', 'ProxyGetPrototypeOfNonExtensible');
}
return handlerProto;
}
// #sec-proxy-object-internal-methods-and-internal-slots-setprototypeof-v
function ProxySetPrototypeOf(V) {
const O = this;
Assert(Type(V) === 'Object' || Type(V) === 'Null');
const handler = O.ProxyHandler;
if (handler === Value.null) {
return surroundingAgent.Throw('TypeError', 'ProxyRevoked', 'setPrototypeOf');
}
Assert(Type(handler) === 'Object');
const target = O.ProxyTarget;
const trap = Q(GetMethod(handler, new Value('setPrototypeOf')));
if (trap === Value.undefined) {
return Q(target.SetPrototypeOf(V));
}
const booleanTrapResult = ToBoolean(Q(Call(trap, handler, [target, V])));
if (booleanTrapResult === Value.false) {
return Value.false;
}
const extensibleTarget = Q(IsExtensible(target));
if (extensibleTarget === Value.true) {
return Value.true;
}
const targetProto = Q(target.GetPrototypeOf());
if (SameValue(V, targetProto) === Value.false) {
return surroundingAgent.Throw('TypeError', 'ProxySetPrototypeOfNonExtensible');
}
return Value.true;
}
// #sec-proxy-object-internal-methods-and-internal-slots-isextensible
function ProxyIsExtensible() {
const O = this;
const handler = O.ProxyHandler;
if (handler === Value.null) {
return surroundingAgent.Throw('TypeError', 'ProxyRevoked', 'isExtensible');
}
Assert(Type(handler) === 'Object');
const target = O.ProxyTarget;
const trap = Q(GetMethod(handler, new Value('isExtensible')));
if (trap === Value.undefined) {
return Q(IsExtensible(target));
}
const booleanTrapResult = ToBoolean(Q(Call(trap, handler, [target])));
const targetResult = Q(IsExtensible(target));
if (SameValue(booleanTrapResult, targetResult) === Value.false) {
return surroundingAgent.Throw('TypeError', 'ProxyIsExtensibleInconsistent', targetResult);
}
return booleanTrapResult;
}
// #sec-proxy-object-internal-methods-and-internal-slots-preventextensions
function ProxyPreventExtensions() {
const O = this;
const handler = O.ProxyHandler;
if (handler === Value.null) {
return surroundingAgent.Throw('TypeError', 'ProxyRevoked', 'preventExtensions');
}
Assert(Type(handler) === 'Object');
const target = O.ProxyTarget;
const trap = Q(GetMethod(handler, new Value('preventExtensions')));
if (trap === Value.undefined) {
return Q(target.PreventExtensions());
}
const booleanTrapResult = ToBoolean(Q(Call(trap, handler, [target])));
if (booleanTrapResult === Value.true) {
const extensibleTarget = Q(IsExtensible(target));
if (extensibleTarget === Value.true) {
return surroundingAgent.Throw('TypeError', 'ProxyPreventExtensionsExtensible');
}
}
return booleanTrapResult;
}
// #sec-proxy-object-internal-methods-and-internal-slots-getownproperty-p
function ProxyGetOwnProperty(P) {
const O = this;
// 1. Assert: IsPropertyKey(P) is true.
Assert(IsPropertyKey(P));
// 2. Let handler be O.[[ProxyHandler]].
const handler = O.ProxyHandler;
// 3. If handler is null, throw a TypeError exception.
if (handler === Value.null) {
return surroundingAgent.Throw('TypeError', 'ProxyRevoked', 'getOwnPropertyDescriptor');
}
// 4. Assert: Type(Handler) is Object.
Assert(Type(handler) === 'Object');
// 5. Let target be O.[[ProxyTarget]].
const target = O.ProxyTarget;
// 6. Let trap be ? Getmethod(handler, "getOwnPropertyDescriptor").
const trap = Q(GetMethod(handler, new Value('getOwnPropertyDescriptor')));
// 7. If trap is undefined, then
if (trap === Value.undefined) {
// a. Return ? target.[[GetOwnProperty]](P).
return Q(target.GetOwnProperty(P));
}
// 8. Let trapResultObj be ? Call(trap, handler, « target, P »).
const trapResultObj = Q(Call(trap, handler, [target, P]));
// 9. If Type(trapResultObj) is neither Object nor Undefined, throw a TypeError exception.
if (Type(trapResultObj) !== 'Object' && Type(trapResultObj) !== 'Undefined') {
return surroundingAgent.Throw('TypeError', 'ProxyGetOwnPropertyDescriptorInvalid', P);
}
// 10. Let targetDesc be ? target.[[GetOwnProperty]](P).
const targetDesc = Q(target.GetOwnProperty(P));
// 11. If trapResultObj is undefined, then
if (trapResultObj === Value.undefined) {
// a. If targetDesc is undefined, return undefined.
if (targetDesc === Value.undefined) {
return Value.undefined;
}
// b. If targetDesc.[[Configurable]] is false, throw a TypeError exception.
if (targetDesc.Configurable === Value.false) {
return surroundingAgent.Throw('TypeError', 'ProxyGetOwnPropertyDescriptorUndefined', P);
}
// c. Let extensibleTarget be ? IsExtensible(target).
const extensibleTarget = Q(IsExtensible(target));
// d. If extensibleTarget is false, throw a TypeError exception.
if (extensibleTarget === Value.false) {
return surroundingAgent.Throw('TypeError', 'ProxyGetOwnPropertyDescriptorNonExtensible', P);
}
// e. Return undefined.
return Value.undefined;
}
// 12. Let extensibleTarget be ? IsExtensible(target).
const extensibleTarget = Q(IsExtensible(target));
// 13. Let resultDesc be ? ToPropertyDescriptor(trapResultObj).
const resultDesc = Q(ToPropertyDescriptor(trapResultObj));
// 14. Call CompletePropertyDescriptor(resultDesc).
CompletePropertyDescriptor(resultDesc);
// 15. Let valid be IsCompatiblePropertyDescriptor(extensibleTarget, resultDesc, targetDesc).
const valid = IsCompatiblePropertyDescriptor(extensibleTarget, resultDesc, targetDesc);
// 16. If valid is false, throw a TypeError exception.
if (valid === Value.false) {
return surroundingAgent.Throw('TypeError', 'ProxyGetOwnPropertyDescriptorIncompatible', P);
}
// 17. If resultDesc.[[Configurable]] is false, then
if (resultDesc.Configurable === Value.false) {
// a. If targetDesc is undefined or targetDesc.[[Configurable]] is true, then
if (targetDesc === Value.undefined || targetDesc.Configurable === Value.true) {
// i. Throw a TypeError exception.
return surroundingAgent.Throw('TypeError', 'ProxyGetOwnPropertyDescriptorNonConfigurable', P);
}
// b. If resultDesc has a [[Writable]] field and resultDesc.[[Writable]] is false, then
if ('Writable' in resultDesc && resultDesc.Writable === Value.false) {
// i. If targetDesc.[[Writable]] is true, throw a TypeError exception.
if (targetDesc.Writable === Value.true) {
return surroundingAgent.Throw('TypeError', 'ProxyGetOwnPropertyDescriptorNonConfigurableWritable', P);
}
}
}
// 18. Return resultDesc.
return resultDesc;
}
// #sec-proxy-object-internal-methods-and-internal-slots-defineownproperty-p-desc
function ProxyDefineOwnProperty(P, Desc) {
const O = this;
// 1. Assert: IsPropertyKey(P) is true.
Assert(IsPropertyKey(P));
// 2. Let handler be O.[[ProxyHandler]].
const handler = O.ProxyHandler;
// 3. If handler is null, throw a TypeError exception.
if (handler === Value.null) {
return surroundingAgent.Throw('TypeError', 'ProxyRevoked', 'defineProperty');
}
// 4. Assert: Type(handler) is Object.
Assert(Type(handler) === 'Object');
// 5. Let target be O.[[ProxyTarget]].
const target = O.ProxyTarget;
// 6. Let trap be ? GetMethod(handler, "defineProperty").
const trap = Q(GetMethod(handler, new Value('defineProperty')));
// 7. If trap is undefined, then
if (trap === Value.undefined) {
// a. Return ? target.[[DefineOwnProperty]](P, Desc).
return Q(target.DefineOwnProperty(P, Desc));
}
// 8. Let descObj be FromPropertyDescriptor(Desc).
const descObj = FromPropertyDescriptor(Desc);
// 9. Let booleanTrapResult be ! ToBoolean(? Call(trap, handler, « target, P, descObj »)).
const booleanTrapResult = ToBoolean(Q(Call(trap, handler, [target, P, descObj])));
// 10. If booleanTrapResult is false, return false.
if (booleanTrapResult === Value.false) {
return Value.false;
}
// 11. Let targetDesc be ? target.[[GetOwnProperty]](P).
const targetDesc = Q(target.GetOwnProperty(P));
// 12. Let extensibleTarget be ? IsExtensible(target).
const extensibleTarget = Q(IsExtensible(target));
let settingConfigFalse;
// 13. If Desc has a [[Configurable]] field and if Desc.[[Configurable]] is false, then
if (Desc.Configurable !== undefined && Desc.Configurable === Value.false) {
// a. Let settingConfigFalse be true.
settingConfigFalse = true;
} else {
// Else, let settingConfigFalse be false.
settingConfigFalse = false;
}
// 15. If targetDesc is undefined, then
if (targetDesc === Value.undefined) {
// a. If extensibleTarget is false, throw a TypeError exception.
if (extensibleTarget === Value.false) {
return surroundingAgent.Throw('TypeError', 'ProxyDefinePropertyNonExtensible', P);
}
// b. If settingConfigFalse is true, throw a TypeError exception.
if (settingConfigFalse === true) {
return surroundingAgent.Throw('TypeError', 'ProxyDefinePropertyNonConfigurable', P);
}
} else {
// a. If IsCompatiblePropertyDescriptor(extensibleTarget, Desc, targetDesc) is false, throw a TypeError exception.
if (IsCompatiblePropertyDescriptor(extensibleTarget, Desc, targetDesc) === Value.false) {
return surroundingAgent.Throw('TypeError', 'ProxyDefinePropertyIncompatible', P);
}
// b. If settingConfigFalse is true and targetDesc.[[Configurable]] is true, throw a TypeError exception.
if (settingConfigFalse === true && targetDesc.Configurable === Value.true) {
return surroundingAgent.Throw('TypeError', 'ProxyDefinePropertyNonConfigurable', P);
}
// c. If IsDataDescriptor(targetDesc) is true, targetDesc.[[Configurable]] is false, and targetDesc.[[Writable]] is true, then
if (IsDataDescriptor(targetDesc)
&& targetDesc.Configurable === Value.false
&& targetDesc.Writable === Value.true) {
// i. If Desc has a [[Writable]] field and Desc.[[Writable]] is false, throw a TypeError exception.
if ('Writable' in Desc && Desc.Writable === Value.false) {
return surroundingAgent.Throw('TypeError', 'ProxyDefinePropertyNonConfigurableWritable', P);
}
}
}
return Value.true;
}
// #sec-proxy-object-internal-methods-and-internal-slots-hasproperty-p
function ProxyHasProperty(P) {
const O = this;
Assert(IsPropertyKey(P));
const handler = O.ProxyHandler;
if (handler === Value.null) {
return surroundingAgent.Throw('TypeError', 'ProxyRevoked', 'has');
}
Assert(Type(handler) === 'Object');
const target = O.ProxyTarget;
const trap = Q(GetMethod(handler, new Value('has')));
if (trap === Value.undefined) {
return Q(target.HasProperty(P));
}
const booleanTrapResult = ToBoolean(Q(Call(trap, handler, [target, P])));
if (booleanTrapResult === Value.false) {
const targetDesc = Q(target.GetOwnProperty(P));
if (targetDesc !== Value.undefined) {
if (targetDesc.Configurable === Value.false) {
return surroundingAgent.Throw('TypeError', 'ProxyHasNonConfigurable', P);
}
const extensibleTarget = Q(IsExtensible(target));
if (extensibleTarget === Value.false) {
return surroundingAgent.Throw('TypeError', 'ProxyHasNonExtensible', P);
}
}
}
return booleanTrapResult;
}
// #sec-proxy-object-internal-methods-and-internal-slots-get-p-receiver
function ProxyGet(P, Receiver) {
const O = this;
Assert(IsPropertyKey(P));
const handler = O.ProxyHandler;
if (handler === Value.null) {
return surroundingAgent.Throw('TypeError', 'ProxyRevoked', 'get');
}
Assert(Type(handler) === 'Object');
const target = O.ProxyTarget;
const trap = Q(GetMethod(handler, new Value('get')));
if (trap === Value.undefined) {
return Q(target.Get(P, Receiver));
}
const trapResult = Q(Call(trap, handler, [target, P, Receiver]));
const targetDesc = Q(target.GetOwnProperty(P));
if (targetDesc !== Value.undefined && targetDesc.Configurable === Value.false) {
if (IsDataDescriptor(targetDesc) === true && targetDesc.Writable === Value.false) {
if (SameValue(trapResult, targetDesc.Value) === Value.false) {
return surroundingAgent.Throw('TypeError', 'ProxyGetNonConfigurableData', P);
}
}
if (IsAccessorDescriptor(targetDesc) === true && targetDesc.Get === Value.undefined) {
if (trapResult !== Value.undefined) {
return surroundingAgent.Throw('TypeError', 'ProxyGetNonConfigurableAccessor', P);
}
}
}
return trapResult;
}
// #sec-proxy-object-internal-methods-and-internal-slots-set-p-v-receiver
function ProxySet(P, V, Receiver) {
const O = this;
Assert(IsPropertyKey(P));
const handler = O.ProxyHandler;
if (handler === Value.null) {
return surroundingAgent.Throw('TypeError', 'ProxyRevoked', 'set');
}
Assert(Type(handler) === 'Object');
const target = O.ProxyTarget;
const trap = Q(GetMethod(handler, new Value('set')));
if (trap === Value.undefined) {
return Q(target.Set(P, V, Receiver));
}
const booleanTrapResult = ToBoolean(Q(Call(trap, handler, [target, P, V, Receiver])));
if (booleanTrapResult === Value.false) {
return Value.false;
}
const targetDesc = Q(target.GetOwnProperty(P));
if (targetDesc !== Value.undefined && targetDesc.Configurable === Value.false) {
if (IsDataDescriptor(targetDesc) === true && targetDesc.Writable === Value.false) {
if (SameValue(V, targetDesc.Value) === Value.false) {
return surroundingAgent.Throw('TypeError', 'ProxySetFrozenData', P);
}
}
if (IsAccessorDescriptor(targetDesc) === true) {
if (targetDesc.Set === Value.undefined) {
return surroundingAgent.Throw('TypeError', 'ProxySetFrozenAccessor', P);
}
}
}
return Value.true;
}
// #sec-proxy-object-internal-methods-and-internal-slots-delete-p
function ProxyDelete(P) {
const O = this;
// 1. Assert: IsPropertyKey(P) is true.
Assert(IsPropertyKey(P));
// 2. Let handler be O.[[ProxyHandler]].
const handler = O.ProxyHandler;
// 3. If handler is null, throw a TypeError exception.
if (handler === Value.null) {
return surroundingAgent.Throw('TypeError', 'ProxyRevoked', 'deleteProperty');
}
// 4. Assert: Type(handler) is Object.
Assert(Type(handler) === 'Object');
// 5. Let target be O.[[ProxyTarget]].
const target = O.ProxyTarget;
// 6. Let trap be ? GetMethod(handler, "deleteProperty").
const trap = Q(GetMethod(handler, new Value('deleteProperty')));
// 7. If trap is undefined, then
if (trap === Value.undefined) {
// a. Return ? target.[[Delete]](P).
return Q(target.Delete(P));
}
// 8. Let booleanTrapResult be ! ToBoolean(? Call(trap, handler, « target, P »)).
const booleanTrapResult = ToBoolean(Q(Call(trap, handler, [target, P])));
// 9. If booleanTrapResult is false, return false.
if (booleanTrapResult === Value.false) {
return Value.false;
}
// 10. Let targetDesc be ? target.[[GetOwnProperty]](P).
const targetDesc = Q(target.GetOwnProperty(P));
// 11. If targetDesc is undefined, return true.
if (targetDesc === Value.undefined) {
return Value.true;
}
// 12. If targetDesc.[[Configurable]] is false, throw a TypeError exception.
if (targetDesc.Configurable === Value.false) {
return surroundingAgent.Throw('TypeError', 'ProxyDeletePropertyNonConfigurable', P);
}
// 13. Let extensibleTarget be ? IsExtensible(target).
const extensibleTarget = Q(IsExtensible(target));
// 14. If extensibleTarget is false, throw a TypeError exception.
if (extensibleTarget === Value.false) {
return surroundingAgent.Throw('TypeError', 'ProxyDeletePropertyNonExtensible', P);
}
// 15. Return true.
return Value.true;
}
// #sec-proxy-object-internal-methods-and-internal-slots-ownpropertykeys
function ProxyOwnPropertyKeys() {
const O = this;
const handler = O.ProxyHandler;
if (handler === Value.null) {
return surroundingAgent.Throw('TypeError', 'ProxyRevoked', 'ownKeys');
}
Assert(Type(handler) === 'Object');
const target = O.ProxyTarget;
const trap = Q(GetMethod(handler, new Value('ownKeys')));
if (trap === Value.undefined) {
return Q(target.OwnPropertyKeys());
}
const trapResultArray = Q(Call(trap, handler, [target]));
const trapResult = Q(CreateListFromArrayLike(trapResultArray, ['String', 'Symbol']));
if (new ValueSet(trapResult).size !== trapResult.length) {
return surroundingAgent.Throw('TypeError', 'ProxyOwnKeysDuplicateEntries');
}
const extensibleTarget = Q(IsExtensible(target));
const targetKeys = Q(target.OwnPropertyKeys());
// Assert: targetKeys is a List containing only String and Symbol values.
// Assert: targetKeys contains no duplicate entries.
const targetConfigurableKeys = [];
const targetNonconfigurableKeys = [];
for (const key of targetKeys) {
const desc = Q(target.GetOwnProperty(key));
if (desc !== Value.undefined && desc.Configurable === Value.false) {
targetNonconfigurableKeys.push(key);
} else {
targetConfigurableKeys.push(key);
}
}
if (extensibleTarget === Value.true && targetNonconfigurableKeys.length === 0) {
return trapResult;
}
const uncheckedResultKeys = new ValueSet(trapResult);
for (const key of targetNonconfigurableKeys) {
if (!uncheckedResultKeys.has(key)) {
return surroundingAgent.Throw('TypeError', 'ProxyOwnKeysMissing', 'non-configurable key');
}
uncheckedResultKeys.delete(key);
}
if (extensibleTarget === Value.true) {
return trapResult;
}
for (const key of targetConfigurableKeys) {
if (!uncheckedResultKeys.has(key)) {
return surroundingAgent.Throw('TypeError', 'ProxyOwnKeysMissing', 'configurable key');
}
uncheckedResultKeys.delete(key);
}
if (uncheckedResultKeys.size > 0) {
return surroundingAgent.Throw('TypeError', 'ProxyOwnKeysNonExtensible');
}
return trapResult;
}
// #sec-proxy-object-internal-methods-and-internal-slots-call-thisargument-argumentslist
function ProxyCall(thisArgument, argumentsList) {
const O = this;
const handler = O.ProxyHandler;
if (handler === Value.null) {
return surroundingAgent.Throw('TypeError', 'ProxyRevoked', 'apply');
}
Assert(Type(handler) === 'Object');
const target = O.ProxyTarget;
const trap = Q(GetMethod(handler, new Value('apply')));
if (trap === Value.undefined) {
return Q(Call(target, thisArgument, argumentsList));
}
const argArray = X(CreateArrayFromList(argumentsList));
return Q(Call(trap, handler, [target, thisArgument, argArray]));
}
// #sec-proxy-object-internal-methods-and-internal-slots-construct-argumentslist-newtarget
function ProxyConstruct(argumentsList, newTarget) {
const O = this;
const handler = O.ProxyHandler;
if (handler === Value.null) {
return surroundingAgent.Throw('TypeError', 'ProxyRevoked', 'construct');
}
Assert(Type(handler) === 'Object');
const target = O.ProxyTarget;
Assert(IsConstructor(target) === Value.true);
const trap = Q(GetMethod(handler, new Value('construct')));
if (trap === Value.undefined) {
return Q(Construct(target, argumentsList, newTarget));
}
const argArray = X(CreateArrayFromList(argumentsList));
const newObj = Q(Call(trap, handler, [target, argArray, newTarget]));
if (Type(newObj) !== 'Object') {
return surroundingAgent.Throw('TypeError', 'NotAnObject', newObj);
}
return newObj;
}
export function isProxyExoticObject(O) {
return 'ProxyHandler' in O;
}
// #sec-proxycreate
export function ProxyCreate(target, handler) {
// 1. If Type(target) is not Object, throw a TypeError exception.
if (Type(target) !== 'Object') {
return surroundingAgent.Throw('TypeError', 'CannotCreateProxyWith', 'non-object', 'target');
}
// 2. If Type(handler) is not Object, throw a TypeError exception.
if (Type(handler) !== 'Object') {
return surroundingAgent.Throw('TypeError', 'CannotCreateProxyWith', 'non-object', 'handler');
}
// 3. Let P be ! MakeBasicObject(« [[ProxyHandler]], [[ProxyTarget]] »).
const P = X(MakeBasicObject(['ProxyHandler', 'ProxyTarget']));
// 4. Set P's essential internal methods, except for [[Call]] and [[Construct]], to the definitions specified in 9.5.
P.GetPrototypeOf = ProxyGetPrototypeOf;
P.SetPrototypeOf = ProxySetPrototypeOf;
P.IsExtensible = ProxyIsExtensible;
P.PreventExtensions = ProxyPreventExtensions;
P.GetOwnProperty = ProxyGetOwnProperty;
P.DefineOwnProperty = ProxyDefineOwnProperty;
P.HasProperty = ProxyHasProperty;
P.Get = ProxyGet;
P.Set = ProxySet;
P.Delete = ProxyDelete;
P.OwnPropertyKeys = ProxyOwnPropertyKeys;
// 5. If IsCallable(target) is true, then
if (IsCallable(target) === Value.true) {
// a. Set P.[[Call]] as specified in #sec-proxy-object-internal-methods-and-internal-slots-call-thisargument-argumentslist.
P.Call = ProxyCall;
// b. If IsConstructor(target) is true, then
if (IsConstructor(target) === Value.true) {
// i. Set P.[[Construct]] as specified in #sec-proxy-object-internal-methods-and-internal-slots-construct-argumentslist-newtarget.
P.Construct = ProxyConstruct;
}
}
// 6. Set P.[[ProxyTarget]] to target.
P.ProxyTarget = target;
// 7. Set P.[[ProxyHandler]] to handler.
P.ProxyHandler = handler;
// 8. Return P.
return P;
}
-366
View File
@@ -1,366 +0,0 @@
import {
Descriptor,
Value,
} from '../value.mjs';
import { NewGlobalEnvironment } from '../environment.mjs';
import { Q, X } from '../completion.mjs';
import { BootstrapObjectPrototype } from '../intrinsics/ObjectPrototype.mjs';
import { BootstrapObject } from '../intrinsics/Object.mjs';
import { BootstrapArrayPrototype } from '../intrinsics/ArrayPrototype.mjs';
import { BootstrapArray } from '../intrinsics/Array.mjs';
import { BootstrapBigInt } from '../intrinsics/BigInt.mjs';
import { BootstrapBigIntPrototype } from '../intrinsics/BigIntPrototype.mjs';
import { BootstrapBooleanPrototype } from '../intrinsics/BooleanPrototype.mjs';
import { BootstrapBoolean } from '../intrinsics/Boolean.mjs';
import { BootstrapNumberPrototype } from '../intrinsics/NumberPrototype.mjs';
import { BootstrapNumber } from '../intrinsics/Number.mjs';
import { BootstrapFunctionPrototype } from '../intrinsics/FunctionPrototype.mjs';
import { BootstrapFunction } from '../intrinsics/Function.mjs';
import { BootstrapSymbolPrototype } from '../intrinsics/SymbolPrototype.mjs';
import { BootstrapSymbol } from '../intrinsics/Symbol.mjs';
import { BootstrapMath } from '../intrinsics/Math.mjs';
import { BootstrapDatePrototype } from '../intrinsics/DatePrototype.mjs';
import { BootstrapDate } from '../intrinsics/Date.mjs';
import { BootstrapRegExpPrototype } from '../intrinsics/RegExpPrototype.mjs';
import { BootstrapRegExp } from '../intrinsics/RegExp.mjs';
import { BootstrapPromisePrototype } from '../intrinsics/PromisePrototype.mjs';
import { BootstrapPromise } from '../intrinsics/Promise.mjs';
import { BootstrapProxy } from '../intrinsics/Proxy.mjs';
import { BootstrapReflect } from '../intrinsics/Reflect.mjs';
import { BootstrapStringPrototype } from '../intrinsics/StringPrototype.mjs';
import { BootstrapString } from '../intrinsics/String.mjs';
import { BootstrapErrorPrototype } from '../intrinsics/ErrorPrototype.mjs';
import { BootstrapError } from '../intrinsics/Error.mjs';
import { BootstrapNativeError } from '../intrinsics/NativeError.mjs';
import { BootstrapIteratorPrototype } from '../intrinsics/IteratorPrototype.mjs';
import { BootstrapAsyncIteratorPrototype } from '../intrinsics/AsyncIteratorPrototype.mjs';
import { BootstrapArrayIteratorPrototype } from '../intrinsics/ArrayIteratorPrototype.mjs';
import { BootstrapMapIteratorPrototype } from '../intrinsics/MapIteratorPrototype.mjs';
import { BootstrapSetIteratorPrototype } from '../intrinsics/SetIteratorPrototype.mjs';
import { BootstrapStringIteratorPrototype } from '../intrinsics/StringIteratorPrototype.mjs';
import { BootstrapRegExpStringIteratorPrototype } from '../intrinsics/RegExpStringIteratorPrototype.mjs';
import { BootstrapForInIteratorPrototype } from '../intrinsics/ForInIteratorPrototype.mjs';
import { BootstrapMapPrototype } from '../intrinsics/MapPrototype.mjs';
import { BootstrapMap } from '../intrinsics/Map.mjs';
import { BootstrapSetPrototype } from '../intrinsics/SetPrototype.mjs';
import { BootstrapSet } from '../intrinsics/Set.mjs';
import { BootstrapGenerator } from '../intrinsics/Generator.mjs';
import { BootstrapGeneratorFunction } from '../intrinsics/GeneratorFunction.mjs';
import { BootstrapGeneratorPrototype } from '../intrinsics/GeneratorPrototype.mjs';
import { BootstrapAsyncFunctionPrototype } from '../intrinsics/AsyncFunctionPrototype.mjs';
import { BootstrapAsyncFunction } from '../intrinsics/AsyncFunction.mjs';
import { BootstrapAsyncGenerator } from '../intrinsics/AsyncGenerator.mjs';
import { BootstrapAsyncGeneratorFunction } from '../intrinsics/AsyncGeneratorFunction.mjs';
import { BootstrapAsyncGeneratorPrototype } from '../intrinsics/AsyncGeneratorPrototype.mjs';
import { BootstrapAsyncFromSyncIteratorPrototype } from '../intrinsics/AsyncFromSyncIteratorPrototype.mjs';
import { BootstrapArrayBuffer } from '../intrinsics/ArrayBuffer.mjs';
import { BootstrapArrayBufferPrototype } from '../intrinsics/ArrayBufferPrototype.mjs';
import { BootstrapJSON } from '../intrinsics/JSON.mjs';
import { BootstrapEval } from '../intrinsics/eval.mjs';
import { BootstrapIsFinite } from '../intrinsics/isFinite.mjs';
import { BootstrapIsNaN } from '../intrinsics/isNaN.mjs';
import { BootstrapParseFloat } from '../intrinsics/parseFloat.mjs';
import { BootstrapParseInt } from '../intrinsics/parseInt.mjs';
import { BootstrapURIHandling } from '../intrinsics/URIHandling.mjs';
import { BootstrapThrowTypeError } from '../intrinsics/ThrowTypeError.mjs';
import { BootstrapTypedArray } from '../intrinsics/TypedArray.mjs';
import { BootstrapTypedArrayPrototype } from '../intrinsics/TypedArrayPrototype.mjs';
import { BootstrapTypedArrayConstructors } from '../intrinsics/TypedArrayConstructors.mjs';
import { BootstrapTypedArrayPrototypes } from '../intrinsics/TypedArrayPrototypes.mjs';
import { BootstrapDataView } from '../intrinsics/DataView.mjs';
import { BootstrapDataViewPrototype } from '../intrinsics/DataViewPrototype.mjs';
import { BootstrapWeakMapPrototype } from '../intrinsics/WeakMapPrototype.mjs';
import { BootstrapWeakMap } from '../intrinsics/WeakMap.mjs';
import { BootstrapWeakSetPrototype } from '../intrinsics/WeakSetPrototype.mjs';
import { BootstrapWeakSet } from '../intrinsics/WeakSet.mjs';
import { BootstrapAggregateError } from '../intrinsics/AggregateError.mjs';
import { BootstrapAggregateErrorPrototype } from '../intrinsics/AggregateErrorPrototype.mjs';
import { BootstrapWeakRefPrototype } from '../intrinsics/WeakRefPrototype.mjs';
import { BootstrapWeakRef } from '../intrinsics/WeakRef.mjs';
import { BootstrapFinalizationRegistryPrototype } from '../intrinsics/FinalizationRegistryPrototype.mjs';
import { BootstrapFinalizationRegistry } from '../intrinsics/FinalizationRegistry.mjs';
import {
Assert,
DefinePropertyOrThrow,
OrdinaryObjectCreate,
} from './all.mjs';
// 8.2 #sec-code-realms
export class Realm {
constructor() {
this.Intrinsics = undefined;
this.GlobalObject = undefined;
this.GlobalEnv = undefined;
this.TemplateMap = undefined;
this.HostDefined = undefined;
this.randomState = undefined;
}
mark(m) {
m(this.GlobalObject);
m(this.GlobalEnv);
for (const v of Object.values(this.Intrinsics)) {
m(v);
}
for (const v of Object.values(this.TemplateMap)) {
m(v);
}
}
}
// 8.2.1 #sec-createrealm
export function CreateRealm() {
const realmRec = new Realm();
CreateIntrinsics(realmRec);
realmRec.GlobalObject = Value.undefined;
realmRec.GlobalEnv = Value.undefined;
realmRec.TemplateMap = [];
return realmRec;
}
function AddRestrictedFunctionProperties(F, realm) {
Assert(realm.Intrinsics['%ThrowTypeError%']);
const thrower = realm.Intrinsics['%ThrowTypeError%'];
X(DefinePropertyOrThrow(F, new Value('caller'), Descriptor({
Get: thrower,
Set: thrower,
Enumerable: Value.false,
Configurable: Value.true,
})));
X(DefinePropertyOrThrow(F, new Value('arguments'), Descriptor({
Get: thrower,
Set: thrower,
Enumerable: Value.false,
Configurable: Value.true,
})));
}
// #sec-createintrinsics
export function CreateIntrinsics(realmRec) {
const intrinsics = Object.create(null);
realmRec.Intrinsics = intrinsics;
intrinsics['%Object.prototype%'] = OrdinaryObjectCreate(Value.null);
BootstrapFunctionPrototype(realmRec);
BootstrapObjectPrototype(realmRec);
BootstrapThrowTypeError(realmRec);
BootstrapEval(realmRec);
BootstrapIsFinite(realmRec);
BootstrapIsNaN(realmRec);
BootstrapParseFloat(realmRec);
BootstrapParseInt(realmRec);
BootstrapURIHandling(realmRec);
BootstrapObject(realmRec);
BootstrapErrorPrototype(realmRec);
BootstrapError(realmRec);
BootstrapNativeError(realmRec);
BootstrapAggregateErrorPrototype(realmRec);
BootstrapAggregateError(realmRec);
BootstrapFunction(realmRec);
BootstrapIteratorPrototype(realmRec);
BootstrapAsyncIteratorPrototype(realmRec);
BootstrapArrayIteratorPrototype(realmRec);
BootstrapMapIteratorPrototype(realmRec);
BootstrapSetIteratorPrototype(realmRec);
BootstrapStringIteratorPrototype(realmRec);
BootstrapRegExpStringIteratorPrototype(realmRec);
BootstrapForInIteratorPrototype(realmRec);
BootstrapStringPrototype(realmRec);
BootstrapString(realmRec);
BootstrapArrayPrototype(realmRec);
BootstrapArray(realmRec);
BootstrapBooleanPrototype(realmRec);
BootstrapBoolean(realmRec);
BootstrapNumberPrototype(realmRec);
BootstrapNumber(realmRec);
BootstrapBigIntPrototype(realmRec);
BootstrapBigInt(realmRec);
BootstrapSymbolPrototype(realmRec);
BootstrapSymbol(realmRec);
BootstrapPromisePrototype(realmRec);
BootstrapPromise(realmRec);
BootstrapProxy(realmRec);
BootstrapReflect(realmRec);
BootstrapMath(realmRec);
BootstrapDatePrototype(realmRec);
BootstrapDate(realmRec);
BootstrapRegExpPrototype(realmRec);
BootstrapRegExp(realmRec);
BootstrapSetPrototype(realmRec);
BootstrapSet(realmRec);
BootstrapMapPrototype(realmRec);
BootstrapMap(realmRec);
BootstrapGeneratorPrototype(realmRec);
BootstrapGenerator(realmRec);
BootstrapGeneratorFunction(realmRec);
BootstrapAsyncFunctionPrototype(realmRec);
BootstrapAsyncFunction(realmRec);
BootstrapAsyncGeneratorPrototype(realmRec);
BootstrapAsyncGenerator(realmRec);
BootstrapAsyncGeneratorFunction(realmRec);
BootstrapAsyncFromSyncIteratorPrototype(realmRec);
BootstrapArrayBufferPrototype(realmRec);
BootstrapArrayBuffer(realmRec);
BootstrapTypedArrayPrototype(realmRec);
BootstrapTypedArray(realmRec);
BootstrapTypedArrayPrototypes(realmRec);
BootstrapTypedArrayConstructors(realmRec);
BootstrapDataViewPrototype(realmRec);
BootstrapDataView(realmRec);
BootstrapJSON(realmRec);
BootstrapWeakMapPrototype(realmRec);
BootstrapWeakMap(realmRec);
BootstrapWeakSetPrototype(realmRec);
BootstrapWeakSet(realmRec);
BootstrapWeakRefPrototype(realmRec);
BootstrapWeakRef(realmRec);
BootstrapFinalizationRegistryPrototype(realmRec);
BootstrapFinalizationRegistry(realmRec);
AddRestrictedFunctionProperties(intrinsics['%Function.prototype%'], realmRec);
return intrinsics;
}
// 8.2.3 #sec-setrealmglobalobject
export function SetRealmGlobalObject(realmRec, globalObj, thisValue) {
const intrinsics = realmRec.Intrinsics;
if (globalObj === Value.undefined) {
globalObj = OrdinaryObjectCreate(intrinsics['%Object.prototype%']);
}
if (thisValue === Value.undefined) {
thisValue = globalObj;
}
realmRec.GlobalObject = globalObj;
const newGlobalEnv = NewGlobalEnvironment(globalObj, thisValue);
realmRec.GlobalEnv = newGlobalEnv;
return realmRec;
}
// 8.2.4 #sec-setdefaultglobalbindings
export function SetDefaultGlobalBindings(realmRec) {
const global = realmRec.GlobalObject;
// Value Properties of the Global Object
[
['Infinity', new Value(Infinity)],
['NaN', new Value(NaN)],
['undefined', Value.undefined],
].forEach(([name, value]) => {
Q(DefinePropertyOrThrow(global, new Value(name), Descriptor({
Value: value,
Writable: Value.false,
Enumerable: Value.false,
Configurable: Value.false,
})));
});
Q(DefinePropertyOrThrow(global, new Value('globalThis'), Descriptor({
Value: realmRec.GlobalEnv.GlobalThisValue,
Writable: Value.true,
Enumerable: Value.false,
Configurable: Value.true,
})));
[
// Function Properties of the Global Object
'eval',
'isFinite',
'isNaN',
'parseFloat',
'parseInt',
'decodeURI',
'decodeURIComponent',
'encodeURI',
'encodeURIComponent',
// Constructor Properties of the Global Object
'AggregateError',
'Array',
'ArrayBuffer',
'Boolean',
'BigInt',
'BigInt64Array',
'BigUint64Array',
'DataView',
'Date',
'Error',
'EvalError',
'FinalizationRegistry',
'Float32Array',
'Float64Array',
'Function',
'Int8Array',
'Int16Array',
'Int32Array',
'Map',
'Number',
'Object',
'Promise',
'Proxy',
'RangeError',
'ReferenceError',
'RegExp',
'Set',
// 'SharedArrayBuffer',
'String',
'Symbol',
'SyntaxError',
'TypeError',
'Uint8Array',
'Uint8ClampedArray',
'Uint16Array',
'Uint32Array',
'URIError',
'WeakMap',
'WeakRef',
'WeakSet',
// Other Properties of the Global Object
// 'Atomics',
'JSON',
'Math',
'Reflect',
].forEach((name) => {
Q(DefinePropertyOrThrow(global, new Value(name), Descriptor({
Value: realmRec.Intrinsics[`%${name}%`],
Writable: Value.true,
Enumerable: Value.false,
Configurable: Value.true,
})));
});
return global;
}
@@ -1,142 +0,0 @@
import { surroundingAgent } from '../engine.mjs';
import { PrimitiveValue, Type, Value } from '../value.mjs';
import {
NormalCompletion,
Q,
ReturnIfAbrupt,
X,
} from '../completion.mjs';
import {
Assert,
GetGlobalObject,
ToObject,
Set,
} from './all.mjs';
// 6.2.4.1 #sec-getbase
export function GetBase(V) {
Assert(Type(V) === 'Reference');
return V.BaseValue;
}
// 6.2.4.2 #sec-getreferencedname
export function GetReferencedName(V) {
Assert(Type(V) === 'Reference');
return V.ReferencedName;
}
// 6.2.4.3 #sec-isstrictreference
export function IsStrictReference(V) {
Assert(Type(V) === 'Reference');
return V.StrictReference;
}
// 6.2.4.4 #sec-hasprimitivebase
export function HasPrimitiveBase(V) {
Assert(Type(V) === 'Reference');
if (V.BaseValue instanceof PrimitiveValue) {
return Value.true;
}
return Value.false;
}
// 6.2.4.5 #sec-ispropertyreference
export function IsPropertyReference(V) {
Assert(Type(V) === 'Reference');
if (Type(V.BaseValue) === 'Object' || HasPrimitiveBase(V) === Value.true) {
return Value.true;
}
return Value.false;
}
// 6.2.4.6 #sec-isunresolvablereference
export function IsUnresolvableReference(V) {
Assert(Type(V) === 'Reference');
if (V.BaseValue === Value.undefined) {
return Value.true;
}
return Value.false;
}
// 6.2.4.7 #sec-issuperreference
export function IsSuperReference(V) {
// 1. Assert: Type(V) is Reference.
Assert(Type(V) === 'Reference');
// 2. If V has a thisValue component, return true; otherwise return false.
return 'thisValue' in V ? Value.true : Value.false;
}
// 6.2.4.8 #sec-getvalue
export function GetValue(V) {
ReturnIfAbrupt(V);
if (Type(V) !== 'Reference') {
return V;
}
let base = GetBase(V);
if (IsUnresolvableReference(V) === Value.true) {
return surroundingAgent.Throw('ReferenceError', 'NotDefined', GetReferencedName(V));
}
if (IsPropertyReference(V) === Value.true) {
if (HasPrimitiveBase(V) === Value.true) {
Assert(base !== Value.undefined && base !== Value.null);
base = X(ToObject(base));
}
return Q(base.Get(GetReferencedName(V), GetThisValue(V)));
} else {
return Q(base.GetBindingValue(GetReferencedName(V), IsStrictReference(V)));
}
}
// 6.2.4.9 #sec-putvalue
export function PutValue(V, W) {
ReturnIfAbrupt(V);
ReturnIfAbrupt(W);
if (Type(V) !== 'Reference') {
return surroundingAgent.Throw('ReferenceError', 'NotDefined', V);
}
let base = GetBase(V);
if (IsUnresolvableReference(V) === Value.true) {
if (IsStrictReference(V) === Value.true) {
return surroundingAgent.Throw('ReferenceError', 'NotDefined', GetReferencedName(V));
}
const globalObj = GetGlobalObject();
return Q(Set(globalObj, GetReferencedName(V), W, Value.false));
} else if (IsPropertyReference(V) === Value.true) {
if (HasPrimitiveBase(V) === Value.true) {
Assert(Type(base) !== 'Undefined' && Type(base) !== 'Null');
base = X(ToObject(base));
}
const succeeded = Q(base.Set(GetReferencedName(V), W, GetThisValue(V)));
if (succeeded === Value.false && IsStrictReference(V) === Value.true) {
return surroundingAgent.Throw('TypeError', 'CannotSetProperty', GetReferencedName(V), base);
}
return NormalCompletion(Value.undefined);
} else {
return Q(base.SetMutableBinding(GetReferencedName(V), W, IsStrictReference(V)));
}
}
// 6.2.4.10 #sec-getthisvalue
export function GetThisValue(V) {
// 1. Assert: IsPropertyReference(V) is true.
Assert(IsPropertyReference(V) === Value.true);
// 2. If IsSuperReference(V) is true, then
if (IsSuperReference(V) === Value.true) {
// a. Return the value of the thisValue component of the reference V.
return V.thisValue;
}
// 3. Return GetBase(V).
return GetBase(V);
}
// 6.2.4.11 #sec-initializereferencedbinding
export function InitializeReferencedBinding(V, W) {
ReturnIfAbrupt(V);
ReturnIfAbrupt(W);
Assert(Type(V) === 'Reference');
Assert(IsUnresolvableReference(V) === Value.false);
const base = GetBase(V);
Assert(Type(base) === 'EnvironmentRecord');
return base.InitializeBinding(GetReferencedName(V), W);
}
@@ -1,254 +0,0 @@
import { surroundingAgent } from '../engine.mjs';
import { Descriptor, Value, Type } from '../value.mjs';
import { Q, X } from '../completion.mjs';
import { Evaluate_Pattern } from '../runtime-semantics/all.mjs';
import { ParsePattern } from '../parse.mjs';
import { isLineTerminator } from '../parser/Lexer.mjs';
import {
ArrayCreate,
Assert,
CreateArrayFromList,
CreateDataPropertyOrThrow,
DefinePropertyOrThrow,
OrdinaryCreateFromConstructor,
OrdinaryObjectCreate,
Set,
ToString,
} from './all.mjs';
// #sec-regexpalloc
export function RegExpAlloc(newTarget) {
const obj = Q(OrdinaryCreateFromConstructor(newTarget, '%RegExp.prototype%', ['RegExpMatcher', 'OriginalSource', 'OriginalFlags']));
X(DefinePropertyOrThrow(obj, new Value('lastIndex'), Descriptor({
Writable: Value.true,
Enumerable: Value.false,
Configurable: Value.false,
})));
return obj;
}
// #sec-regexpinitialize
export function RegExpInitialize(obj, pattern, flags) {
let P;
// 1. If pattern is undefined, let P be the empty String.
if (pattern === Value.undefined) {
P = new Value('');
} else { // 2. Else, let P be ? ToString(pattern).
P = Q(ToString(pattern));
}
let F;
// 3. If flags is undefined, let F be the empty String.
if (flags === Value.undefined) {
F = new Value('');
} else { // 4. Else, let F be ? ToString(flags).
F = Q(ToString(flags));
}
const f = F.stringValue();
// 5. If F contains any code unit other than "g", "i", "m", "s", "u", or "y" or if it contains the same code unit more than once, throw a SyntaxError exception.
if (/^[gimsuy]*$/.test(f) === false || (new globalThis.Set(f).size !== f.length)) {
return surroundingAgent.Throw('SyntaxError', 'InvalidRegExpFlags', f);
}
// 6. If F contains "u", let u be true; else let u be false.
const u = f.includes('u');
// 7. If u is true, then
// a. Let patternText be ! UTF16DecodeString(P).
// b. Let patternCharacters be a List whose elements are the code points of patternText.
// 8. Else,
// a. Let patternText be the result of interpreting each of P's 16-bit elements as a Unicode BMP code point. UTF-16 decoding is not applied to the elements.
// b. Let patternCharacters be a List whose elements are the code unit elements of P.
// 9. Let parseResult be ParsePattern(patternText, u).
const patternText = P.stringValue();
const parseResult = ParsePattern(patternText, u);
// 10. If parseResult is a non-empty List of SyntaxError objects, throw a SyntaxError exception.
if (Array.isArray(parseResult)) {
return surroundingAgent.Throw(parseResult[0]);
}
obj.parsedPattern = parseResult;
// 11. Assert: parseResult is a Parse Node for Pattern.
Assert(parseResult.type === 'Pattern');
// 12. Set obj.[[OriginalSource]] to P.
obj.OriginalSource = P;
// 13. Set obj.[[OriginalFlags]] to F.
obj.OriginalFlags = F;
// 14. Set obj.[[RegExpMatcher]] to the Abstract Closure that evaluates parseResult by
// applying the semantics provided in 21.2.2 using patternCharacters as the pattern's
// List of SourceCharacter values and F as the flag parameters.
obj.RegExpMatcher = Evaluate_Pattern(parseResult, F.stringValue());
// 15. Perform ? Set(obj, "lastIndex", 0, true).
Q(Set(obj, new Value('lastIndex'), new Value(0), Value.true));
// 16. Return obj.
return obj;
}
// 21.2.3.2.3 #sec-regexpcreate
export function RegExpCreate(P, F) {
const obj = Q(RegExpAlloc(surroundingAgent.intrinsic('%RegExp%')));
return Q(RegExpInitialize(obj, P, F));
}
// #sec-escaperegexppattern
export function EscapeRegExpPattern(P, _F) {
const source = P.stringValue();
if (source === '') {
return new Value('(:?)');
}
let index = 0;
let escaped = '';
let inClass = false;
while (index < source.length) {
const c = source[index];
switch (c) {
case '\\':
index += 1;
if (isLineTerminator(source[index])) {
// nothing
} else {
escaped += '\\';
}
break;
case '/':
index += 1;
if (inClass) {
escaped += '/';
} else {
escaped += '\\/';
}
break;
case '[':
inClass = true;
index += 1;
escaped += '[';
break;
case ']':
inClass = false;
index += 1;
escaped += ']';
break;
case '\n':
index += 1;
escaped += '\\n';
break;
case '\r':
index += 1;
escaped += '\\r';
break;
case '\u2028':
index += 1;
escaped += '\\u2028';
break;
case '\u2029':
index += 1;
escaped += '\\u2029';
break;
default:
index += 1;
escaped += c;
break;
}
}
return new Value(escaped);
}
// https://tc39.es/proposal-regexp-match-indices/#sec-getstringindex
export function GetStringIndex(S, Input, e) {
// 1. Assert: Type(S) is String.
Assert(Type(S) === 'String');
// 2. Assert: Input is a List of the code points of S interpreted as a UTF-16 encoded string.
Assert(Array.isArray(Input));
// 3. Assert: e is an integer value ≥ 0 and < the number of elements in Input.
Assert(e >= 0);
// 4. Let eUTF be the smallest index into S that corresponds to the character at element e of Input.
// If e is greater than or equal to the number of elements in Input, then eUTF is the number of code units in S.
let eUTF = 0;
if (e >= Input.length) {
eUTF = S.stringValue().length;
} else {
for (let i = 0; i < e; i += 1) {
eUTF += Input[i].length;
}
}
// 5. Return eUTF.
return eUTF;
}
// https://tc39.es/proposal-regexp-match-indices/#sec-getmatchstring
export function GetMatchString(S, match) {
// 1. Assert: Type(S) is String.
Assert(Type(S) === 'String');
// 2. Assert: match is a Match Record.
Assert('StartIndex' in match && 'EndIndex' in match);
// 3. Assert: match.[[StartIndex]] is an integer value ≥ 0 and < the length of S.
Assert(match.StartIndex >= 0 && match.StartIndex < S.stringValue().length);
// 4. Assert: match.[[EndIndex]] is an integer value ≥ match.[[StartIndex]] and ≤ the length of S.
Assert(match.EndIndex >= match.StartIndex && match.EndIndex <= S.stringValue().length);
// 5. Return the portion of S between offset match.[[StartIndex]] inclusive and offset match.[[EndIndex]] exclusive.
return new Value(S.stringValue().slice(match.StartIndex, match.EndIndex));
}
// https://tc39.es/proposal-regexp-match-indices/#sec-getmatchindicesarray
export function GetMatchIndicesArray(S, match) {
// 1. Assert: Type(S) is String.
Assert(Type(S) === 'String');
// 2. Assert: match is a Match Record.
Assert('StartIndex' in match && 'EndIndex' in match);
// 3. Assert: match.[[StartIndex]] is an integer value ≥ 0 and < the length of S.
Assert(match.StartIndex >= 0 && match.StartIndex < S.stringValue().length);
// 4. Assert: match.[[EndIndex]] is an integer value ≥ match.[[StartIndex]] and ≤ the length of S.
Assert(match.EndIndex >= match.StartIndex && match.EndIndex <= S.stringValue().length);
// 1. Return CreateArrayFromList(« match.[[StartIndex]], match.[[EndIndex]] »).
return CreateArrayFromList([
new Value(match.StartIndex),
new Value(match.EndIndex),
]);
}
// https://tc39.es/proposal-regexp-match-indices/#sec-makeindicesarray
export function MakeIndicesArray(S, indices, groupNames) {
// 1. Assert: Type(S) is String.
Assert(Type(S) === 'String');
// 2. Assert: indices is a List.
Assert(Array.isArray(indices));
// 3. Assert: groupNames is a List or is undefined.
Assert(Array.isArray(indices) || groupNames === Value.undefined);
// 4. Let n be the number of elements in indices.
const n = indices.length;
// 5. Assert: n < 2**32-1.
Assert(n < (2 ** 32) - 1);
// 6. Set A to ! ArrayCreate(n).
// 7. Assert: The value of A's "length" property is n.
const A = X(ArrayCreate(new Value(n)));
// 8. If groupNames is not undefined, then
let groups;
if (groupNames !== Value.undefined) {
// a. Let groups be ! ObjectCreate(null).
groups = X(OrdinaryObjectCreate(Value.null));
} else { // 9. Else,
// a. Let groups be undefined.
groups = Value.undefined;
}
// 10. Perform ! CreateDataProperty(A, "groups", groups).
X(CreateDataPropertyOrThrow(A, new Value('groups'), groups));
// 11. For each integer i such that i ≥ 0 and i < n, do
for (let i = 0; i < n; i += 1) {
// a. Let matchIndices be indices[i].
const matchIndices = indices[i];
// b. If matchIndices is not undefined, then
let matchIndicesArray;
if (matchIndices !== Value.undefined) {
// i. Let matchIndicesArray be ! GetMatchIndicesArray(S, matchIndices).
matchIndicesArray = X(GetMatchIndicesArray(S, matchIndices));
} else { // c. Else,
// i. Let matchIndicesArray be undefined.
matchIndicesArray = Value.undefined;
}
// d. Perform ! CreateDataProperty(A, ! ToString(i), matchIndicesArray).
X(CreateDataPropertyOrThrow(A, X(ToString(new Value(i))), matchIndicesArray));
// e. If groupNames is not undefined and groupNames[i] is not undefined, then
if (groupNames !== Value.undefined && groupNames[i] !== Value.undefined) {
// i. Perform ! CreateDataProperty(groups, groupNames[i], matchIndicesArray).
X(CreateDataPropertyOrThrow(groups, groupNames[i], matchIndicesArray));
}
}
// 12. Return A.
return A;
}
-210
View File
@@ -1,210 +0,0 @@
import { surroundingAgent } from '../engine.mjs';
import {
DataBlock,
Descriptor,
Type,
Value,
} from '../value.mjs';
import { NormalCompletion, Q, X } from '../completion.mjs';
import {
Assert,
CreateDataProperty,
Get,
HasProperty,
IsCallable,
OrdinaryObjectCreate,
ToBoolean,
} from './all.mjs';
// 6.2.5.1 IsAccessorDescriptor
export function IsAccessorDescriptor(Desc) {
if (Type(Desc) === 'Undefined') {
return false;
}
if (Desc.Get === undefined && Desc.Set === undefined) {
return false;
}
return true;
}
// 6.2.5.2 IsDataDescriptor
export function IsDataDescriptor(Desc) {
if (Type(Desc) === 'Undefined') {
return false;
}
if (Desc.Value === undefined && Desc.Writable === undefined) {
return false;
}
return true;
}
// 6.2.5.3 IsGenericDescriptor
export function IsGenericDescriptor(Desc) {
if (Type(Desc) === 'Undefined') {
return false;
}
if (!IsAccessorDescriptor(Desc) && !IsDataDescriptor(Desc)) {
return true;
}
return false;
}
// 6.2.5.4 #sec-frompropertydescriptor
export function FromPropertyDescriptor(Desc) {
if (Type(Desc) === 'Undefined') {
return Value.undefined;
}
const obj = OrdinaryObjectCreate(surroundingAgent.intrinsic('%Object.prototype%'));
if (Desc.Value !== undefined) {
X(CreateDataProperty(obj, new Value('value'), Desc.Value));
}
if (Desc.Writable !== undefined) {
X(CreateDataProperty(obj, new Value('writable'), Desc.Writable));
}
if (Desc.Get !== undefined) {
X(CreateDataProperty(obj, new Value('get'), Desc.Get));
}
if (Desc.Set !== undefined) {
X(CreateDataProperty(obj, new Value('set'), Desc.Set));
}
if (Desc.Enumerable !== undefined) {
X(CreateDataProperty(obj, new Value('enumerable'), Desc.Enumerable));
}
if (Desc.Configurable !== undefined) {
X(CreateDataProperty(obj, new Value('configurable'), Desc.Configurable));
}
// Assert: All of the above CreateDataProperty operations return true.
return obj;
}
// 6.2.5.5 #sec-topropertydescriptor
export function ToPropertyDescriptor(Obj) {
if (Type(Obj) !== 'Object') {
return surroundingAgent.Throw('TypeError', 'NotAnObject', Obj);
}
const desc = Descriptor({});
const hasEnumerable = Q(HasProperty(Obj, new Value('enumerable')));
if (hasEnumerable === Value.true) {
const enumerable = ToBoolean(Q(Get(Obj, new Value('enumerable'))));
desc.Enumerable = enumerable;
}
const hasConfigurable = Q(HasProperty(Obj, new Value('configurable')));
if (hasConfigurable === Value.true) {
const conf = ToBoolean(Q(Get(Obj, new Value('configurable'))));
desc.Configurable = conf;
}
const hasValue = Q(HasProperty(Obj, new Value('value')));
if (hasValue === Value.true) {
const value = Q(Get(Obj, new Value('value')));
desc.Value = value;
}
const hasWritable = Q(HasProperty(Obj, new Value('writable')));
if (hasWritable === Value.true) {
const writable = ToBoolean(Q(Get(Obj, new Value('writable'))));
desc.Writable = writable;
}
const hasGet = Q(HasProperty(Obj, new Value('get')));
if (hasGet === Value.true) {
const getter = Q(Get(Obj, new Value('get')));
if (IsCallable(getter) === Value.false && Type(getter) !== 'Undefined') {
return surroundingAgent.Throw('TypeError', 'NotAFunction', getter);
}
desc.Get = getter;
}
const hasSet = Q(HasProperty(Obj, new Value('set')));
if (hasSet === Value.true) {
const setter = Q(Get(Obj, new Value('set')));
if (IsCallable(setter) === Value.false && Type(setter) !== 'Undefined') {
return surroundingAgent.Throw('TypeError', 'NotAFunction', setter);
}
desc.Set = setter;
}
if (desc.Get !== undefined || desc.Set !== undefined) {
if (desc.Value !== undefined || desc.Writable !== undefined) {
return surroundingAgent.Throw('TypeError', 'InvalidPropertyDescriptor');
}
}
return desc;
}
// 6.2.5.6 #sec-completepropertydescriptor
export function CompletePropertyDescriptor(Desc) {
Assert(Type(Desc) === 'Descriptor');
const like = Descriptor({
Value: Value.undefined,
Writable: false,
Get: Value.undefined,
Set: Value.undefined,
Enumerable: false,
Configurable: false,
});
if (IsGenericDescriptor(Desc) || IsDataDescriptor(Desc)) {
if (Desc.Value === undefined) {
Desc.Value = like.Value;
}
if (Desc.Writable === undefined) {
Desc.Writable = like.Writable;
}
} else {
if (Desc.Get === undefined) {
Desc.Get = like.Get;
}
if (Desc.Set === undefined) {
Desc.Set = like.Set;
}
}
if (Desc.Enumerable === undefined) {
Desc.Enumerable = like.Enumerable;
}
if (Desc.Configurable === undefined) {
Desc.Configurable = like.Configurable;
}
return Desc;
}
// 6.2.7.1 #sec-createbytedatablock
export function CreateByteDataBlock(size) {
size = size.numberValue();
Assert(size >= 0);
let db;
try {
db = new DataBlock(size);
} catch (err) {
return surroundingAgent.Throw('RangeError', 'CannotAllocateDataBlock');
}
return db;
}
// 6.2.7.3 #sec-copydatablockbytes
export function CopyDataBlockBytes(toBlock, toIndex, fromBlock, fromIndex, count) {
Assert(fromBlock !== toBlock);
Assert(Type(fromBlock) === 'Data Block' || Type(fromBlock) === 'Shared Data Block');
Assert(Type(toBlock) === 'Data Block' || Type(toBlock) === 'Shared Data Block');
Assert(Number.isSafeInteger(fromIndex) && fromIndex >= 0);
Assert(Number.isSafeInteger(toIndex) && toIndex >= 0);
Assert(Number.isSafeInteger(count) && count >= 0);
const fromSize = fromBlock.byteLength;
Assert(fromIndex + count <= fromSize);
const toSize = toBlock.byteLength;
Assert(toIndex + count <= toSize);
while (count > 0) {
if (Type(fromBlock) === 'Shared Data Block') {
Assert(false);
} else {
Assert(Type(toBlock) !== 'Shared Data Block');
toBlock[toIndex] = fromBlock[fromIndex];
}
toIndex += 1;
fromIndex += 1;
count -= 1;
}
return NormalCompletion(undefined);
}
@@ -1,145 +0,0 @@
import {
Descriptor,
Type,
Value,
} from '../value.mjs';
import { X } from '../completion.mjs';
import {
Assert,
CanonicalNumericIndexString,
DefinePropertyOrThrow,
IsInteger,
IsPropertyKey,
MakeBasicObject,
OrdinaryGetOwnProperty,
OrdinaryDefineOwnProperty,
IsCompatiblePropertyDescriptor,
ToInteger,
isArrayIndex,
} from './all.mjs';
function StringExoticGetOwnProperty(P) {
const S = this;
Assert(IsPropertyKey(P));
const desc = OrdinaryGetOwnProperty(S, P);
if (Type(desc) !== 'Undefined') {
return desc;
}
return X(StringGetOwnProperty(S, P));
}
function StringExoticDefineOwnProperty(P, Desc) {
const S = this;
Assert(IsPropertyKey(P));
const stringDesc = X(StringGetOwnProperty(S, P));
if (Type(stringDesc) !== 'Undefined') {
const extensible = S.Extensible;
return X(IsCompatiblePropertyDescriptor(extensible, Desc, stringDesc));
}
return X(OrdinaryDefineOwnProperty(S, P, Desc));
}
function StringExoticOwnPropertyKeys() {
const O = this;
const keys = [];
const str = O.StringData;
Assert(Type(str) === 'String');
const len = str.stringValue().length;
for (let i = 0; i < len; i += 1) {
keys.push(new Value(`${i}`));
}
// For each own property key P of O such that P is an array index and
// ToInteger(P) ≥ len, in ascending numeric index order, do
// Add P as the last element of keys.
for (const P of O.properties.keys()) {
// This is written with two nested ifs to work around https://github.com/devsnek/engine262/issues/24
if (isArrayIndex(P)) {
if (X(ToInteger(P)).numberValue() >= len) {
keys.push(P);
}
}
}
// For each own property key P of O such that Type(P) is String and
// P is not an array index, in ascending chronological order of property creation, do
// Add P as the last element of keys.
for (const P of O.properties.keys()) {
if (Type(P) === 'String' && isArrayIndex(P) === false) {
keys.push(P);
}
}
// For each own property key P of O such that Type(P) is Symbol,
// in ascending chronological order of property creation, do
// Add P as the last element of keys.
for (const P of O.properties.keys()) {
if (Type(P) === 'Symbol') {
keys.push(P);
}
}
return keys;
}
// 9.4.3.4 #sec-stringcreate
export function StringCreate(value, prototype) {
// 1. Assert: Type(value) is String.
Assert(Type(value) === 'String');
// 2. Let S be ! MakeBasicObject(« [[Prototype]], [[Extensible]], [[StringData]] »).
const S = X(MakeBasicObject(['Prototype', 'Extensible', 'StringData']));
// 3. Set S.[[Prototype]] to prototype.
S.Prototype = prototype;
// 4. Set S.[[StringData]] to value.
S.StringData = value;
// 5. Set S.[[GetOwnProperty]] as specified in 9.4.3.1.
S.GetOwnProperty = StringExoticGetOwnProperty;
// 6. Set S.[[DefineOwnProperty]] as specified in 9.4.3.2.
S.DefineOwnProperty = StringExoticDefineOwnProperty;
// 7. Set S.[[OwnPropertyKeys]] as specified in 9.4.3.3.
S.OwnPropertyKeys = StringExoticOwnPropertyKeys;
// 8. Let length be the number of code unit elements in value.
const length = new Value(value.stringValue().length);
// 9. Perform ! DefinePropertyOrThrow(S, "length", PropertyDescriptor { [[Value]]: length, [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false }).
X(DefinePropertyOrThrow(S, new Value('length'), Descriptor({
Value: length,
Writable: Value.false,
Enumerable: Value.false,
Configurable: Value.false,
})));
// 10. Return S.
return S;
}
// 9.4.3.5 #sec-stringgetownproperty
export function StringGetOwnProperty(S, P) {
Assert(Type(S) === 'Object' && 'StringData' in S);
Assert(IsPropertyKey(P));
if (Type(P) !== 'String') {
return Value.undefined;
}
const index = X(CanonicalNumericIndexString(P));
if (Type(index) === 'Undefined') {
return Value.undefined;
}
if (IsInteger(index) === Value.false) {
return Value.undefined;
}
if (Object.is(index.numberValue(), -0)) {
return Value.undefined;
}
const str = S.StringData;
Assert(Type(str) === 'String');
const len = str.stringValue().length;
if (index.numberValue() < 0 || len <= index.numberValue()) {
return Value.undefined;
}
const resultStr = str.stringValue()[index.numberValue()];
return Descriptor({
Value: new Value(resultStr),
Writable: Value.false,
Enumerable: Value.true,
Configurable: Value.false,
});
}
@@ -1,12 +0,0 @@
import { Type, Value } from '../value.mjs';
import { Assert } from './all.mjs';
// 19.4.3.3.1 #sec-symboldescriptivestring
export function SymbolDescriptiveString(sym) {
Assert(Type(sym) === 'Symbol');
let desc = sym.Description;
if (Type(desc) === 'Undefined') {
desc = new Value('');
}
return new Value(`Symbol(${desc.stringValue()})`);
}
@@ -1,412 +0,0 @@
import {
BigIntValue,
Type,
TypeNumeric,
Value,
wellKnownSymbols,
} from '../value.mjs';
import { surroundingAgent } from '../engine.mjs';
import { Q, X } from '../completion.mjs';
import { OutOfRange } from '../helpers.mjs';
import {
Assert,
Get,
ToBoolean,
ToNumber,
ToNumeric,
ToPrimitive,
StringToBigInt,
isProxyExoticObject,
isArrayExoticObject,
isIntegerIndexedExoticObject,
} from './all.mjs';
// This file covers abstract operations defined in
// 7.2 #sec-testing-and-comparison-operations
// 7.2.1 #sec-requireobjectcoercible
export function RequireObjectCoercible(argument) {
const type = Type(argument);
switch (type) {
case 'Undefined':
return surroundingAgent.Throw('TypeError', 'CannotConvertToObject', 'undefined');
case 'Null':
return surroundingAgent.Throw('TypeError', 'CannotConvertToObject', 'null');
case 'Boolean':
case 'Number':
case 'String':
case 'Symbol':
case 'BigInt':
case 'Object':
return argument;
default:
throw new OutOfRange('RequireObjectCoercible', { type, argument });
}
}
// 7.2.2 #sec-isarray
export function IsArray(argument) {
if (Type(argument) !== 'Object') {
return Value.false;
}
if (isArrayExoticObject(argument)) {
return Value.true;
}
if (isProxyExoticObject(argument)) {
if (argument.ProxyHandler === Value.null) {
return surroundingAgent.Throw('TypeError', 'ProxyRevoked', 'IsArray');
}
const target = argument.ProxyTarget;
return IsArray(target);
}
return Value.false;
}
// 7.2.3 #sec-iscallable
export function IsCallable(argument) {
if (Type(argument) !== 'Object') {
return Value.false;
}
if ('Call' in argument) {
return Value.true;
}
return Value.false;
}
// 7.2.4 #sec-isconstructor
export function IsConstructor(argument) {
if (Type(argument) !== 'Object') {
return Value.false;
}
if ('Construct' in argument) {
return Value.true;
}
return Value.false;
}
// 7.2.5 #sec-isextensible-o
export function IsExtensible(O) {
Assert(Type(O) === 'Object');
return O.IsExtensible();
}
// 7.2.6 #sec-isinteger
export function IsInteger(argument) {
if (Type(argument) !== 'Number') {
return Value.false;
}
if (argument.isNaN() || argument.isInfinity()) {
return Value.false;
}
if (Math.floor(Math.abs(argument.numberValue())) !== Math.abs(argument.numberValue())) {
return Value.false;
}
return Value.true;
}
// 7.2.7 #sec-ispropertykey
export function IsPropertyKey(argument) {
if (Type(argument) === 'String') {
return true;
}
if (Type(argument) === 'Symbol') {
return true;
}
return false;
}
// 7.2.8 #sec-isregexp
export function IsRegExp(argument) {
if (Type(argument) !== 'Object') {
return Value.false;
}
const matcher = Q(Get(argument, wellKnownSymbols.match));
if (matcher !== Value.undefined) {
return ToBoolean(matcher);
}
if ('RegExpMatcher' in argument) {
return Value.true;
}
return Value.false;
}
// 7.2.9 #sec-isstringprefix
export function IsStringPrefix(p, q) {
Assert(Type(p) === 'String');
Assert(Type(q) === 'String');
return q.stringValue().startsWith(p.stringValue());
}
// 7.2.10 #sec-samevalue
export function SameValue(x, y) {
// 1. If Type(x) is different from Type(y), return false.
if (Type(x) !== Type(y)) {
return Value.false;
}
// 2. If Type(x) is Number or BigInt, then
if (Type(x) === 'Number' || Type(x) === 'BigInt') {
// a. Return ! Type(x)::sameValue(x, y).
return TypeNumeric(x).sameValue(x, y);
}
// 3. Return ! SameValueNonNumeric(x, y).
return X(SameValueNonNumber(x, y));
}
// 7.2.11 #sec-samevaluezero
export function SameValueZero(x, y) {
// 1. If Type(x) is different from Type(y), return false.
if (Type(x) !== Type(y)) {
return Value.false;
}
// 2. If Type(x) is Number or BigInt, then
if (Type(x) === 'Number' || Type(x) === 'BigInt') {
// a. Return ! Type(x)::sameValueZero(x, y).
return TypeNumeric(x).sameValueZero(x, y);
}
// 3. Return ! SameValueNonNumeric(x, y).
return X(SameValueNonNumber(x, y));
}
// 7.2.12 #sec-samevaluenonnumber
export function SameValueNonNumber(x, y) {
Assert(Type(x) !== 'Number');
Assert(Type(x) === Type(y));
if (Type(x) === 'Undefined') {
return Value.true;
}
if (Type(x) === 'Null') {
return Value.true;
}
if (Type(x) === 'String') {
if (x.stringValue() === y.stringValue()) {
return Value.true;
}
return Value.false;
}
if (Type(x) === 'Boolean') {
if (x === y) {
return Value.true;
}
return Value.false;
}
if (Type(x) === 'Symbol') {
return x === y ? Value.true : Value.false;
}
return x === y ? Value.true : Value.false;
}
// 7.2.13 #sec-abstract-relational-comparison
export function AbstractRelationalComparison(x, y, LeftFirst = true) {
let px;
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'));
} 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'));
}
// 3. If Type(px) is String and Type(py) is String, then
if (Type(px) === 'String' && Type(py) === 'String') {
// a. If IsStringPrefix(py, px) is true, return false.
if (IsStringPrefix(py, px)) {
return Value.false;
}
// b. If IsStringPrefix(px, py) is true, return true.
if (IsStringPrefix(px, py)) {
return Value.true;
}
// c. Let k be the smallest nonnegative integer such that the code unit at index k within px
// is different from the code unit at index k within py. (There must be such a k, for
// neither String is a prefix of the other.)
let k = 0;
while (true) {
if (px.stringValue()[k] !== py.stringValue()[k]) {
break;
}
k += 1;
}
// d. Let m be the integer that is the numeric value of the code unit at index k within px.
const m = px.stringValue().charCodeAt(k);
// e. Let n be the integer that is the numeric value of the code unit at index k within py.
const n = py.stringValue().charCodeAt(k);
// f. If m < n, return true. Otherwise, return false.
if (m < n) {
return Value.true;
} else {
return Value.false;
}
} else {
// a. If Type(px) is BigInt and Type(py) is String, then
if (Type(px) === 'BigInt' && Type(py) === 'String') {
// i. Let ny be ! StringToBigInt(py).
const ny = X(StringToBigInt(py));
// ii. If ny is NaN, return undefined.
if (Number.isNaN(ny)) {
return Value.undefined;
}
// iii. Return BigInt::lessThan(px, ny).
return BigIntValue.lessThan(px, ny);
}
// b. If Type(px) is String and Type(py) is BigInt, then
if (Type(px) === 'String' && Type(py) === 'BigInt') {
// i. Let ny be ! StringToBigInt(py).
const nx = X(StringToBigInt(px));
// ii. If ny is NaN, return undefined.
if (Number.isNaN(nx)) {
return Value.undefined;
}
// iii. Return BigInt::lessThan(px, ny).
return BigIntValue.lessThan(nx, py);
}
// c. Let nx be ? ToNumeric(px). NOTE: Because px and py are primitive values evaluation order is not important.
const nx = Q(ToNumeric(px));
// d. Let ny be ? ToNumeric(py).
const ny = Q(ToNumeric(py));
// e. If Type(nx) is the same as Type(ny), return Type(nx)::lessThan(nx, ny).
if (Type(nx) === Type(ny)) {
return TypeNumeric(nx).lessThan(nx, ny);
}
// f. Assert: Type(nx) is BigInt and Type(ny) is Number, or Type(nx) is Number and Type(ny) is BigInt.
Assert((Type(nx) === 'BigInt' && Type(ny) === 'Number') || (Type(nx) === 'Number' && Type(ny) === 'BigInt'));
// g. If nx or ny is NaN, return undefined.
if ((nx.isNaN && nx.isNaN()) || (ny.isNaN && ny.isNaN())) {
return Value.undefined;
}
// h. If nx is -∞ or ny is +∞, return true.
if ((nx.numberValue && nx.numberValue() === -Infinity) || (ny.numberValue && ny.numberValue() === +Infinity)) {
return Value.true;
}
// i. If nx is +∞ or ny is -∞, return false.
if ((nx.numberValue && nx.numberValue() === +Infinity) || (ny.numberValue && ny.numberValue() === -Infinity)) {
return Value.false;
}
// j. If the mathematical value of nx is less than the mathematical value of ny, return true; otherwise return false.
const a = nx.numberValue ? nx.numberValue() : nx.bigintValue();
const b = ny.numberValue ? ny.numberValue() : ny.bigintValue();
return a < b ? Value.true : Value.false;
}
}
// 7.2.14 #sec-abstract-equality-comparison
export function AbstractEqualityComparison(x, y) {
// 1. If Type(x) is the same as Type(y), then
if (Type(x) === Type(y)) {
// a. Return the result of performing Strict Equality Comparison x === y.
return StrictEqualityComparison(x, y);
}
// 2. If x is null and y is undefined, return true.
if (x === Value.null && y === Value.undefined) {
return Value.true;
}
// 3. If x is undefined and y is null, return true.
if (x === Value.undefined && y === Value.null) {
return Value.true;
}
// 4. If Type(x) is Number and Type(y) is String, return the result of the comparison x == ! ToNumber(y).
if (Type(x) === 'Number' && Type(y) === 'String') {
return AbstractEqualityComparison(x, X(ToNumber(y)));
}
// 5. If Type(x) is String and Type(y) is Number, return the result of the comparison ! ToNumber(x) == y.
if (Type(x) === 'String' && Type(y) === 'Number') {
return AbstractEqualityComparison(X(ToNumber(x)), y);
}
// 6. If Type(x) is BigInt and Type(y) is String, then
if (Type(x) === 'BigInt' && Type(y) === 'String') {
// a. Let n be ! StringToBigInt(y).
const n = X(StringToBigInt(y));
// b. If n is NaN, return false.
if (Number.isNaN(n)) {
return Value.false;
}
// c. Return the result of the comparison x == n.
return AbstractEqualityComparison(x, n);
}
// 7. If Type(x) is String and Type(y) is BigInt, return the result of the comparison y == x.
if (Type(x) === 'String' && Type(y) === 'BigInt') {
return AbstractEqualityComparison(y, x);
}
// 8. If Type(x) is Boolean, return the result of the comparison ! ToNumber(x) == y.
if (Type(x) === 'Boolean') {
return AbstractEqualityComparison(X(ToNumber(x)), y);
}
// 9. If Type(y) is Boolean, return the result of the comparison x == ! ToNumber(y).
if (Type(y) === 'Boolean') {
return AbstractEqualityComparison(x, X(ToNumber(y)));
}
// 10. If Type(x) is either String, Number, BigInt, or Symbol and Type(y) is Object, return the result of the comparison x == ToPrimitive(y).
if (['String', 'Number', 'BigInt', 'Symbol'].includes(Type(x)) && Type(y) === 'Object') {
return AbstractEqualityComparison(x, Q(ToPrimitive(y)));
}
// 11. If Type(x) is Object and Type(y) is either String, Number, BigInt, or Symbol, return the result of the comparison ToPrimitive(x) == y.
if (Type(x) === 'Object' && ['String', 'Number', 'BigInt', 'Symbol'].includes(Type(y))) {
return AbstractEqualityComparison(Q(ToPrimitive(x)), y);
}
// 12. If Type(x) is BigInt and Type(y) is Number, or if Type(x) is Number and Type(y) is BigInt, then
if ((Type(x) === 'BigInt' && Type(y) === 'Number') || (Type(x) === 'Number' && Type(y) === 'BigInt')) {
// a. If x or y are any of NaN, +∞, or -∞, return false.
if ((x.isNaN && (x.isNaN() || !x.isFinite())) || (y.isNaN && (y.isNaN() || !y.isFinite()))) {
return Value.false;
}
// b. If the mathematical value of x is equal to the mathematical value of y, return true; otherwise return false.
const a = (x.numberValue ? x.numberValue() : x.bigintValue());
const b = (y.numberValue ? y.numberValue() : y.bigintValue());
return a == b ? Value.true : Value.false; // eslint-disable-line eqeqeq
}
// 13. Return false.
return Value.false;
}
// 7.2.15 #sec-strict-equality-comparison
export function StrictEqualityComparison(x, y) {
// 1. If Type(x) is different from Type(y), return false.
if (Type(x) !== Type(y)) {
return Value.false;
}
// 2. If Type(x) is Number or BigInt, then
if (Type(x) === 'Number' || Type(x) === 'BigInt') {
// a. Return ! Type(x)::equal(x, y).
return X(TypeNumeric(x).equal(x, y));
}
// 3. Return ! SameValueNonNumeric(x, y).
return SameValueNonNumber(x, y);
}
// #sec-isvalidintegerindex
export function IsValidIntegerIndex(O, index) {
Assert(isIntegerIndexedExoticObject(O));
Assert(Type(index) === 'Number');
if (IsInteger(index) === Value.false) {
return Value.false;
}
index = index.numberValue();
if (Object.is(index, -0)) {
return Value.false;
}
if (index < 0 || index >= O.ArrayLength.numberValue()) {
return Value.false;
}
return Value.true;
}
// #sec-isnonnegativeinteger
export function IsNonNegativeInteger(argument) {
// 1. If ! IsInteger(argument) is true and argument ≥ 0, return true.
if (X(IsInteger(argument)) === Value.true && argument.numberValue() >= 0) {
return Value.true;
}
// 2. Otherwise, return false.
return Value.false;
}
@@ -1,469 +0,0 @@
import {
Type,
Value,
NumberValue,
BigIntValue,
wellKnownSymbols,
} from '../value.mjs';
import {
surroundingAgent,
} from '../engine.mjs';
import { Q, X } from '../completion.mjs';
import { OutOfRange } from '../helpers.mjs';
import { MV_StringNumericLiteral } from '../runtime-semantics/all.mjs';
import {
Assert,
Call,
Get,
GetMethod,
IsCallable,
OrdinaryObjectCreate,
SameValue,
StringCreate,
} from './all.mjs';
// 7.1.1 #sec-toprimitive
export function ToPrimitive(input, PreferredType) {
Assert(input instanceof Value);
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');
}
const exoticToPrim = Q(GetMethod(input, wellKnownSymbols.toPrimitive));
if (exoticToPrim !== Value.undefined) {
const result = Q(Call(exoticToPrim, input, [hint]));
if (Type(result) !== 'Object') {
return result;
}
return surroundingAgent.Throw('TypeError', 'ObjectToPrimitive');
}
if (hint.stringValue() === 'default') {
hint = new Value('number');
}
return Q(OrdinaryToPrimitive(input, hint));
}
return input;
}
// 7.1.1.1 #sec-ordinarytoprimitive
export function OrdinaryToPrimitive(O, hint) {
Assert(Type(O) === 'Object');
Assert(Type(hint) === 'String' && (hint.stringValue() === 'string' || hint.stringValue() === 'number'));
let methodNames;
if (hint.stringValue() === 'string') {
methodNames = [new Value('toString'), new Value('valueOf')];
} else {
methodNames = [new Value('valueOf'), new Value('toString')];
}
for (const name of methodNames) {
const method = Q(Get(O, name));
if (IsCallable(method) === Value.true) {
const result = Q(Call(method, O));
if (Type(result) !== 'Object') {
return result;
}
}
}
return surroundingAgent.Throw('TypeError', 'ObjectToPrimitive');
}
// 7.1.2 #sec-toboolean
export function ToBoolean(argument) {
const type = Type(argument);
switch (type) {
case 'Undefined':
return Value.false;
case 'Null':
return Value.false;
case 'Boolean':
return argument;
case 'Number':
if (argument.numberValue() === 0 || argument.isNaN()) {
return Value.false;
}
return Value.true;
case 'String':
if (argument.stringValue().length === 0) {
return Value.false;
}
return Value.true;
case 'Symbol':
return Value.true;
case 'BigInt':
if (argument.bigintValue() === 0n) {
return Value.false;
}
return Value.true;
case 'Object':
return Value.true;
default:
throw new OutOfRange('ToBoolean', { type, argument });
}
}
// #sec-tonumeric
export function ToNumeric(value) {
// 1. Let primValue be ? ToPrimitive(value, hint Number).
const primValue = Q(ToPrimitive(value, 'Number'));
// 2. If Type(primValue) is BigInt, return primValue.
if (Type(primValue) === 'BigInt') {
return primValue;
}
// 3. Return ? ToNumber(primValue).
return Q(ToNumber(primValue));
}
// 7.1.3 #sec-tonumber
export function ToNumber(argument) {
const type = Type(argument);
switch (type) {
case 'Undefined':
return new Value(NaN);
case 'Null':
return new Value(0);
case 'Boolean':
if (argument === Value.true) {
return new Value(1);
}
return new Value(0);
case 'Number':
return argument;
case 'String':
return MV_StringNumericLiteral(argument.stringValue());
case 'BigInt':
return surroundingAgent.Throw('TypeError', 'CannotMixBigInts');
case 'Symbol':
return surroundingAgent.Throw('TypeError', 'CannotConvertSymbol', 'number');
case 'Object': {
const primValue = Q(ToPrimitive(argument, 'Number'));
return Q(ToNumber(primValue));
}
default:
throw new OutOfRange('ToNumber', { type, argument });
}
}
const mod = (n, m) => {
const r = n % m;
return Math.floor(r >= 0 ? r : r + m);
};
// 7.1.4 #sec-tointeger
export function ToInteger(argument) {
// 1. Let number be ? ToNumber(argument).
const number = Q(ToNumber(argument)).numberValue();
// 2. If number is NaN, +0, or -0, return +0.
if (Number.isNaN(number) || number === 0) {
return new Value(0);
}
// 3. If number is +∞, or -∞, return number.
if (!Number.isFinite(number)) {
return new Value(number);
}
// 4. Let integer be the Number value that is the same sign as number and whose magnitude is floor(abs(number)).
const integer = Math.sign(number) * Math.floor(Math.abs(number));
// 5. If integer is -0, return +0.
if (Object.is(integer, -0)) {
return new Value(+0);
}
// 6. Return integer.
return new Value(integer);
}
// 7.1.5 #sec-toint32
export function ToInt32(argument) {
const number = Q(ToNumber(argument)).numberValue();
if (Number.isNaN(number) || number === 0 || !Number.isFinite(number)) {
return new Value(0);
}
const int = Math.sign(number) * Math.floor(Math.abs(number));
const int32bit = mod(int, 2 ** 32);
if (int32bit >= (2 ** 31)) {
return new Value(int32bit - (2 ** 32));
}
return new Value(int32bit);
}
// 7.1.6 #sec-touint32
export function ToUint32(argument) {
const number = Q(ToNumber(argument)).numberValue();
if (Number.isNaN(number) || number === 0 || !Number.isFinite(number)) {
return new Value(0);
}
const int = Math.sign(number) * Math.floor(Math.abs(number));
const int32bit = mod(int, 2 ** 32);
return new Value(int32bit);
}
// 7.1.7 #sec-toint16
export function ToInt16(argument) {
const number = Q(ToNumber(argument)).numberValue();
if (Number.isNaN(number) || number === 0 || !Number.isFinite(number)) {
return new Value(0);
}
const int = Math.sign(number) * Math.floor(Math.abs(number));
const int16bit = mod(int, 2 ** 16);
if (int16bit >= (2 ** 15)) {
return new Value(int16bit - (2 ** 16));
}
return new Value(int16bit);
}
// 7.1.8 #sec-touint16
export function ToUint16(argument) {
const number = Q(ToNumber(argument)).numberValue();
if (Number.isNaN(number) || number === 0 || !Number.isFinite(number)) {
return new Value(0);
}
const int = Math.sign(number) * Math.floor(Math.abs(number));
const int16bit = mod(int, 2 ** 16);
return new Value(int16bit);
}
// 7.1.9 #sec-toint8
export function ToInt8(argument) {
const number = Q(ToNumber(argument)).numberValue();
if (Number.isNaN(number) || number === 0 || !Number.isFinite(number)) {
return new Value(0);
}
const int = Math.sign(number) * Math.floor(Math.abs(number));
const int8bit = mod(int, 2 ** 8);
if (int8bit >= (2 ** 7)) {
return new Value(int8bit - (2 ** 8));
}
return new Value(int8bit);
}
// 7.1.10 #sec-touint8
export function ToUint8(argument) {
const number = Q(ToNumber(argument)).numberValue();
if (Number.isNaN(number) || number === 0 || !Number.isFinite(number)) {
return new Value(0);
}
const int = Math.sign(number) * Math.floor(Math.abs(number));
const int8bit = mod(int, 2 ** 8);
return new Value(int8bit);
}
// 7.1.11 #sec-touint8clamp
export function ToUint8Clamp(argument) {
const number = Q(ToNumber(argument)).numberValue();
if (Number.isNaN(number)) {
return new Value(0);
}
if (number <= 0) {
return new Value(0);
}
if (number >= 255) {
return new Value(255);
}
const f = Math.floor(number);
if (f + 0.5 < number) {
return new Value(f + 1);
}
if (number < f + 0.5) {
return new Value(f);
}
if (f % 2 === 1) {
return new Value(f + 1);
}
return new Value(f);
}
// #sec-tobigint
export function ToBigInt(argument) {
// 1. Let prim be ? ToPrimitive(argument, hint 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':
// Throw a TypeError exception.
return surroundingAgent.Throw('TypeError', 'CannotConvertToBigInt', prim);
case 'Null':
// Throw a TypeError exception.
return surroundingAgent.Throw('TypeError', 'CannotConvertToBigInt', prim);
case 'Boolean':
// Return 1n if prim is true and 0n if prim is false.
if (prim === Value.true) {
return new Value(1n);
}
return new Value(0n);
case 'BigInt':
// Return prim.
return prim;
case 'Number':
// Throw a TypeError exception.
return surroundingAgent.Throw('TypeError', 'CannotConvertToBigInt', prim);
case 'String': {
// 1. Let n be ! StringToBigInt(prim).
const n = X(StringToBigInt(prim));
// 2. If n is NaN, throw a SyntaxError exception.
if (Number.isNaN(n)) {
return surroundingAgent.Throw('SyntaxError', 'CannotConvertToBigInt', prim);
}
// 3. Return n.
return n;
}
case 'Symbol':
return surroundingAgent.Throw('TypeError', 'CannotConvertSymbol', 'bigint');
default:
throw new OutOfRange('ToBigInt', argument);
}
}
// #sec-stringtobigint
export function StringToBigInt(argument) {
// Apply the algorithm in 7.1.4.1 (#sec-tonumber-applied-to-the-string-type) with the following changes:
// 1. Replace the StrUnsignedDecimalLiteral production with DecimalDigits to not allow Infinity, decimal points, or exponents.
// 2. If the MV is NaN, return NaN, otherwise return the BigInt which exactly corresponds to the MV, rather than rounding to a Number.
// TODO: Adapt nearley grammar for this.
try {
return new Value(BigInt(argument.stringValue()));
} catch {
return NaN;
}
}
// #sec-tobigint64
export function ToBigInt64(argument) {
// 1. Let n be ? ToBigInt(argument).
const n = Q(ToBigInt(argument));
// 2. Let int64bit be n modulo 2^64.
const int64bit = n.bigintValue() % (2n ** 64n);
// 3. If int64bit ≥ 2^63, return int64bit - 2^64; otherwise return int64bit.
if (int64bit >= 2n ** 63n) {
return new Value(int64bit - (2n ** 64n));
}
return new Value(int64bit);
}
// #sec-tobiguint64
export function ToBigUint64(argument) {
// 1. Let n be ? ToBigInt(argument).
const n = Q(ToBigInt(argument));
// 2. Let int64bit be n modulo 2^64.
const int64bit = n.bigintValue() % (2n ** 64n);
// 3. Return int64bit.
return new Value(int64bit);
}
// 7.1.12 #sec-tostring
export function ToString(argument) {
const type = Type(argument);
switch (type) {
case 'Undefined':
return new Value('undefined');
case 'Null':
return new Value('null');
case 'Boolean':
return new Value(argument === Value.true ? 'true' : 'false');
case 'Number':
// Return ! Number::toString(argument).
return X(NumberValue.toString(argument));
case 'String':
return argument;
case 'Symbol':
return surroundingAgent.Throw('TypeError', 'CannotConvertSymbol', 'string');
case 'BigInt':
// Return ! BigInt::toString(argument).
return X(BigIntValue.toString(argument));
case 'Object': {
const primValue = Q(ToPrimitive(argument, 'String'));
return Q(ToString(primValue));
}
default:
throw new OutOfRange('ToString', { type, argument });
}
}
// 7.1.13 #sec-toobject
export function ToObject(argument) {
const type = Type(argument);
switch (type) {
case 'Undefined':
return surroundingAgent.Throw('TypeError', 'CannotConvertToObject', 'undefined');
case 'Null':
return surroundingAgent.Throw('TypeError', 'CannotConvertToObject', 'null');
case 'Boolean': {
const obj = OrdinaryObjectCreate(surroundingAgent.intrinsic('%Boolean.prototype%'));
obj.BooleanData = argument;
return obj;
}
case 'Number': {
const obj = OrdinaryObjectCreate(surroundingAgent.intrinsic('%Number.prototype%'));
obj.NumberData = argument;
return obj;
}
case 'String':
return StringCreate(argument, surroundingAgent.intrinsic('%String.prototype%'));
case 'Symbol': {
const obj = OrdinaryObjectCreate(surroundingAgent.intrinsic('%Symbol.prototype%'));
obj.SymbolData = argument;
return obj;
}
case 'BigInt': {
const obj = OrdinaryObjectCreate(surroundingAgent.intrinsic('%BigInt.prototype%'));
obj.BigIntData = argument;
return obj;
}
case 'Object':
return argument;
default:
throw new OutOfRange('ToObject', { type, argument });
}
}
// 7.1.14 #sec-topropertykey
export function ToPropertyKey(argument) {
const key = Q(ToPrimitive(argument, 'String'));
if (Type(key) === 'Symbol') {
return key;
}
return X(ToString(key));
}
// 7.1.15 #sec-tolength
export function ToLength(argument) {
const len = Q(ToInteger(argument));
if (len.numberValue() <= 0) {
return new Value(0);
}
return new Value(Math.min(len.numberValue(), (2 ** 53) - 1));
}
// 7.1.16 #sec-canonicalnumericindexstring
export function CanonicalNumericIndexString(argument) {
Assert(Type(argument) === 'String');
if (argument.stringValue() === '-0') {
return new Value(-0);
}
const n = X(ToNumber(argument));
if (SameValue(X(ToString(n)), argument) === Value.false) {
return Value.undefined;
}
return n;
}
// 7.1.17 #sec-toindex
export function ToIndex(value) {
let index;
if (Type(value) === 'Undefined') {
index = new Value(0);
} else {
const integerIndex = Q(ToInteger(value));
if (integerIndex.numberValue() < 0) {
return surroundingAgent.Throw('RangeError', 'NegativeIndex', 'Index');
}
index = X(ToLength(integerIndex));
if (X(SameValue(integerIndex, index)) === Value.false) {
return surroundingAgent.Throw('RangeError', 'OutOfRange', 'Index');
}
}
return index;
}
@@ -1,240 +0,0 @@
import { surroundingAgent } from '../engine.mjs';
import { Type, Value } from '../value.mjs';
import { Q, X } from '../completion.mjs';
import {
Assert,
ToInt8,
ToUint8,
ToUint8Clamp,
ToInt16,
ToUint16,
ToInt32,
ToUint32,
ToBigInt64,
ToBigUint64,
RequireInternalSlot,
Construct,
GetIterator,
IteratorStep,
IteratorValue,
SpeciesConstructor,
IsDetachedBuffer,
IsNonNegativeInteger,
IntegerIndexedObjectCreate,
GetPrototypeFromConstructor,
AllocateArrayBuffer,
} from './all.mjs';
export const typedArrayInfoByName = {
Int8Array: {
IntrinsicName: '%Int8Array%',
ElementType: 'Int8',
ElementSize: 1,
ConversionOperation: ToInt8,
},
Uint8Array: {
IntrinsicName: '%Uint8Array%',
ElementType: 'Uint8',
ElementSize: 1,
ConversionOperation: ToUint8,
},
Uint8ClampedArray: {
IntrinsicName: '%Uint8ClampedArray%',
ElementType: 'Uint8C',
ElementSize: 1,
ConversionOperation: ToUint8Clamp,
},
Int16Array: {
IntrinsicName: '%Int16Array%',
ElementType: 'Int16',
ElementSize: 2,
ConversionOperation: ToInt16,
},
Uint16Array: {
IntrinsicName: '%Uint16Array%',
ElementType: 'Uint16',
ElementSize: 2,
ConversionOperation: ToUint16,
},
Int32Array: {
IntrinsicName: '%Int32Array%',
ElementType: 'Int32',
ElementSize: 4,
ConversionOperation: ToInt32,
},
Uint32Array: {
IntrinsicName: '%Uint32Array%',
ElementType: 'Uint32',
ElementSize: 4,
ConversionOperation: ToUint32,
},
BigInt64Array: {
IntrinsicName: '%BigInt64Array%',
ElementType: 'BigInt64',
ElementSize: 8,
ConversionOperation: ToBigInt64,
},
BigUint64Array: {
IntrinsicName: '%BigUint64Array%',
ElementType: 'BigUint64',
ElementSize: 8,
ConversionOperation: ToBigUint64,
},
Float32Array: {
IntrinsicName: '%Float32Array%',
ElementType: 'Float32',
ElementSize: 4,
ConversionOperation: undefined,
},
Float64Array: {
IntrinsicName: '%Float64Array%',
ElementType: 'Float64',
ElementSize: 8,
ConversionOperation: undefined,
},
};
export const typedArrayInfoByType = {};
Object.values(typedArrayInfoByName).forEach((v) => {
typedArrayInfoByType[v.ElementType] = v;
});
// #sec-validatetypedarray
export function ValidateTypedArray(O) {
// 1. Perform ? RequireInternalSlot(O, [[TypedArrayName]]).
Q(RequireInternalSlot(O, 'TypedArrayName'));
// 2. Assert: O has a [[ViewedArrayBuffer]] internal slot.
Assert('ViewedArrayBuffer' in O);
// 3. Let buffer be O.[[ViewedArrayBuffer]].
const buffer = O.ViewedArrayBuffer;
// 4. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
if (IsDetachedBuffer(buffer) === Value.true) {
return surroundingAgent.Throw('TypeError', 'ArrayBufferDetached');
}
// 5. Return buffer.
return buffer;
}
// #typedarray-create
export function TypedArrayCreate(constructor, argumentList) {
// 1. Let newTypedArray be ? Construct(constructor, argumentList).
const newTypedArray = Q(Construct(constructor, argumentList));
// 2. Perform ? ValidateTypedArray(newTypedArray).
Q(ValidateTypedArray(newTypedArray));
// 3. If argumentList is a List of a single Number, then
if (argumentList.length === 1 && Type(argumentList[0]) === 'Number') {
// a. If newTypedArray.[[ArrayLength]] < argumentList[0], throw a TypeError exception.
if (newTypedArray.ArrayLength.numberValue() < argumentList[0].numberValue()) {
return surroundingAgent.Throw('TypeError', 'TypedArrayTooSmall');
}
}
// 4. Return newTypedArray.
return newTypedArray;
}
// #sec-allocatetypedarray
export function AllocateTypedArray(constructorName, newTarget, defaultProto, length) {
// 1. Let proto be ? GetPrototypeFromConstructor(newTarget, defaultProto).
const proto = Q(GetPrototypeFromConstructor(newTarget, defaultProto));
// 2. Let obj be ! IntegerIndexedObjectCreate(proto).
const obj = X(IntegerIndexedObjectCreate(proto));
// 3. Assert: obj.[[ViewedArrayBuffer]] is undefined.
Assert(obj.ViewedArrayBuffer === Value.undefined);
// 4. Set obj.[[TypedArrayName]] to constructorName.
obj.TypedArrayName = constructorName;
// 5. If constructorName is "BigInt64Array" or "BigUint64Array", set obj.[[ContentType]] to BigInt.
// 6. Otherwise, set obj.[[ContentType]] to Number.
if (constructorName.stringValue() === 'BigInt64Array' || constructorName.stringValue() === 'BigUint64Array') {
obj.ContentType = 'BigInt';
} else {
obj.ContentType = 'Number';
}
// 7. If length is not present, then
if (length === undefined) {
// 1. Set obj.[[ByteLength]] to 0.
obj.ByteLength = new Value(0);
// 1. Set obj.[[ByteOffset]] to 0.
obj.ByteOffset = new Value(0);
// 1. Set obj.[[ArrayLength]] to 0.
obj.ArrayLength = new Value(0);
} else {
// a. Perform ? AllocateTypedArrayBuffer(obj, length).
Q(AllocateTypedArrayBuffer(obj, length));
}
// 9. Return obj.
return obj;
}
// #sec-allocatetypedarraybuffer
export function AllocateTypedArrayBuffer(O, length) {
// 1. Assert: O is an Object that has a [[ViewedArrayBuffer]] internal slot.
Assert(Type(O) === 'Object' && 'ViewedArrayBuffer' in O);
// 2. Assert: O.[[ViewedArrayBuffer]] is undefined.
Assert(O.ViewedArrayBuffer === Value.undefined);
// 3. Assert: ! IsNonNegativeInteger(length) is true.
Assert(X(IsNonNegativeInteger(length)) === Value.true);
// 4. Let constructorName be the String value of O.[[TypedArrayName]].
const constructorName = O.TypedArrayName.stringValue();
// 5. Let elementSize be the Element Size value specified in Table 61 for constructorName.
const elementSize = typedArrayInfoByName[constructorName].ElementSize;
// 6. Let byteLength be elementSize × length.
const byteLength = new Value(elementSize * length.numberValue());
// 7. Let data be ? AllocateArrayBuffer(%ArrayBuffer%, byteLength).
const data = Q(AllocateArrayBuffer(surroundingAgent.intrinsic('%ArrayBuffer%'), byteLength));
// 8. Set O.[[ViewedArrayBuffer]] to data.
O.ViewedArrayBuffer = data;
// 9. Set O.[[ByteLength]] to byteLength.
O.ByteLength = byteLength;
// 10. Set O.[[ByteOffset]] to 0.
O.ByteOffset = new Value(0);
// 11. Set O.[[ArrayLength]] to length.
O.ArrayLength = length;
// 12. Return O.
return O;
}
// #typedarray-species-create
export function TypedArraySpeciesCreate(exemplar, argumentList) {
// 1. Assert: exemplar is an Object that has [[TypedArrayName]] and [[ContentType]] internal slots.
Assert(Type(exemplar) === 'Object'
&& 'TypedArrayName' in exemplar
&& 'ContentType' in exemplar);
// 2. Let defaultConstructor be the intrinsic object listed in column one of Table 61 for exemplar.[[TypedArrayName]].
const defaultConstructor = surroundingAgent.intrinsic(typedArrayInfoByName[exemplar.TypedArrayName.stringValue()].IntrinsicName);
// 3. Let constructor be ? SpeciesConstructor(exemplar, defaultConstructor).
const constructor = Q(SpeciesConstructor(exemplar, defaultConstructor));
// 4. Let result be ? TypedArrayCreate(constructor, argumentList).
const result = Q(TypedArrayCreate(constructor, argumentList));
// 5. Assert: result has [[TypedArrayName]] and [[ContentType]] internal slots.
Assert('TypedArrayName' in result && 'ContentType' in result);
// 6. If result.[[ContentType]] is not equal to exemplar.[[ContentType]], throw a TypeError exception.
if (result.ContentType !== exemplar.ContentType) {
return surroundingAgent.Throw('TypeError', 'BufferContentTypeMismatch');
}
// 7. Return result.
return result;
}
// #sec-iterabletolist
export function IterableToList(items, method) {
// 1. Let iteratorRecord be ? GetIterator(items, sync, method).
const iteratorRecord = Q(GetIterator(items, 'sync', method));
// 2. Let values be a new empty List.
const values = [];
// 3. Let next be true.
let next = Value.true;
// 4. Repeat, while next is not false
while (next !== Value.false) {
// a. Set next to ? IteratorStep(iteratorRecord).
next = Q(IteratorStep(iteratorRecord));
// b. If next is not false, then
if (next !== Value.false) {
// i. Let nextValue be ? IteratorValue(next).
const nextValue = Q(IteratorValue(next));
// ii. Append nextValue to the end of the List values.
values.push(nextValue);
}
}
// 5. Return values.
return values;
}
@@ -1,60 +0,0 @@
import { surroundingAgent } from '../engine.mjs';
import { Value } from '../value.mjs';
import { NormalCompletion, Q, X } from '../completion.mjs';
import { Assert, Call } from './all.mjs';
// #sec-clear-kept-objects
export function ClearKeptObjects() {
// 1. Let agentRecord be the surrounding agent's Agent Record.
const agentRecord = surroundingAgent.AgentRecord;
// 2. Set agentRecord.[[KeptAlive]] to a new empty List.
agentRecord.KeptAlive = new Set();
}
// #sec-addtokeptobjects
export function AddToKeptObjects(object) {
// 1. Let agentRecord be the surrounding agent's Agent Record.
const agentRecord = surroundingAgent.AgentRecord;
// 2. Append object to agentRecord.[[KeptAlive]].
agentRecord.KeptAlive.add(object);
}
// #sec-weakrefderef
export function WeakRefDeref(weakRef) {
// 1. Let target be weakRef.[[WeakRefTarget]].
const target = weakRef.WeakRefTarget;
// 2. If target is not empty, then
if (target !== undefined) {
// a. Perform ! AddToKeptObjects(target).
X(AddToKeptObjects(target));
// b. Return target.
return target;
}
// 3. Return undefined.
return Value.undefined;
}
// #sec-cleanup-finalization-registry
export function CleanupFinalizationRegistry(finalizationRegistry, callback) {
// 1. Assert: finalizationRegistry has [[Cells]] and [[CleanupCallback]] internal slots.
Assert('Cells' in finalizationRegistry && 'CleanupCallback' in finalizationRegistry);
// 2. Set callback to finalizationRegistry.[[CleanupCallback]].
if (callback === undefined || callback === Value.undefined) {
callback = finalizationRegistry.CleanupCallback;
}
// 3. While finalizationRegistry.[[Cells]] contains a Record cell such that cell.[[WeakRefTarget]] is empty, an implementation may perform the following steps:
for (let i = 0; i < finalizationRegistry.Cells.length; i += 1) {
// a. Choose any such _cell_.
const cell = finalizationRegistry.Cells[i];
if (cell.WeakRefTarget !== undefined) {
continue;
}
// b. Remove cell from finalizationRegistry.[[Cells]].
finalizationRegistry.Cells.splice(i, 1);
i -= 1;
// c. Perform ? Call(callback, undefined, « cell.[[HeldValue]] »).
Q(Call(callback, Value.undefined, [cell.HeldValue]));
}
// 4. Return NormalCompletion(undefined).
return NormalCompletion(Value.undefined);
}
-269
View File
@@ -1,269 +0,0 @@
import { Value } from './value.mjs';
import {
surroundingAgent,
ExecutionContext,
HostEnqueueFinalizationRegistryCleanupJob,
ScriptEvaluation,
} from './engine.mjs';
import {
X,
ThrowCompletion,
AbruptCompletion,
EnsureCompletion,
} from './completion.mjs';
import {
Realm,
ClearKeptObjects,
CreateIntrinsics,
SetRealmGlobalObject,
SetDefaultGlobalBindings,
} from './abstract-ops/all.mjs';
import {
ParseScript,
ParseModule,
} from './parse.mjs';
import { SourceTextModuleRecord } from './modules.mjs';
export * from './value.mjs';
export * from './engine.mjs';
export * from './completion.mjs';
export * from './abstract-ops/all.mjs';
export * from './static-semantics/all.mjs';
export * from './runtime-semantics/all.mjs';
export * from './environment.mjs';
export * from './parse.mjs';
export * from './modules.mjs';
export * from './inspect.mjs';
export function Throw(...args) {
return surroundingAgent.Throw(...args);
}
export function gc() {
// #sec-weakref-execution
// At any time, if a set of objects S is not live, an ECMAScript implementation may perform the following steps atomically:
// 1. For each obj of S, do
// a. For each WeakRef ref such that ref.[[WeakRefTarget]] is obj,
// i. Set ref.[[WeakRefTarget]] to empty.
// b. For each FinalizationRegistry fg such that fg.[[Cells]] contains cell, and cell.[[WeakRefTarget]] is obj,
// i. Set cell.[[WeakRefTarget]] to empty.
// ii. Optionally, perform ! HostEnqueueFinalizationRegistryCleanupJob(fg).
// c. For each WeakMap map such that map.WeakMapData contains a record r such that r.Key is obj,
// i. Set r.[[Key]] to empty.
// ii. Set r.[[Value]] to empty.
// d. For each WeakSet set such that set.[[WeakSetData]] contains obj,
// i. Replace the element of set whose value is obj with an element whose value is empty.
const marked = new Set();
const weakrefs = new Set();
const fgs = new Set();
const weakmaps = new Set();
const weaksets = new Set();
const ephemeronQueue = [];
const markCb = (O) => {
if (typeof O !== 'object' || O === null) {
return;
}
if (marked.has(O)) {
return;
}
marked.add(O);
if ('WeakRefTarget' in O && !('HeldValue' in O)) {
weakrefs.add(O);
markCb(O.properties);
markCb(O.Prototype);
} else if ('Cells' in O) {
fgs.add(O);
markCb(O.properties);
markCb(O.Prototype);
O.Cells.forEach((cell) => {
markCb(cell.HeldValue);
});
} else if ('WeakMapData' in O) {
weakmaps.add(O);
markCb(O.properties);
markCb(O.Prototype);
O.WeakMapData.forEach((r) => {
ephemeronQueue.push(r);
});
} else if ('WeakSetData' in O) {
weaksets.add(O);
markCb(O.properties);
markCb(O.Prototype);
} else if (O.mark) {
O.mark(markCb);
}
};
markCb(surroundingAgent);
while (ephemeronQueue.length > 0) {
const item = ephemeronQueue.shift();
if (marked.has(item.Key)) {
markCb(item.Value);
}
}
weakrefs.forEach((ref) => {
if (!marked.has(ref.WeakRefTarget)) {
ref.WeakRefTarget = undefined;
}
});
fgs.forEach((fg) => {
let dirty = false;
fg.Cells.forEach((cell) => {
if (!marked.has(cell.WeakRefTarget)) {
cell.WeakRefTarget = undefined;
dirty = true;
}
});
if (dirty) {
X(HostEnqueueFinalizationRegistryCleanupJob(fg));
}
});
weakmaps.forEach((map) => {
map.WeakMapData.forEach((r) => {
if (!marked.has(r.Key)) {
r.Key = undefined;
r.Value = undefined;
}
});
});
weaksets.forEach((set) => {
set.WeakSetData.forEach((obj, i) => {
if (!marked.has(obj)) {
set.WeakSetData[i] = undefined;
}
});
});
}
// https://tc39.es/ecma262/#sec-jobs
export function runJobQueue() {
if (surroundingAgent.executionContextStack.some((e) => e.ScriptOrModule !== Value.null)) {
return;
}
// At some future point in time, when there is no running execution context
// and the execution context stack is empty, the implementation must:
while (surroundingAgent.jobQueue.length > 0) { // eslint-disable-line no-constant-condition
const {
job: abstractClosure,
callerRealm,
callerScriptOrModule,
} = surroundingAgent.jobQueue.shift();
// 1. Perform any implementation-defined preparation steps.
const newContext = new ExecutionContext();
surroundingAgent.executionContextStack.push(newContext);
newContext.Function = Value.null;
newContext.Realm = callerRealm;
newContext.ScriptOrModule = callerScriptOrModule;
// 2. Call the abstract closure.
X(abstractClosure());
// 3. Perform any host-defined cleanup steps, after which the execution context stack must be empty.
ClearKeptObjects();
gc();
surroundingAgent.executionContextStack.pop(newContext);
}
}
export function evaluateScript(sourceText, realm, hostDefined) {
const s = ParseScript(sourceText, realm, hostDefined);
if (Array.isArray(s)) {
return ThrowCompletion(s[0]);
}
return EnsureCompletion(ScriptEvaluation(s));
}
export class ManagedRealm extends Realm {
constructor(HostDefined = {}) {
super();
// CreateRealm()
CreateIntrinsics(this);
this.GlobalObject = Value.undefined;
this.GlobalEnv = Value.undefined;
this.TemplateMap = [];
// InitializeHostDefinedRealm()
const newContext = new ExecutionContext();
newContext.Function = Value.null;
newContext.Realm = this;
newContext.ScriptOrModule = Value.null;
surroundingAgent.executionContextStack.push(newContext);
SetRealmGlobalObject(this, Value.undefined, Value.undefined);
SetDefaultGlobalBindings(this);
// misc
surroundingAgent.executionContextStack.pop(newContext);
this.HostDefined = HostDefined;
this.topContext = newContext;
this.active = false;
}
scope(cb) {
if (this.active) {
return cb();
}
this.active = true;
surroundingAgent.executionContextStack.push(this.topContext);
const r = cb();
surroundingAgent.executionContextStack.pop(this.topContext);
this.active = false;
return r;
}
evaluateScript(sourceText, { specifier } = {}) {
if (typeof sourceText !== 'string') {
throw new TypeError('sourceText must be a string');
}
const res = this.scope(() => {
const realm = surroundingAgent.currentRealmRecord;
return evaluateScript(sourceText, realm, {
specifier,
public: { specifier },
});
});
if (!(res instanceof AbruptCompletion)) {
runJobQueue();
}
return res;
}
createSourceTextModule(specifier, sourceText) {
if (typeof sourceText !== 'string') {
throw new TypeError('sourceText must be a string');
}
if (typeof specifier !== 'string') {
throw new TypeError('specifier must be a string');
}
const module = this.scope(() => ParseModule(sourceText, this, {
specifier,
SourceTextModuleRecord: ManagedSourceTextModuleRecord,
}));
if (Array.isArray(module)) {
return ThrowCompletion(module[0]);
}
return module;
}
}
class ManagedSourceTextModuleRecord extends SourceTextModuleRecord {
Evaluate() {
const r = super.Evaluate();
if (!(r instanceof AbruptCompletion)) {
runJobQueue();
}
return r;
}
}
-136
View File
@@ -1,136 +0,0 @@
import { surroundingAgent } from './engine.mjs';
import {
Assert,
CreateBuiltinFunction,
PerformPromiseThen,
PromiseResolve,
SetFunctionLength,
} from './abstract-ops/all.mjs';
import { Value } from './value.mjs';
import { resume } from './helpers.mjs';
// #sec-completion-record-specification-type
export function Completion(init) {
if (new.target === Completion) {
this.Type = init.Type;
this.Value = init.Value;
this.Target = init.Target;
} else {
// 1. Assert: completionRecord is a Completion Record.
Assert(init instanceof Completion);
// 2. Return completionRecord as the Completion Record of this abstract operation.
return init;
}
}
// NON-SPEC
Completion.prototype.mark = function mark(m) {
m(this.Value);
};
// #sec-normalcompletion
export function NormalCompletion(argument) {
// 1. Return Completion { [[Type]]: normal, [[Value]]: argument, [[Target]]: empty }.
return new Completion({ Type: 'normal', Value: argument, Target: undefined });
}
Object.defineProperty(NormalCompletion, Symbol.hasInstance, {
value: function hasInstance(v) {
return v instanceof Completion && v.Type === 'normal';
},
writable: true,
enumerable: false,
configurable: true,
});
export class AbruptCompletion {
static [Symbol.hasInstance](v) {
return v instanceof Completion && v.Type !== 'normal';
}
}
// #sec-throwcompletion
export function ThrowCompletion(argument) {
// 1. Return Completion { [[Type]]: throw, [[Value]]: argument, [[Target]]: empty }.
return new Completion({ Type: 'throw', Value: argument, Target: undefined });
}
// 6.2.3.4 #sec-updateempty
export function UpdateEmpty(completionRecord, value) {
Assert(completionRecord instanceof Completion);
// 1. Assert: If completionRecord.[[Type]] is either return or throw, then completionRecord.[[Value]] is not empty.
Assert(!(completionRecord.Type === 'return' || completionRecord.Type === 'throw') || completionRecord.Value !== undefined);
// 2. If completionRecord.[[Value]] is not empty, return Completion(completionRecord).
if (completionRecord.Value !== undefined) {
return Completion(completionRecord);
}
// 3. Return Completion { [[Type]]: completionRecord.[[Type]], [[Value]]: value, [[Target]]: completionRecord.[[Target]] }.
return new Completion({ Type: completionRecord.Type, Value: value, Target: completionRecord.Target });
}
// #sec-returnifabrupt
export function ReturnIfAbrupt() {
/* istanbul skip next */
throw new TypeError('ReturnIfAbrupt requires build');
}
// #sec-returnifabrupt-shorthands ? OperationName()
export const Q = ReturnIfAbrupt;
// #sec-returnifabrupt-shorthands ! OperationName()
export function X() {
/* istanbul skip next */
throw new TypeError('X() requires build');
}
// 25.6.1.1.1 #sec-ifabruptrejectpromise
export function IfAbruptRejectPromise() {
/* istanbul skip next */
throw new TypeError('IfAbruptRejectPromise requires build');
}
export function EnsureCompletion(val) {
if (val instanceof Completion) {
return val;
}
return NormalCompletion(val);
}
export function AwaitFulfilledFunctions([value]) {
const F = surroundingAgent.activeFunctionObject;
const asyncContext = F.AsyncContext;
const prevContext = surroundingAgent.runningExecutionContext;
// Suspend prevContext
surroundingAgent.executionContextStack.push(asyncContext);
resume(asyncContext, NormalCompletion(value));
Assert(surroundingAgent.runningExecutionContext === prevContext);
return Value.undefined;
}
function AwaitRejectedFunctions([reason]) {
const F = surroundingAgent.activeFunctionObject;
const asyncContext = F.AsyncContext;
const prevContext = surroundingAgent.runningExecutionContext;
// Suspend prevContext
surroundingAgent.executionContextStack.push(asyncContext);
resume(asyncContext, ThrowCompletion(reason));
Assert(surroundingAgent.runningExecutionContext === prevContext);
return Value.undefined;
}
export function* Await(value) {
const asyncContext = surroundingAgent.runningExecutionContext;
const promise = Q(PromiseResolve(surroundingAgent.intrinsic('%Promise%'), value));
const stepsFulfilled = AwaitFulfilledFunctions;
const onFulfilled = X(CreateBuiltinFunction(stepsFulfilled, ['AsyncContext']));
X(SetFunctionLength(onFulfilled, new Value(1)));
onFulfilled.AsyncContext = asyncContext;
const stepsRejected = AwaitRejectedFunctions;
const onRejected = X(CreateBuiltinFunction(stepsRejected, ['AsyncContext']));
X(SetFunctionLength(onRejected, new Value(1)));
onRejected.AsyncContext = asyncContext;
X(PerformPromiseThen(promise, onFulfilled, onRejected));
surroundingAgent.executionContextStack.pop(asyncContext);
const completion = yield Value.undefined;
return completion;
}
-404
View File
@@ -1,404 +0,0 @@
import { Value } from './value.mjs';
import {
AbruptCompletion,
EnsureCompletion,
NormalCompletion,
ThrowCompletion,
Q, X,
} from './completion.mjs';
import {
IsCallable,
Call, Construct, Assert, GetModuleNamespace,
PerformPromiseThen, CreateBuiltinFunction,
GetActiveScriptOrModule,
CleanupFinalizationRegistry,
CreateArrayFromList,
} from './abstract-ops/all.mjs';
import { GlobalDeclarationInstantiation } from './runtime-semantics/all.mjs';
import { Evaluate } from './evaluator.mjs';
import { CyclicModuleRecord } from './modules.mjs';
import { CallSite, unwind } from './helpers.mjs';
import * as messages from './messages.mjs';
export const FEATURES = Object.freeze([
{
name: 'Top-Level Await',
flag: 'top-level-await',
url: 'https://github.com/tc39/proposal-top-level-await',
},
{
name: 'Hashbang Grammar',
flag: 'hashbang',
url: 'https://github.com/tc39/proposal-hashbang',
},
{
name: 'Numeric Separators',
flag: 'numeric-separators',
url: 'https://github.com/tc39/proposal-numeric-separator',
},
{
name: 'RegExp Match Indices',
flag: 'regexp-match-indices',
url: 'https://github.com/tc39/proposal-regexp-match-indices',
},
{
name: 'FinalizationRegistry.prototype.cleanupSome',
flag: 'cleanup-some',
url: 'https://github.com/tc39/proposal-cleanup-some',
},
{
name: 'Arbitrary Module Namespace Names',
flag: 'arbitrary-module-namespace-names',
url: 'https://github.com/tc39/ecma262/pull/2154',
},
].map(Object.freeze));
let agentSignifier = 0;
// #sec-agents
export class Agent {
constructor(options = {}) {
// #table-agent-record
const Signifier = agentSignifier;
agentSignifier += 1;
this.AgentRecord = {
LittleEndian: Value.true,
CanBlock: Value.true,
Signifier,
IsLockFree1: Value.true,
IsLockFree2: Value.true,
CandidateExecution: undefined,
KeptAlive: new Set(),
};
// #execution-context-stack
this.executionContextStack = [];
const stackPop = this.executionContextStack.pop;
this.executionContextStack.pop = function pop(ctx) {
if (!ctx.poppedForTailCall) {
const popped = stackPop.call(this);
Assert(popped === ctx);
}
};
// NON-SPEC
this.jobQueue = [];
this.hostDefinedOptions = {
...options,
features: FEATURES.reduce((acc, { flag }) => {
if (options.features) {
acc[flag] = options.features.includes(flag);
} else {
acc[flag] = false;
}
return acc;
}, {}),
};
}
// #running-execution-context
get runningExecutionContext() {
return this.executionContextStack[this.executionContextStack.length - 1];
}
// #current-realm
get currentRealmRecord() {
return this.runningExecutionContext.Realm;
}
// #active-function-object
get activeFunctionObject() {
return this.runningExecutionContext.Function;
}
// Get an intrinsic by name for the current realm
intrinsic(name) {
return this.currentRealmRecord.Intrinsics[name];
}
// Generate a throw completion using message templates
Throw(type, template, ...templateArgs) {
if (type instanceof Value) {
return ThrowCompletion(type);
}
const message = messages[template](...templateArgs);
const cons = this.currentRealmRecord.Intrinsics[`%${type}%`];
let error;
if (type === 'AggregateError') {
error = X(Construct(cons, [
X(CreateArrayFromList([])),
new Value(message),
]));
} else {
error = X(Construct(cons, [new Value(message)]));
}
return ThrowCompletion(error);
}
queueJob(queueName, job) {
const callerContext = this.runningExecutionContext;
const callerRealm = callerContext.Realm;
const callerScriptOrModule = GetActiveScriptOrModule();
const pending = {
queueName,
job,
callerRealm,
callerScriptOrModule,
};
this.jobQueue.push(pending);
}
// NON-SPEC: Check if a feature is enabled in this agent.
feature(name) {
return this.hostDefinedOptions.features[name];
}
// NON-SPEC
mark(m) {
this.AgentRecord.KeptAlive.forEach((v) => {
m(v);
});
this.executionContextStack.forEach((e) => {
m(e);
});
this.jobQueue.forEach((j) => {
m(j.callerRealm);
m(j.callerScriptOrModule);
});
}
}
export let surroundingAgent;
export function setSurroundingAgent(a) {
surroundingAgent = a;
}
// #sec-execution-contexts
export class ExecutionContext {
constructor() {
this.codeEvaluationState = undefined;
this.Function = undefined;
this.Realm = undefined;
this.ScriptOrModule = undefined;
this.VariableEnvironment = undefined;
this.LexicalEnvironment = undefined;
// NON-SPEC
this.callSite = new CallSite(this);
this.promiseCapability = undefined;
this.poppedForTailCall = false;
}
copy() {
const e = new ExecutionContext();
e.codeEvaluationState = this.codeEvaluationState;
e.Function = this.Function;
e.Realm = this.Realm;
e.ScriptOrModule = this.ScriptOrModule;
e.VariableEnvironment = this.VariableEnvironment;
e.LexicalEnvironment = this.LexicalEnvironment;
e.callSite = this.callSite.clone(e);
e.promiseCapability = this.promiseCapability;
return e;
}
// NON-SPEC
mark(m) {
m(this.Function);
m(this.Realm);
m(this.ScriptOrModule);
m(this.VariableEnvironment);
m(this.LexicalEnvironment);
m(this.promiseCapability);
}
}
// 15.1.10 #sec-runtime-semantics-scriptevaluation
export function ScriptEvaluation(scriptRecord) {
if (surroundingAgent.hostDefinedOptions.boost) {
return surroundingAgent.hostDefinedOptions.boost.evaluateScript(scriptRecord);
}
const globalEnv = scriptRecord.Realm.GlobalEnv;
const scriptContext = new ExecutionContext();
scriptContext.Function = Value.null;
scriptContext.Realm = scriptRecord.Realm;
scriptContext.ScriptOrModule = scriptRecord;
scriptContext.VariableEnvironment = globalEnv;
scriptContext.LexicalEnvironment = globalEnv;
scriptContext.HostDefined = scriptRecord.HostDefined;
// Suspend runningExecutionContext
surroundingAgent.executionContextStack.push(scriptContext);
const scriptBody = scriptRecord.ECMAScriptCode;
let result = EnsureCompletion(GlobalDeclarationInstantiation(scriptBody, globalEnv));
if (result.Type === 'normal') {
result = EnsureCompletion(unwind(Evaluate(scriptBody)));
}
if (result.Type === 'normal' && !result.Value) {
result = NormalCompletion(Value.undefined);
}
// Suspend scriptCtx
surroundingAgent.executionContextStack.pop(scriptContext);
// Resume(surroundingAgent.runningExecutionContext);
return result;
}
// #sec-hostenqueuepromisejob
export function HostEnqueuePromiseJob(job, _realm) {
surroundingAgent.queueJob('PromiseJobs', job);
}
// #sec-agentsignifier
export function AgentSignifier() {
// 1. Let AR be the Agent Record of the surrounding agent.
const AR = surroundingAgent.AgentRecord;
// 2. Return AR.[[Signifier]].
return AR.Signifier;
}
export function HostEnsureCanCompileStrings(callerRealm, calleeRealm) {
if (surroundingAgent.hostDefinedOptions.ensureCanCompileStrings !== undefined) {
Q(surroundingAgent.hostDefinedOptions.ensureCanCompileStrings(callerRealm, calleeRealm));
}
return NormalCompletion(undefined);
}
export function HostPromiseRejectionTracker(promise, operation) {
const realm = surroundingAgent.currentRealmRecord;
if (realm && realm.HostDefined.promiseRejectionTracker) {
X(realm.HostDefined.promiseRejectionTracker(promise, operation));
}
}
export function HostHasSourceTextAvailable(func) {
if (surroundingAgent.hostDefinedOptions.hasSourceTextAvailable) {
return X(surroundingAgent.hostDefinedOptions.hasSourceTextAvailable(func));
}
return Value.true;
}
export function HostResolveImportedModule(referencingScriptOrModule, specifier) {
const realm = referencingScriptOrModule.Realm || surroundingAgent.currentRealmRecord;
if (realm.HostDefined.resolveImportedModule) {
specifier = specifier.stringValue();
if (referencingScriptOrModule !== Value.null) {
if (!referencingScriptOrModule.HostDefined.moduleMap) {
referencingScriptOrModule.HostDefined.moduleMap = new Map();
}
if (referencingScriptOrModule.HostDefined.moduleMap.has(specifier)) {
return referencingScriptOrModule.HostDefined.moduleMap.get(specifier);
}
}
const resolved = Q(realm.HostDefined.resolveImportedModule(referencingScriptOrModule, specifier));
if (referencingScriptOrModule !== Value.null) {
referencingScriptOrModule.HostDefined.moduleMap.set(specifier, resolved);
}
return resolved;
}
return surroundingAgent.Throw('Error', 'CouldNotResolveModule', specifier);
}
function FinishDynamicImport(referencingScriptOrModule, specifier, promiseCapability, completion) {
// 1. If completion is an abrupt completion, then perform ! Call(promiseCapability.[[Reject]], undefined, « completion.[[Value]] »).
if (completion instanceof AbruptCompletion) {
X(Call(promiseCapability.Reject, Value.undefined, [completion.Value]));
} else { // 2. Else,
// a. Assert: completion is a normal completion and completion.[[Value]] is undefined.
Assert(completion instanceof NormalCompletion);
// b. Let moduleRecord be ! HostResolveImportedModule(referencingScriptOrModule, specifier).
const moduleRecord = X(HostResolveImportedModule(referencingScriptOrModule, specifier));
// c. Assert: Evaluate has already been invoked on moduleRecord and successfully completed.
// d. Let namespace be GetModuleNamespace(moduleRecord).
const namespace = EnsureCompletion(GetModuleNamespace(moduleRecord));
// e. If namespace is an abrupt completion, perform ! Call(promiseCapability.[[Reject]], undefined, « namespace.[[Value]] »).
if (namespace instanceof AbruptCompletion) {
X(Call(promiseCapability.Reject, Value.undefined, [namespace.Value]));
} else {
// f. Else, perform ! Call(promiseCapability.[[Resolve]], undefined, « namespace.[[Value]] »).
X(Call(promiseCapability.Resolve, Value.undefined, [namespace.Value]));
}
}
}
export function HostImportModuleDynamically(referencingScriptOrModule, specifier, promiseCapability) {
surroundingAgent.queueJob('ImportModuleDynamicallyJobs', () => {
const finish = (c) => FinishDynamicImport(referencingScriptOrModule, specifier, promiseCapability, c);
const c = (() => {
const module = Q(HostResolveImportedModule(referencingScriptOrModule, specifier));
Q(module.Link());
const maybePromise = Q(module.Evaluate());
if (module instanceof CyclicModuleRecord) {
const onFulfilled = CreateBuiltinFunction(([v = Value.undefined]) => {
finish(NormalCompletion(v));
return Value.undefined;
}, []);
const onRejected = CreateBuiltinFunction(([r = Value.undefined]) => {
finish(ThrowCompletion(r));
return Value.undefined;
}, []);
PerformPromiseThen(maybePromise, onFulfilled, onRejected);
} else {
finish(NormalCompletion(undefined));
}
})();
if (c instanceof AbruptCompletion) {
finish(c);
}
});
return NormalCompletion(Value.undefined);
}
// #sec-hostgetimportmetaproperties
export function HostGetImportMetaProperties(moduleRecord) {
const realm = surroundingAgent.currentRealmRecord;
if (realm.HostDefined.getImportMetaProperties) {
return X(realm.HostDefined.getImportMetaProperties(moduleRecord.HostDefined.public));
}
return [];
}
// #sec-hostfinalizeimportmeta
export function HostFinalizeImportMeta(importMeta, moduleRecord) {
const realm = surroundingAgent.currentRealmRecord;
if (realm.HostDefined.finalizeImportMeta) {
return X(realm.HostDefined.finalizeImportMeta(importMeta, moduleRecord.HostDefined.public));
}
return Value.undefined;
}
// #sec-host-cleanup-finalization-registry
const scheduledForCleanup = new Set();
export function HostEnqueueFinalizationRegistryCleanupJob(fg) {
if (surroundingAgent.hostDefinedOptions.cleanupFinalizationRegistry !== undefined) {
Q(surroundingAgent.hostDefinedOptions.cleanupFinalizationRegistry(fg));
} else {
if (!scheduledForCleanup.has(fg)) {
scheduledForCleanup.add(fg);
surroundingAgent.queueJob('FinalizationCleanup', () => {
scheduledForCleanup.delete(fg);
CleanupFinalizationRegistry(fg);
});
}
}
return NormalCompletion(undefined);
}
// #sec-hostmakejobcallback
export function HostMakeJobCallback(callback) {
// 1. Assert: IsCallable(callback) is true.
Assert(IsCallable(callback) === Value.true);
// 2. Return the JobCallback Record { [[Callback]]: callback, [[HostDefined]]: empty }.
return { Callback: callback, HostDefined: undefined };
}
// #sec-hostcalljobcallback
export function HostCallJobCallback(jobCallback, V, argumentsList) {
// 1. Assert: IsCallable(jobCallback.[[Callback]]) is true.
Assert(IsCallable(jobCallback.Callback) === Value.true);
// 1. Return ? Call(jobCallback.[[Callback]], V, argumentsList).
return Q(Call(jobCallback.Callback, V, argumentsList));
}
-982
View File
@@ -1,982 +0,0 @@
import { AbstractModuleRecord } from './modules.mjs';
import {
Descriptor,
Reference,
Type,
Value,
wellKnownSymbols,
} from './value.mjs';
import { surroundingAgent } from './engine.mjs';
import {
Assert,
DefinePropertyOrThrow,
Get,
HasOwnProperty,
HasProperty,
IsDataDescriptor,
IsExtensible,
IsPropertyKey,
Set,
ToBoolean,
isECMAScriptFunctionObject,
} from './abstract-ops/all.mjs';
import { NormalCompletion, Q, X } from './completion.mjs';
import { ValueMap } from './helpers.mjs';
// #sec-environment-records
export class EnvironmentRecord {
constructor() {
this.OuterEnv = undefined;
}
// NON-SPEC
mark(m) {
m(this.OuterEnv);
}
}
// #sec-declarative-environment-records
export class DeclarativeEnvironmentRecord extends EnvironmentRecord {
constructor() {
super();
this.bindings = new ValueMap();
}
// #sec-declarative-environment-records-hasbinding-n
HasBinding(N) {
// 1. Let envRec be the declarative Environment Record for which the method was invoked.
const envRec = this;
// 2. If envRec has a binding for the name that is the value of N, return true.
if (envRec.bindings.has(N)) {
return Value.true;
}
// 3. Return false.
return Value.false;
}
// #sec-declarative-environment-records-createmutablebinding-n-d
CreateMutableBinding(N, D) {
// 1. Let envRec be the declarative Environment Record for which the method was invoked.
const envRec = this;
// 2. Assert: envRec does not already have a binding for N.
Assert(!envRec.bindings.has(N));
// 3. Create a mutable binding in envRec for N and record that it is uninitialized. If D
// is true, record that the newly created binding may be delted by a subsequent
// DeleteBinding call.
this.bindings.set(N, {
indirect: false,
initialized: false,
mutable: true,
strict: undefined,
deletable: D === Value.true,
value: undefined,
mark(m) {
m(this.value);
},
});
// 4. Return NormalCompletion(empty).
return NormalCompletion(undefined);
}
// #sec-declarative-environment-records-createimmutablebinding-n-s
CreateImmutableBinding(N, S) {
// 1. Let envRec be the declarative Environment Record for which the method was invoked.
const envRec = this;
// 2. Assert: envRec does not already have a binding for N.
Assert(!envRec.bindings.has(N));
// 3. Create an immutable binding in envRec for N and record that it is uninitialized. If
// S is true, record that the newly created binding is a strict binding.
this.bindings.set(N, {
indirect: false,
initialized: false,
mutable: false,
strict: S === Value.true,
deletable: false,
value: undefined,
mark(m) {
m(this.value);
},
});
// 4. Return NormalCompletion(empty).
return NormalCompletion(undefined);
}
// #sec-declarative-environment-records-initializebinding-n-v
InitializeBinding(N, V) {
// 1. Let envRec be the declarative Environment Record for which the method was invoked.
const envRec = this;
// 2. Assert: envRec must have an uninitialized binding for N.
const binding = envRec.bindings.get(N);
Assert(binding !== undefined && binding.initialized === false);
// 3. Set the bound value for N in envRec to V.
binding.value = V;
// 4. Record that the binding for N in envRec has been initialized.
binding.initialized = true;
// 5. Return NormalCompletion(empty).
return NormalCompletion(undefined);
}
// #sec-declarative-environment-records-setmutablebinding-n-v-s
SetMutableBinding(N, V, S) {
Assert(IsPropertyKey(N));
// 1. Let envRec be the declarative Environment Record for which the method was invoked.
const envRec = this;
// 2. If envRec does not have a binding for N, then
if (!envRec.bindings.has(N)) {
// a. If S is true, throw a ReferenceError exception.
if (S === Value.true) {
return surroundingAgent.Throw('ReferenceError', 'NotDefined', N);
}
// b. Perform envRec.CreateMutableBinding(N, true).
envRec.CreateMutableBinding(N, true);
// c. Perform envRec.InitializeBinding(N, V).
envRec.InitializeBinding(N, V);
// d. Return NormalCompletion(empty).
return NormalCompletion(undefined);
}
const binding = this.bindings.get(N);
// 3. If the binding for N in envRec is a strict binding, set S to true.
if (binding.strict === true) {
S = Value.true;
}
// 4. If the binding for N in envRec has not yet been initialized, throw a ReferenceError exception.
if (binding.initialized === false) {
return surroundingAgent.Throw('ReferenceError', 'NotInitialized', N);
}
// 5. Else if the binding for N in envRec is a mutable binding, change its bound value to V.
if (binding.mutable === true) {
binding.value = V;
} else {
// a. Assert: This is an attempt to change the value of an immutable binding.
// b. If S is true, throw a TypeError exception.
if (S === Value.true) {
return surroundingAgent.Throw('TypeError', 'AssignmentToConstant', N);
}
}
// 7. Return NormalCompletion(empty).
return NormalCompletion(undefined);
}
// #sec-declarative-environment-records-getbindingvalue-n-s
GetBindingValue(N) {
// 1. Let envRec be the declarative Environment Record for which the method was invoked.
const envRec = this;
// 2. Assert: envRec has a binding for N.
const binding = envRec.bindings.get(N);
Assert(binding !== undefined);
// 3. If the binding for N in envRec is an uninitialized binding, throw a ReferenceError exception.
if (binding.initialized === false) {
return surroundingAgent.Throw('ReferenceError', 'NotInitialized', N);
}
// 4. Return the value currently bound to N in envRec.
return binding.value;
}
// #sec-declarative-environment-records-deletebinding-n
DeleteBinding(N) {
// 1. Let envRec be the declarative Environment Record for which the method was invoked.
const envRec = this;
// 2. Assert: envRec has a binding for the name that is the value of N.
const binding = envRec.bindings.get(N);
Assert(binding !== undefined);
// 3. If the binding for N in envRec cannot be deleted, return false.
if (binding.deletable === false) {
return Value.false;
}
// 4. Remove the binding for N from envRec.
envRec.bindings.delete(N);
// 5. Return true.
return Value.true;
}
// #sec-declarative-environment-records-hasthisbinding
HasThisBinding() {
// 1. Return false.
return Value.false;
}
// #sec-declarative-environment-records-hassuperbinding
HasSuperBinding() {
// 1. Return false.
return Value.false;
}
// #sec-declarative-environment-records-withbaseobject
WithBaseObject() {
// 1. Return undefined.
return Value.undefined;
}
// NON-SPEC
mark(m) {
m(this.bindings);
}
}
// #sec-object-environment-records
export class ObjectEnvironmentRecord extends EnvironmentRecord {
constructor(BindingObject) {
super();
this.bindingObject = BindingObject;
this.withEnvironment = false;
}
// #sec-object-environment-records-hasbinding-n
HasBinding(N) {
// 1. Let envRec be the object Environment Record for which the method was invoked.
const envRec = this;
// 2. Let bindings be the binding object for envRec.
const bindings = envRec.bindingObject;
// 3. Let foundBinding be ? HasProperty(bindings, N).
const foundBinding = Q(HasProperty(bindings, N));
// 4. If foundBinding is false, return false.
if (foundBinding === Value.false) {
return Value.false;
}
// 5. If the withEnvironment flag of envRec i s false, return true.
if (envRec.withEnvironment === false) {
return Value.true;
}
// 6. Let unscopables be ? Get(bindings, @@unscopables).
const unscopables = Q(Get(bindings, wellKnownSymbols.unscopables));
// 7. If Type(unscopables) is Object, then
if (Type(unscopables) === 'Object') {
// a. Let blocked be ! ToBoolean(? Get(unscopables, N)).
const blocked = X(ToBoolean(Q(Get(unscopables, N))));
// b. If blocked is true, return false.
if (blocked === Value.true) {
return Value.false;
}
}
// 8. Return true.
return Value.true;
}
// #sec-object-environment-records-createmutablebinding-n-d
CreateMutableBinding(N, D) {
// 1. Let envRec be the object Environment Record for which the method was invoked.
const envRec = this;
// 2. Let envRec be the object Environment Record for which the method was invoked.
const bindings = envRec.bindingObject;
// 3. Return ? DefinePropertyOrThrow(bindings, N, PropertyDescriptor { [[Value]]: undefined, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: D }).
return Q(DefinePropertyOrThrow(bindings, N, Descriptor({
Value: Value.undefined,
Writable: Value.true,
Enumerable: Value.true,
Configurable: D,
})));
}
// #sec-object-environment-records-createimmutablebinding-n-s
CreateImmutableBinding(_N, _S) {
Assert(false, 'CreateImmutableBinding called on an Object Environment Record');
}
// #sec-object-environment-records-initializebinding-n-v
InitializeBinding(N, V) {
// 1. Let envRec be the object Environment Record for which the method was invoked.
const envRec = this;
// 2. Assert: envRec must have an uninitialized binding for N.
// 3. Record that the binding for N in envRec has been initialized.
// 4. Return ? envRec.SetMutableBinding(N, V, false).
return Q(envRec.SetMutableBinding(N, V, Value.false));
}
// #sec-object-environment-records-setmutablebinding-n-v-s
SetMutableBinding(N, V, S) {
// 1. Let envRec be the object Environment Record for which the method was invoked.
const envRec = this;
// 2. Let bindings be the binding object for envRec.
const bindings = envRec.bindingObject;
// 3. Let stillExists be ? HasProperty(bindings, N).
const stillExists = Q(HasProperty(bindings, N));
// 4. If stillExists is false and S is true, throw a ReferenceError exception.
if (stillExists === Value.false && S === Value.true) {
return surroundingAgent.Throw('ReferenceError', 'NotDefined', N);
}
// 5. Return ? Set(bindings, N, V, S).
return Q(Set(bindings, N, V, S));
}
// #sec-object-environment-records-getbindingvalue-n-s
GetBindingValue(N, S) {
// 1. Let envRec be the object Environment Record for which the method was invoked.
const envRec = this;
// 2. Let bindings be the binding object for envRec.
const bindings = envRec.bindingObject;
// 3. Let value be ? HasProperty(bindings, N).
const value = Q(HasProperty(bindings, N));
// 4. If value is false, then
if (value === Value.false) {
// a. If S is false, return the value undefined; otherwise throw a ReferenceError exception.
if (S === Value.false) {
return Value.undefined;
} else {
return surroundingAgent.Throw('ReferenceError', 'NotDefined', N);
}
}
// 5. Return ? Get(bindings, N).
return Q(Get(bindings, N));
}
// #sec-object-environment-records-deletebinding-n
DeleteBinding(N) {
// 1. Let envRec be the object Environment Record for which the method was invoked.
const envRec = this;
// 2. Let bindings be the binding object for envRec.
const bindings = envRec.bindingObject;
// 3. Return ? bindings.[[Delete]](N).
return Q(bindings.Delete(N));
}
// #sec-object-environment-records-hasthisbinding
HasThisBinding() {
// 1. Return false.
return Value.false;
}
// #sec-object-environment-records-hassuperbinding
HasSuperBinding() {
// 1. Return falase.
return Value.false;
}
// #sec-object-environment-records-withbaseobject
WithBaseObject() {
// 1. Let envRec be the object Environment Record for which the method was invoked.
const envRec = this;
// 2. If the withEnvironment flag of envRec is true, return the binding object for envRec.
if (envRec.withEnvironment === true) {
return envRec.bindingObject;
}
// 3. Otherwise, return undefined.
return Value.undefined;
}
// NON-SPEC
mark(m) {
m(this.bindingObject);
}
}
// #sec-function-environment-records
export class FunctionEnvironmentRecord extends DeclarativeEnvironmentRecord {
constructor() {
super();
this.ThisValue = undefined;
this.ThisBindingValue = undefined;
this.FunctionObject = undefined;
this.HomeObject = Value.undefined;
this.NewTarget = undefined;
}
// #sec-bindthisvalue
BindThisValue(V) {
// 1. Let envRec be the function Environment Record for which the method was invoked.
const envRec = this;
// 2. Assert: envRec.[[ThisBindingStatus]] is not lexical.
Assert(envRec.ThisBindingStatus !== 'lexical');
// 3. If envRec.[[ThisBindingStatus]] is initialized, throw a ReferenceError exception.
if (envRec.ThisBindingStatus === 'initialized') {
return surroundingAgent.Throw('ReferenceError', 'InvalidThis');
}
// 4. Set envRec.[[ThisValue]] to V.
envRec.ThisValue = V;
// 5. Set envRec.[[ThisBindingStatus]] to initialized.
envRec.ThisBindingStatus = 'initialized';
// 6. Return V.
return V;
}
// #sec-function-environment-records-hasthisbinding
HasThisBinding() {
// 1. Let envRec be the function Environment Record for which the method was invoked.
const envRec = this;
// 2. If envRec.[[ThisBindingStatus]] is lexical, return false; otherwise, return true.
if (envRec.ThisBindingStatus === 'lexical') {
return Value.false;
} else {
return Value.true;
}
}
// #sec-function-environment-records-hassuperbinding
HasSuperBinding() {
// 1. Let envRec be the function Environment Record for which the method was invoked.
const envRec = this;
// 2. If envRec.[[ThisBindingStatus]] is lexical, return false.
if (envRec.ThisBindingStatus === 'lexical') {
return Value.false;
}
// 3. If envRec.[[HomeObject]] has the value undefined, return false; otherwise, return true.
if (Type(envRec.HomeObject) === 'Undefined') {
return Value.false;
} else {
return Value.true;
}
}
// #sec-function-environment-records-getthisbinding
GetThisBinding() {
// 1. Let envRec be the function Environment Record for which the method was invoked.
const envRec = this;
// 2. Assert: envRec.[[ThisBindingStatus]] is not lexical.
Assert(envRec.ThisBindingStatus !== 'lexical');
// 3. If envRec.[[ThisBindingStatus]] is uninitialized, throw a ReferenceError exception.
if (envRec.ThisBindingStatus === 'uninitialized') {
return surroundingAgent.Throw('ReferenceError', 'InvalidThis');
}
// 4. Return envRec.[[ThisValue]].
return envRec.ThisValue;
}
// #sec-getsuperbase
GetSuperBase() {
// 1. Let envRec be the function Environment Record for which the method was invoked.
const envRec = this;
// 2. Let home be envRec.[[HomeObject]].
const home = envRec.HomeObject;
// 3. If home has the value undefined, return undefined.
if (Type(home) === 'Undefined') {
return Value.undefined;
}
// 4. Assert: Type(home) is Object.
Assert(Type(home) === 'Object');
// 5. Return ? home.[[GetPrototypeOf]]().
return Q(home.GetPrototypeOf());
}
mark(m) {
super.mark(m);
m(this.ThisValue);
m(this.ThisBindingValue);
m(this.FunctionObject);
m(this.HomeObject);
m(this.NewTarget);
}
}
// #sec-global-environment-records
export class GlobalEnvironmentRecord extends EnvironmentRecord {
constructor() {
super();
this.ObjectRecord = undefined;
this.GlobalThisValue = undefined;
this.DeclarativeRecord = undefined;
this.VarNames = undefined;
}
// #sec-global-environment-records-hasbinding-n
HasBinding(N) {
// 1. Let envRec be the global Environment Record for which the method was invoked.
const envRec = this;
// 2. Let DclRec be envRec.[[DeclarativeRecord]].
const DclRec = envRec.DeclarativeRecord;
// 3. If DclRec.HasBinding(N) is true, return true.
if (DclRec.HasBinding(N) === Value.true) {
return Value.true;
}
// 4. If DclRec.HasBinding(N) is true, return true.
const ObjRec = envRec.ObjectRecord;
// 5. Let ObjRec be envRec.[[ObjectRecord]].
return ObjRec.HasBinding(N);
}
// #sec-global-environment-records-createmutablebinding-n-d
CreateMutableBinding(N, D) {
// 1. Let envRec be the global Environment Record for which the method was invoked.
const envRec = this;
// 2. Let DclRec be envRec.[[DeclarativeRecord]].
const DclRec = envRec.DeclarativeRecord;
// 3. If DclRec.HasBinding(N) is true, throw a TypeError exception.
if (DclRec.HasBinding(N) === Value.true) {
return surroundingAgent.Throw('TypeError', 'AlreadyDeclared', N);
}
// 4. Return DclRec.CreateMutableBinding(N, D).
return DclRec.CreateMutableBinding(N, D);
}
// #sec-global-environment-records-createimmutablebinding-n-s
CreateImmutableBinding(N, S) {
// 1. Let envRec be the global Environment Record for which the method was invoked.
const envRec = this;
// 2. Let DclRec be envRec.[[DeclarativeRecord]].
const DclRec = envRec.DeclarativeRecord;
// 3. If DclRec.HasBinding(N) is true, throw a TypeError exception.
if (DclRec.HasBinding(N) === Value.true) {
return surroundingAgent.Throw('TypeError', 'AlreadyDeclared', N);
}
// Return DclRec.CreateImmutableBinding(N, S).
return DclRec.CreateImmutableBinding(N, S);
}
// #sec-global-environment-records-initializebinding-n-v
InitializeBinding(N, V) {
// 1. Let envRec be the global Environment Record for which the method was invoked.
const envRec = this;
// 2. Let DclRec be envRec.[[DeclarativeRecord]].
const DclRec = envRec.DeclarativeRecord;
// 3. If DclRec.HasBinding(N) is true, then
if (DclRec.HasBinding(N) === Value.true) {
// a. Return DclRec.InitializeBinding(N, V).
return DclRec.InitializeBinding(N, V);
}
// 4. Assert: If the binding exists, it must be in the object Environment Record.
// 5. Let ObjRec be envRec.[[ObjectRecord]].
const ObjRec = envRec.ObjectRecord;
// 6. Return ? ObjRec.InitializeBinding(N, V).
return ObjRec.InitializeBinding(N, V);
}
// #sec-global-environment-records-setmutablebinding-n-v-s
SetMutableBinding(N, V, S) {
// 1. Let envRec be the global Environment Record for which the method was invoked.
const envRec = this;
// 2. Let DclRec be envRec.[[DeclarativeRecord]].
const DclRec = envRec.DeclarativeRecord;
// 3. If DclRec.HasBinding(N) is true, then
if (DclRec.HasBinding(N) === Value.true) {
// a. Return DclRec.SetMutableBinding(N, V, S).
return DclRec.SetMutableBinding(N, V, S);
}
// 4. Let ObjRec be envRec.[[ObjectRecord]].
const ObjRec = envRec.ObjectRecord;
// 5. Return ? ObjRec.SetMutableBinding(N, V, S).
return Q(ObjRec.SetMutableBinding(N, V, S));
}
// #sec-global-environment-records-getbindingvalue-n-s
GetBindingValue(N, S) {
// 1. Let envRec be the global Environment Record for which the method was invoked.
const envRec = this;
// 2. Let DclRec be envRec.[[DeclarativeRecord]].
const DclRec = envRec.DeclarativeRecord;
// 3. If DclRec.HasBinding(N) is true, then
if (DclRec.HasBinding(N) === Value.true) {
// a. Return DclRec.GetBindingValue(N, S).
return DclRec.GetBindingValue(N, S);
}
// 4. Let ObjRec be envRec.[[ObjectRecord]].
const ObjRec = envRec.ObjectRecord;
// 5. Return ? ObjRec.GetBindingValue(N, S).
return Q(ObjRec.GetBindingValue(N, S));
}
// #sec-global-environment-records-deletebinding-n
DeleteBinding(N) {
// 1. Let envRec be the global Environment Record for which the method was invoked.
const envRec = this;
// 2. Let DclRec be envRec.[[DeclarativeRecord]].
const DclRec = this.DeclarativeRecord;
// 3. Let DclRec be envRec.[[DeclarativeRecord]].
if (DclRec.HasBinding(N) === Value.true) {
// a. Return DclRec.DeleteBinding(N).
return Q(DclRec.DeleteBinding(N));
}
// 4. Let ObjRec be envRec.[[ObjectRecord]].
const ObjRec = envRec.ObjectRecord;
// 5. Let globalObject be the binding object for ObjRec.
const globalObject = ObjRec.bindingObject;
// 6. Let existingProp be ? HasOwnProperty(globalObject, N).
const existingProp = Q(HasOwnProperty(globalObject, N));
// 7. If existingProp is true, then
if (existingProp === Value.true) {
// a. Let status be ? ObjRec.DeleteBinding(N).
const status = Q(ObjRec.DeleteBinding(N));
// b. If status is true, then
if (status === Value.true) {
// i. Let varNames be envRec.[[VarNames]].
const varNames = envRec.VarNames;
// ii. If N is an element of varNames, remove that element from the varNames.
if (varNames.includes(N)) {
varNames.splice(varNames.indexOf(N), 1);
}
}
// c. Return status.
return status;
}
// 8. Return true.
return Value.true;
}
// #sec-global-environment-records-hasthisbinding
HasThisBinding() {
// Return true.
return Value.true;
}
// #sec-global-environment-records-hassuperbinding
HasSuperBinding() {
// 1. Return false.
return Value.false;
}
// #sec-global-environment-records-withbaseobject
WithBaseObject() {
// 1. Return undefined.
return Value.undefined;
}
// #sec-global-environment-records-getthisbinding
GetThisBinding() {
// 1. Let envRec be the global Environment Record for which the method was invoked.
const envRec = this;
// 2. Return envRec.[[GlobalThisValue]].
return envRec.GlobalThisValue;
}
// #sec-hasvardeclaration
HasVarDeclaration(N) {
// 1. Let envRec be the global Environment Record for which the method was invoked.
const envRec = this;
// 2. Let varDeclaredNames be envRec.[[VarNames]].
const varDeclaredNames = envRec.VarNames;
// 3. If varDeclaredNames contains N, return true.
if (varDeclaredNames.includes(N)) {
return Value.true;
}
// 4. Return false.
return Value.false;
}
// #sec-haslexicaldeclaration
HasLexicalDeclaration(N) {
// 1. Let envRec be the global Environment Record for which the method was invoked.
const envRec = this;
// 2. Let envRec be the global Environment Record for which the method was invoked.
const DclRec = envRec.DeclarativeRecord;
// 3. Let DclRec be envRec.[[DeclarativeRecord]].
return DclRec.HasBinding(N);
}
// #sec-hasrestrictedglobalproperty
HasRestrictedGlobalProperty(N) {
// 1. Let envRec be the global Environment Record for which the method was invoked.
const envRec = this;
// 2. Let ObjRec be envRec.[[ObjectRecord]].
const ObjRec = envRec.ObjectRecord;
// 3. Let globalObject be the binding object for ObjRec.
const globalObject = ObjRec.bindingObject;
// 4. Let existingProp be ? globalObject.[[GetOwnProperty]](N).
const existingProp = Q(globalObject.GetOwnProperty(N));
// 5. If existingProp is undefined, return false.
if (existingProp === Value.undefined) {
return Value.false;
}
// 6. If existingProp.[[Configurable]] is true, return false.
if (existingProp.Configurable === Value.true) {
return Value.false;
}
// Return true.
return Value.true;
}
// #sec-candeclareglobalvar
CanDeclareGlobalVar(N) {
// 1. Let envRec be the global Environment Record for which the method was invoked.
const envRec = this;
// 2. Let ObjRec be envRec.[[ObjectRecord]].
const ObjRec = envRec.ObjectRecord;
// 3. Let globalObject be the binding object for ObjRec.
const globalObject = ObjRec.bindingObject;
// 4. Let hasProperty be ? HasOwnProperty(globalObject, N).
const hasProperty = Q(HasOwnProperty(globalObject, N));
// 5. If hasProperty is true, return true.
if (hasProperty === Value.true) {
return Value.true;
}
// 6. Return ? IsExtensible(globalObject).
return Q(IsExtensible(globalObject));
}
// #sec-candeclareglobalfunction
CanDeclareGlobalFunction(N) {
// 1. Let envRec be the global Environment Record for which the method was invoked.
const envRec = this;
// 2. Let ObjRec be envRec.[[ObjectRecord]].
const ObjRec = envRec.ObjectRecord;
// 3. Let globalObject be the binding object for ObjRec.
const globalObject = ObjRec.bindingObject;
// 4. Let existingProp be ? globalObject.[[GetOwnProperty]](N).
const existingProp = Q(globalObject.GetOwnProperty(N));
// 5. If existingProp is undefined, return ? IsExtensible(globalObject).
if (existingProp === Value.undefined) {
return Q(IsExtensible(globalObject));
}
// 6. If existingProp.[[Configurable]] is true, return true.
if (existingProp.Configurable === Value.true) {
return Value.true;
}
// 7. If IsDataDescriptor(existingProp) is true and existingProp has attribute values
// { [[Writable]]: true, [[Enumerable]]: true }, return true.
if (IsDataDescriptor(existingProp) === true
&& existingProp.Writable === Value.true
&& existingProp.Enumerable === Value.true) {
return Value.true;
}
// 8. Return false.
return Value.false;
}
// #sec-createglobalvarbinding
CreateGlobalVarBinding(N, D) {
// 1. Let envRec be the global Environment Record for which the method was invoked.
const envRec = this;
// 2. Let ObjRec be envRec.[[ObjectRecord]].
const ObjRec = envRec.ObjectRecord;
// 3. Let globalObject be the binding object for ObjRec.
const globalObject = ObjRec.bindingObject;
// 4. Let hasProperty be ? HasOwnProperty(globalObject, N).
const hasProperty = Q(HasOwnProperty(globalObject, N));
// 5. Let extensible be ? IsExtensible(globalObject).
const extensible = Q(IsExtensible(globalObject));
// 6. If hasProperty is false and extensible is true, then
if (hasProperty === Value.false && extensible === Value.true) {
// a. Perform ? ObjRec.CreateMutableBinding(N, D).
Q(ObjRec.CreateMutableBinding(N, D));
// b. Perform ? ObjRec.InitializeBinding(N, undefined).
Q(ObjRec.InitializeBinding(N, Value.undefined));
}
// 7. Let varDeclaredNames be envRec.[[VarNames]].
const varDeclaredNames = envRec.VarNames;
// 8. If varDeclaredNames does not contain N, then
if (!varDeclaredNames.includes(N)) {
// a. Append N to varDeclaredNames.
varDeclaredNames.push(N);
}
// return NormalCompletion(empty).
return NormalCompletion(undefined);
}
// #sec-createglobalfunctionbinding
CreateGlobalFunctionBinding(N, V, D) {
// 1. Let envRec be the global Environment Record for which the method was invoked.
const envRec = this;
// 2. Let ObjRec be envRec.[[ObjectRecord]].
const ObjRec = envRec.ObjectRecord;
// 3. Let globalObject be the binding object for ObjRec.
const globalObject = ObjRec.bindingObject;
// 4. Let existingProp be ? globalObject.[[GetOwnProperty]](N).
const existingProp = Q(globalObject.GetOwnProperty(N));
// 5. If existingProp is undefined or existingProp.[[Configurable]] is true, then
let desc;
if (existingProp === Value.undefined || existingProp.Configurable === Value.true) {
// a. Let desc be the PropertyDescriptor { [[Value]]: V, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: D }.
desc = Descriptor({
Value: V,
Writable: Value.true,
Enumerable: Value.true,
Configurable: D,
});
} else {
// a. Let desc be the PropertyDescriptor { [[Value]]: V }.
desc = Descriptor({
Value: V,
});
}
// 7. Perform ? DefinePropertyOrThrow(globalObject, N, desc).
Q(DefinePropertyOrThrow(globalObject, N, desc));
// 8. Record that the binding for N in ObjRec has been initialized.
// 9. Perform ? Set(globalObject, N, V, false).
Q(Set(globalObject, N, V, Value.false));
// 10. Let varDeclaredNames be envRec.[[VarNames]].
const varDeclaredNames = envRec.VarNames;
// 11. If varDeclaredNames does not contain N, then
if (!varDeclaredNames.includes(N)) {
// a. Append N to varDeclaredNames.
varDeclaredNames.push(N);
}
// 1. Return NormalCompletion(empty).
return NormalCompletion(undefined);
}
mark(m) {
m(this.ObjectRecord);
m(this.GlobalThisValue);
m(this.DeclarativeRecord);
}
}
// #sec-module-environment-records
export class ModuleEnvironmentRecord extends DeclarativeEnvironmentRecord {
// #sec-module-environment-records-getbindingvalue-n-s
GetBindingValue(N, S) {
// 1. Assert: S is true.
Assert(S === Value.true);
// 2. Let envRec be the module Environment Record for which the method was invoked.
const envRec = this;
// 3. Assert: envRec has a binding for N.
const binding = envRec.bindings.get(N);
Assert(binding !== undefined);
// 4. If the binding for N is an indirect binding, then
if (binding.indirect === true) {
// a. Let M and N2 be the indirection values provided when this binding for N was created.
const [M, N2] = binding.target;
// b.Let targetEnv be M.[[Environment]].
const targetEnv = M.Environment;
// c. If targetEnv is undefined, throw a ReferenceError exception.
if (targetEnv === Value.undefined) {
return surroundingAgent.Throw('ReferenceError', 'NotDefined', N);
}
// d. Return ? targetEnv.GetBindingValue(N2, true).
return Q(targetEnv.GetBindingValue(N2, Value.true));
}
// 5. If the binding for N in envRec is an uninitialized binding, throw a ReferenceError exception.
if (binding.initialized === false) {
return surroundingAgent.Throw('ReferenceError', 'NotInitialized', N);
}
// 6. Return the value currently bound to N in envRec.
return binding.value;
}
// #sec-module-environment-records-deletebinding-n
DeleteBinding() {
Assert(false, 'This method is never invoked. See #sec-delete-operator-static-semantics-early-errors');
}
// #sec-module-environment-records-hasthisbinding
HasThisBinding() {
// Return true.
return Value.true;
}
// #sec-module-environment-records-getthisbinding
GetThisBinding() {
// Return undefined.
return Value.undefined;
}
// #sec-createimportbinding
CreateImportBinding(N, M, N2) {
// 1. Let envRec be the module Environment Record for which the method was invoked.
const envRec = this;
// 2. Assert: envRec does not already have a binding for N.
Assert(envRec.HasBinding(N) === Value.false);
// 3. Assert: M is a Module Record.
Assert(M instanceof AbstractModuleRecord);
// 4. Assert: When M.[[Environment]] is instantiated it will have a direct binding for N2.
// 5. Create an immutable indirect binding in envRec for N that references M and N2 as its target binding and record that the binding is initialized.
envRec.bindings.set(N, {
indirect: true,
target: [M, N2],
initialized: true,
mark(m) {
m(this.target[0]);
m(this.target[1]);
},
});
// 6. Return NormalCompletion(empty).
return NormalCompletion(undefined);
}
}
// 8.1.2.1 #sec-getidentifierreference
export function GetIdentifierReference(env, name, strict) {
// 1. If lex is the value null, then
if (env === Value.null) {
// a. Return a value of type Reference whose base value component is undefined, whose
// referenced name component is name, and whose strict reference flag is strict.
return new Reference({
BaseValue: Value.undefined,
ReferencedName: name,
StrictReference: strict,
});
}
// 2. Let exists be ? envRec.HasBinding(name).
const exists = Q(env.HasBinding(name));
// 3. If exists is true, then
if (exists === Value.true) {
// a. Return a value of type Reference whose base value component is envRec, whose
// referenced name component is name, and whose strict reference flag is strict.
return new Reference({
BaseValue: env,
ReferencedName: name,
StrictReference: strict,
});
} else {
// a. Let outer be env.[[OuterEnv]].
const outer = env.OuterEnv;
// b. Return ? GetIdentifierReference(outer, name, strict).
return Q(GetIdentifierReference(outer, name, strict));
}
}
// #sec-newdeclarativeenvironment
export function NewDeclarativeEnvironment(E) {
// 1. Let env be a new declarative Environment Record containing O as the binding object.
const env = new DeclarativeEnvironmentRecord();
// 2. Set env.[[OuterEnv]] to E.
env.OuterEnv = E;
// 3. Return env.
return env;
}
// #sec-newobjectenvironment
export function NewObjectEnvironment(O, E) {
// 1. Let env be a new object Environment Record containing O as the binding object.
const env = new ObjectEnvironmentRecord(O);
// 2. Set env.[[OuterEnv]] to E.
env.OuterEnv = E;
// 3. Return env.
return env;
}
// #sec-newfunctionenvironment
export function NewFunctionEnvironment(F, newTarget) {
// 1. Assert: F is an ECMAScript function.
Assert(isECMAScriptFunctionObject(F));
// 2. Assert: Type(newTarget) is Undefined or Object.
Assert(Type(newTarget) === 'Undefined' || Type(newTarget) === 'Object');
// 3. Let env be a new function Environment Record containing no bindings.
const env = new FunctionEnvironmentRecord();
// 4. Set env.[[FunctionObject]] to F.
env.FunctionObject = F;
// 5. If F.[[ThisMode]] is lexical, set env.[[ThisBindingStatus]] to lexical.
if (F.ThisMode === 'lexical') {
env.ThisBindingStatus = 'lexical';
} else { // 6. Else, set env.[[ThisBindingStatus]] to uninitialized.
env.ThisBindingStatus = 'uninitialized';
}
// 7. Let home be F.[[HomeObject]].
const home = F.HomeObject;
// 8. Set env.[[HomeObject]] to home.
env.HomeObject = home;
// 9. Set env.[[NewTarget]] to newTarget.
env.NewTarget = newTarget;
// 10. Set env.[[OuterEnv]] to F.[[Environment]].
env.OuterEnv = F.Environment;
// 11. Return env.
return env;
}
// #sec-newglobalenvironment
export function NewGlobalEnvironment(G, thisValue) {
// 1. Let objRec be a new object Environment Record containing G as the binding object.
const objRec = new ObjectEnvironmentRecord(G);
// 2. Let dclRec be a new declarative Environment Record containing no bindings.
const dclRec = new DeclarativeEnvironmentRecord();
// 3. Let env be a new global Environment Record.
const env = new GlobalEnvironmentRecord();
// 4. Set env.[[ObjectRecord]] to objRec.
env.ObjectRecord = objRec;
// 5. Set env.[[GlobalThisValue]] to thisValue.
env.GlobalThisValue = thisValue;
// 6. Set env.[[DeclarativeRecord]] to dclRec.
env.DeclarativeRecord = dclRec;
// 7. Set env.[[VarNames]] to a new empty List.
env.VarNames = [];
// 8. Set env.[[OuterEnv]] to null.
env.OuterEnv = Value.null;
// 9. Return env.
return env;
}
// #sec-newmoduleenvironment
export function NewModuleEnvironment(E) {
// 1. Let env be a new module Environment Record containing no bindings.
const env = new ModuleEnvironmentRecord();
// 2. Set env.[[OuterEnv]] to E.
env.OuterEnv = E;
// 3. Return env.
return env;
}
-326
View File
@@ -1,326 +0,0 @@
import { surroundingAgent } from './engine.mjs';
import { OutOfRange } from './helpers.mjs';
import {
Evaluate_Script,
Evaluate_ScriptBody,
Evaluate_Module,
Evaluate_ModuleBody,
Evaluate_ImportDeclaration,
Evaluate_ExportDeclaration,
Evaluate_ClassDeclaration,
Evaluate_LexicalDeclaration,
Evaluate_FunctionDeclaration,
Evaluate_HoistableDeclaration,
Evaluate_Block,
Evaluate_VariableStatement,
Evaluate_ExpressionStatement,
Evaluate_EmptyStatement,
Evaluate_IfStatement,
Evaluate_ReturnStatement,
Evaluate_TryStatement,
Evaluate_ThrowStatement,
Evaluate_DebuggerStatement,
Evaluate_BreakableStatement,
Evaluate_LabelledStatement,
Evaluate_ForBinding,
Evaluate_CaseClause,
Evaluate_BreakStatement,
Evaluate_ContinueStatement,
Evaluate_WithStatement,
Evaluate_IdentifierReference,
Evaluate_CommaOperator,
Evaluate_This,
Evaluate_Literal,
Evaluate_ArrayLiteral,
Evaluate_ObjectLiteral,
Evaluate_TemplateLiteral,
Evaluate_ClassExpression,
Evaluate_FunctionExpression,
Evaluate_GeneratorExpression,
Evaluate_AsyncFunctionExpression,
Evaluate_AsyncGeneratorExpression,
Evaluate_AdditiveExpression,
Evaluate_MultiplicativeExpression,
Evaluate_ExponentiationExpression,
Evaluate_UpdateExpression,
Evaluate_ShiftExpression,
Evaluate_LogicalORExpression,
Evaluate_LogicalANDExpression,
Evaluate_BinaryBitwiseExpression,
Evaluate_RelationalExpression,
Evaluate_CoalesceExpression,
Evaluate_EqualityExpression,
Evaluate_CallExpression,
Evaluate_NewExpression,
Evaluate_MemberExpression,
Evaluate_OptionalExpression,
Evaluate_TaggedTemplateExpression,
Evaluate_SuperCall,
Evaluate_SuperProperty,
Evaluate_NewTarget,
Evaluate_ImportMeta,
Evaluate_ImportCall,
Evaluate_AwaitExpression,
Evaluate_YieldExpression,
Evaluate_ParenthesizedExpression,
Evaluate_AssignmentExpression,
Evaluate_UnaryExpression,
Evaluate_ArrowFunction,
Evaluate_AsyncArrowFunction,
Evaluate_ConditionalExpression,
Evaluate_RegularExpressionLiteral,
Evaluate_AnyFunctionBody,
Evaluate_ExpressionBody,
} from './runtime-semantics/all.mjs';
export function* Evaluate(node) {
surroundingAgent.runningExecutionContext.callSite.setLocation(node);
if (surroundingAgent.hostDefinedOptions.onNodeEvaluation) {
surroundingAgent.hostDefinedOptions.onNodeEvaluation(node, surroundingAgent.currentRealmRecord);
}
let result;
switch (node.type) {
// Language
case 'Script':
result = yield* Evaluate_Script(node);
break;
case 'ScriptBody':
result = yield* Evaluate_ScriptBody(node);
break;
case 'Module':
result = yield* Evaluate_Module(node);
break;
case 'ModuleBody':
result = yield* Evaluate_ModuleBody(node);
break;
// Statements
case 'Block':
result = yield* Evaluate_Block(node);
break;
case 'VariableStatement':
result = yield* Evaluate_VariableStatement(node);
break;
case 'EmptyStatement':
result = Evaluate_EmptyStatement(node);
break;
case 'IfStatement':
result = yield* Evaluate_IfStatement(node);
break;
case 'ExpressionStatement':
result = yield* Evaluate_ExpressionStatement(node);
break;
case 'WhileStatement':
case 'DoWhileStatement':
case 'SwitchStatement':
case 'ForStatement':
case 'ForInStatement':
case 'ForOfStatement':
case 'ForAwaitStatement':
result = yield* Evaluate_BreakableStatement(node);
break;
case 'ForBinding':
result = Evaluate_ForBinding(node);
break;
case 'CaseClause':
case 'DefaultClause':
result = yield* Evaluate_CaseClause(node);
break;
case 'BreakStatement':
result = Evaluate_BreakStatement(node);
break;
case 'ContinueStatement':
result = Evaluate_ContinueStatement(node);
break;
case 'LabelledStatement':
result = yield* Evaluate_LabelledStatement(node);
break;
case 'ReturnStatement':
result = yield* Evaluate_ReturnStatement(node);
break;
case 'ThrowStatement':
result = yield* Evaluate_ThrowStatement(node);
break;
case 'TryStatement':
result = yield* Evaluate_TryStatement(node);
break;
case 'DebuggerStatement':
result = Evaluate_DebuggerStatement(node);
break;
case 'WithStatement':
result = yield* Evaluate_WithStatement(node);
break;
// Declarations
case 'ImportDeclaration':
result = Evaluate_ImportDeclaration(node);
break;
case 'ExportDeclaration':
result = yield* Evaluate_ExportDeclaration(node);
break;
case 'ClassDeclaration':
result = yield* Evaluate_ClassDeclaration(node);
break;
case 'LexicalDeclaration':
result = yield* Evaluate_LexicalDeclaration(node);
break;
case 'FunctionDeclaration':
result = Evaluate_FunctionDeclaration(node);
break;
case 'GeneratorDeclaration':
case 'AsyncFunctionDeclaration':
case 'AsyncGeneratorDeclaration':
result = Evaluate_HoistableDeclaration(node);
break;
// Expressions
case 'CommaOperator':
result = yield* Evaluate_CommaOperator(node);
break;
case 'ThisExpression':
result = Evaluate_This(node);
break;
case 'IdentifierReference':
result = Evaluate_IdentifierReference(node);
break;
case 'NullLiteral':
case 'BooleanLiteral':
case 'NumericLiteral':
case 'StringLiteral':
result = Evaluate_Literal(node);
break;
case 'ArrayLiteral':
result = yield* Evaluate_ArrayLiteral(node);
break;
case 'ObjectLiteral':
result = yield* Evaluate_ObjectLiteral(node);
break;
case 'FunctionExpression':
result = yield* Evaluate_FunctionExpression(node);
break;
case 'ClassExpression':
result = yield* Evaluate_ClassExpression(node);
break;
case 'GeneratorExpression':
result = yield* Evaluate_GeneratorExpression(node);
break;
case 'AsyncFunctionExpression':
result = yield* Evaluate_AsyncFunctionExpression(node);
break;
case 'AsyncGeneratorExpression':
result = yield* Evaluate_AsyncGeneratorExpression(node);
break;
case 'TemplateLiteral':
result = yield* Evaluate_TemplateLiteral(node);
break;
case 'ParenthesizedExpression':
result = yield* Evaluate_ParenthesizedExpression(node);
break;
case 'AdditiveExpression':
result = yield* Evaluate_AdditiveExpression(node);
break;
case 'MultiplicativeExpression':
result = yield* Evaluate_MultiplicativeExpression(node);
break;
case 'ExponentiationExpression':
result = yield* Evaluate_ExponentiationExpression(node);
break;
case 'UpdateExpression':
result = yield* Evaluate_UpdateExpression(node);
break;
case 'ShiftExpression':
result = yield* Evaluate_ShiftExpression(node);
break;
case 'LogicalORExpression':
result = yield* Evaluate_LogicalORExpression(node);
break;
case 'LogicalANDExpression':
result = yield* Evaluate_LogicalANDExpression(node);
break;
case 'BitwiseANDExpression':
case 'BitwiseXORExpression':
case 'BitwiseORExpression':
result = yield* Evaluate_BinaryBitwiseExpression(node);
break;
case 'RelationalExpression':
result = yield* Evaluate_RelationalExpression(node);
break;
case 'CoalesceExpression':
result = yield* Evaluate_CoalesceExpression(node);
break;
case 'EqualityExpression':
result = yield* Evaluate_EqualityExpression(node);
break;
case 'CallExpression':
result = yield* Evaluate_CallExpression(node);
break;
case 'NewExpression':
result = yield* Evaluate_NewExpression(node);
break;
case 'MemberExpression':
result = yield* Evaluate_MemberExpression(node);
break;
case 'OptionalExpression':
result = yield* Evaluate_OptionalExpression(node);
break;
case 'TaggedTemplateExpression':
result = yield* Evaluate_TaggedTemplateExpression(node);
break;
case 'SuperProperty':
result = yield* Evaluate_SuperProperty(node);
break;
case 'SuperCall':
result = yield* Evaluate_SuperCall(node);
break;
case 'NewTarget':
result = Evaluate_NewTarget(node);
break;
case 'ImportMeta':
result = Evaluate_ImportMeta(node);
break;
case 'ImportCall':
result = yield* Evaluate_ImportCall(node);
break;
case 'AssignmentExpression':
result = yield* Evaluate_AssignmentExpression(node);
break;
case 'YieldExpression':
result = yield* Evaluate_YieldExpression(node);
break;
case 'AwaitExpression':
result = yield* Evaluate_AwaitExpression(node);
break;
case 'UnaryExpression':
result = yield* Evaluate_UnaryExpression(node);
break;
case 'ArrowFunction':
result = yield* Evaluate_ArrowFunction(node);
break;
case 'AsyncArrowFunction':
result = yield* Evaluate_AsyncArrowFunction(node);
break;
case 'ConditionalExpression':
result = yield* Evaluate_ConditionalExpression(node);
break;
case 'RegularExpressionLiteral':
result = Evaluate_RegularExpressionLiteral(node);
break;
case 'AsyncFunctionBody':
case 'GeneratorBody':
case 'AsyncGeneratorBody':
result = yield* Evaluate_AnyFunctionBody(node);
break;
case 'ExpressionBody':
result = yield* Evaluate_ExpressionBody(node);
break;
default:
throw new OutOfRange('Evaluate', node);
}
if (surroundingAgent.hostDefinedOptions.onNodeEvaluationComplete) {
surroundingAgent.hostDefinedOptions.onNodeEvaluationComplete(
{ node, result },
);
}
return result;
}
-349
View File
@@ -1,349 +0,0 @@
import { surroundingAgent } from './engine.mjs';
import { Type, Value, Descriptor } from './value.mjs';
import { ToString, DefinePropertyOrThrow, CreateBuiltinFunction } from './abstract-ops/all.mjs';
import { X, AwaitFulfilledFunctions } from './completion.mjs';
function convertValueForKey(key) {
if (typeof key === 'string') {
return Symbol.for(`engine262_helper_key_${key}`);
}
switch (Type(key)) {
case 'String':
return key.stringValue();
case 'Number':
if (key.numberValue() === 0 && Object.is(key.numberValue(), -0)) {
return key;
}
return key.numberValue();
default:
return key;
}
}
export class ValueMap {
constructor() {
this.map = new Map();
}
get size() {
return this.map.size;
}
get(key) {
return this.map.get(convertValueForKey(key));
}
set(key, value) {
this.map.set(convertValueForKey(key), value);
return this;
}
has(key) {
return this.map.has(convertValueForKey(key));
}
delete(key) {
return this.map.delete(convertValueForKey(key));
}
* keys() {
for (const [key] of this.entries()) {
yield key;
}
}
entries() {
return this[Symbol.iterator]();
}
forEach(cb) {
for (const [key, value] of this.entries()) {
cb(value, key, this);
}
}
* [Symbol.iterator]() {
for (const [key, value] of this.map.entries()) {
if (typeof key === 'string' || typeof key === 'number') {
yield [new Value(key), value];
} else {
yield [key, value];
}
}
}
mark(m) {
for (const [k, v] of this.entries()) {
m(k);
m(v);
}
}
}
export class ValueSet {
constructor(init) {
this.set = new Set();
if (init !== undefined && init !== null) {
for (const item of init) {
this.add(item);
}
}
}
get size() {
return this.set.size;
}
add(item) {
this.set.add(convertValueForKey(item));
return this;
}
has(item) {
return this.set.has(convertValueForKey(item));
}
delete(item) {
return this.set.delete(convertValueForKey(item));
}
values() {
return this[Symbol.iterator]();
}
* [Symbol.iterator]() {
for (const key of this.set.values()) {
if (typeof key === 'string' || typeof key === 'number') {
yield new Value(key);
} else {
yield key;
}
}
}
mark(m) {
for (const v of this.values()) {
m(v);
}
}
}
export class OutOfRange extends RangeError {
/* istanbul ignore next */
constructor(fn, detail) {
super(`${fn}() argument out of range`);
this.detail = detail;
}
}
export function unwind(iterator, maxSteps = 1) {
let steps = 0;
while (true) {
const { done, value } = iterator.next('Unwind');
if (done) {
return value;
}
/* istanbul ignore next */
steps += 1;
if (steps > maxSteps) {
throw new RangeError('Max steps exceeded');
}
}
}
const kSafeToResume = Symbol('kSameToResume');
export function handleInResume(fn, ...args) {
const bound = () => fn(...args);
bound[kSafeToResume] = true;
return bound;
}
export function resume(context, completion) {
const { value } = context.codeEvaluationState.next(completion);
if (typeof value === 'function' && value[kSafeToResume] === true) {
return X(value());
}
return value;
}
export class CallSite {
constructor(context) {
this.context = context;
this.lastNode = null;
this.constructCall = false;
}
clone(context = this.context) {
const c = new CallSite(context);
c.lastNode = this.lastNode;
c.constructCall = this.constructCall;
return c;
}
isTopLevel() {
return this.context.Function === Value.null;
}
isConstructCall() {
return this.constructCall;
}
isAsync() {
if (this.context.Function !== Value.null && this.context.Function.ECMAScriptCode) {
const code = this.context.Function.ECMAScriptCode;
return code.type === 'AsyncFunctionBody' || code.type === 'AsyncGeneratorBody';
}
return false;
}
isNative() {
return !!this.context.Function.nativeFunction;
}
getFunctionName() {
if (this.context.Function !== Value.null) {
const name = this.context.Function.properties.get(new Value('name'));
if (name) {
return X(ToString(name.Value)).stringValue();
}
}
return null;
}
getSpecifier() {
if (this.context.ScriptOrModule !== Value.null) {
return this.context.ScriptOrModule.HostDefined.specifier;
}
return null;
}
setLocation(node) {
this.lastNode = node;
}
get lineNumber() {
if (this.lastNode) {
return this.lastNode.location.start.line;
}
return null;
}
get columnNumber() {
if (this.lastNode) {
return this.lastNode.location.start.column;
}
return null;
}
loc() {
if (this.isNative()) {
return 'native';
}
let out = '';
const specifier = this.getSpecifier();
if (specifier) {
out += specifier;
} else {
out += '<anonymous>';
}
if (this.lineNumber !== null) {
out += `:${this.lineNumber}`;
if (this.columnNumber !== null) {
out += `:${this.columnNumber}`;
}
}
return out.trim();
}
toString() {
const isAsync = this.isAsync();
const functionName = this.getFunctionName();
const isConstructCall = this.isConstructCall();
const isMethodCall = !isConstructCall && !this.isTopLevel();
let string = isAsync ? 'async ' : '';
if (isConstructCall) {
string += 'new ';
}
if (isMethodCall || isConstructCall) {
if (functionName) {
string += functionName;
} else {
string += '<anonymous>';
}
} else if (functionName) {
string += functionName;
} else {
return `${string}${this.loc()}`;
}
return `${string} (${this.loc()})`;
}
}
function captureAsyncStack(stack) {
let promise = stack[0].context.promiseCapability.Promise;
for (let i = 0; i < 10; i += 1) {
if (promise.PromiseFulfillReactions.length !== 1) {
return;
}
const [reaction] = promise.PromiseFulfillReactions;
if (reaction.Handler && reaction.Handler.Callback.nativeFunction === AwaitFulfilledFunctions) {
const asyncContext = reaction.Handler.Callback.AsyncContext;
stack.push(asyncContext.callSite.clone());
if ('PromiseState' in asyncContext.promiseCapability.Promise) {
promise = asyncContext.promiseCapability.Promise;
} else {
return;
}
} else if (reaction.Capability !== Value.undefined) {
if ('PromiseState' in reaction.Capability.Promise) {
promise = reaction.Capability.Promise;
} else {
return;
}
}
}
}
export function captureStack(O) {
const stack = [];
for (let i = surroundingAgent.executionContextStack.length - 2; i >= 0; i -= 1) {
const e = surroundingAgent.executionContextStack[i];
if (e.VariableEnvironment === undefined && e.Function === Value.null) {
break;
}
stack.push(e.callSite.clone());
if (e.callSite.isAsync()) {
i -= 1; // skip original execution context which has no useful information.
}
}
if (stack.length > 0 && stack[0].context.promiseCapability) {
captureAsyncStack(stack);
}
let cache = null;
X(DefinePropertyOrThrow(O, new Value('stack'), Descriptor({
Get: CreateBuiltinFunction(() => {
if (cache === null) {
let errorString = X(ToString(O)).stringValue();
stack.forEach((s) => {
errorString = `${errorString}\n at ${s.toString()}`;
});
cache = new Value(errorString);
}
return cache;
}, []),
Set: CreateBuiltinFunction(([value = Value.undefined]) => {
cache = value;
return Value.undefined;
}, []),
Enumerable: Value.false,
Configurable: Value.true,
})));
}
-205
View File
@@ -1,205 +0,0 @@
import { surroundingAgent } from './engine.mjs';
import { Type, Value, wellKnownSymbols } from './value.mjs';
import {
Call, IsArray, Get, LengthOfArrayLike,
EscapeRegExpPattern,
} from './abstract-ops/all.mjs';
import { Q, X } from './completion.mjs';
const bareKeyRe = /^[a-zA-Z_][a-zA-Z_0-9]*$/;
const getObjectTag = (value, wrap) => {
let s;
try {
s = X(Get(value, wellKnownSymbols.toStringTag)).stringValue();
} catch {}
try {
const c = X(Get(value, new Value('constructor')));
s = X(Get(c, new Value('name'))).stringValue();
} catch {}
if (s) {
if (wrap) {
return `[${s}] `;
}
return s;
}
return '';
};
const compactObject = (realm, value) => {
try {
const toString = X(Get(value, new Value('toString')));
const objectToString = realm.Intrinsics['%Object.prototype.toString%'];
if (toString.nativeFunction === objectToString.nativeFunction) {
return X(Call(toString, value)).stringValue();
} else {
const tag = getObjectTag(value, false) || 'Unknown';
const ctor = X(Get(value, new Value('constructor')));
if (Type(ctor) === 'Object') {
const ctorName = X(Get(ctor, new Value('name'))).stringValue();
if (ctorName !== '') {
return `#<${ctorName}>`;
}
return `[object ${tag}]`;
}
return `[object ${tag}]`;
}
} catch (e) {
return '[object Unknown]';
}
};
const INSPECTORS = {
Completion: (v, ctx, i) => i(v.Value),
Null: () => 'null',
Undefined: () => 'undefined',
Boolean: (v) => v.boolean.toString(),
Number: (v) => {
const n = v.numberValue();
if (n === 0 && Object.is(n, -0)) {
return '-0';
}
return n.toString();
},
BigInt: (v) => `${v.bigintValue()}n`,
String: (v) => {
const s = JSON.stringify(v.stringValue()).slice(1, -1);
return `'${s}'`;
},
Symbol: (v) => `Symbol(${v.Description === Value.undefined ? '' : v.Description.stringValue()})`,
Object: (v, ctx, i) => {
if (ctx.inspected.includes(v)) {
return '[Circular]';
}
if ('PromiseState' in v) {
ctx.indent += 1;
const result = v.PromiseState === 'pending' ? 'undefined' : i(v.PromiseResult);
ctx.indent -= 1;
return `Promise {
[[PromiseState]]: ${v.PromiseState}
[[PromiseResult]]: ${result}
}`;
}
if ('Call' in v) {
const name = v.properties.get(new Value('name'));
if (name !== undefined && name.Value.stringValue() !== '') {
return `[Function: ${name.Value.stringValue()}]`;
}
return '[Function]';
}
if ('ErrorData' in v) {
let e = Q(Get(v, new Value('stack')));
if (!e.stringValue) {
const toString = Q(Get(v, new Value('toString')));
e = X(Call(toString, v));
}
return e.stringValue();
}
if ('RegExpMatcher' in v) {
const P = EscapeRegExpPattern(v.OriginalSource, v.OriginalFlags).stringValue();
const F = v.OriginalFlags.stringValue();
return `/${P}/${F}`;
}
if ('DateValue' in v) {
const d = new Date(v.DateValue.numberValue());
if (Number.isNaN(d.getTime())) {
return '[Date Invalid]';
}
return `[Date ${d.toISOString()}]`;
}
if ('BooleanData' in v) {
return `[Boolean ${i(v.BooleanData)}]`;
}
if ('NumberData' in v) {
return `[Number ${i(v.NumberData)}]`;
}
if ('BigIntData' in v) {
return `[BigInt ${i(v.BigIntData)}]`;
}
if ('StringData' in v) {
return `[String ${i(v.StringData)}]`;
}
if ('SymbolData' in v) {
return `[Symbol ${i(v.SymbolData)}]`;
}
ctx.indent += 1;
ctx.inspected.push(v);
try {
const isArray = IsArray(v) === Value.true;
const isTypedArray = 'TypedArrayName' in v;
if (isArray || isTypedArray) {
const length = X(LengthOfArrayLike(v)).numberValue();
let holes = 0;
const out = [];
for (let j = 0; j < length; j += 1) {
const elem = X(v.GetOwnProperty(new Value(j.toString())));
if (elem === Value.undefined) {
holes += 1;
} else {
if (holes > 0) {
out.push(`<${holes} empty items>`);
holes = 0;
}
if (elem.Value) {
out.push(i(elem.Value));
} else {
out.push('<accessor>');
}
}
}
return `${isTypedArray ? `${v.TypedArrayName.stringValue()} ` : ''}[${out.join(', ')}]`;
}
const keys = X(v.OwnPropertyKeys());
const cache = [];
for (const key of keys) {
const C = X(v.GetOwnProperty(key));
if (C.Enumerable === Value.true) {
cache.push([
Type(key) === 'String' && bareKeyRe.test(key.stringValue()) ? key.stringValue() : i(key),
C.Value ? i(C.Value) : '<accessor>',
]);
}
}
const tag = getObjectTag(v);
let out = tag && tag !== 'Object' ? `${tag} {` : '{';
if (cache.length > 5) {
cache.forEach((c) => {
out = `${out}\n${' '.repeat(ctx.indent)}${c[0]}: ${c[1]},`;
});
return `${out}\n${' '.repeat(ctx.indent - 1)}}`;
} else {
const oc = ctx.compact;
ctx.compact = true;
cache.forEach((c, index) => {
out = `${out}${index === 0 ? '' : ','} ${c[0]}: ${c[1]}`;
});
ctx.compact = oc;
return `${out} }`;
}
} catch {
return compactObject(ctx, v);
} finally {
ctx.indent -= 1;
ctx.inspected.pop();
}
},
};
export function inspect(value) {
const context = {
realm: surroundingAgent.currentRealmRecord,
indent: 0,
inspected: [],
};
const inner = (v) => INSPECTORS[Type(v)](v, context, inner);
return inner(value);
}
@@ -1,56 +0,0 @@
import { surroundingAgent } from '../engine.mjs';
import { Value, Descriptor } from '../value.mjs';
import {
CreateMethodProperty,
ToString,
IterableToList,
OrdinaryCreateFromConstructor,
DefinePropertyOrThrow,
CreateArrayFromList,
} from '../abstract-ops/all.mjs';
import { Q, X } from '../completion.mjs';
import { captureStack } from '../helpers.mjs';
import { BootstrapConstructor } from './Bootstrap.mjs';
// #sec-aggregate-error-constructor
function AggregateErrorConstructor([errors = Value.undefined, message = Value.undefined], { NewTarget }) {
// 1. If NewTarget is undefined, let newTarget be the active function object, else let newTarget be NewTarget.
let newTarget;
if (NewTarget === Value.undefined) {
newTarget = surroundingAgent.activeFunctionObject;
} else {
newTarget = NewTarget;
}
// 2. Let O be ? OrdinaryCreateFromConstructor(newTarget, "%AggregateError.prototype%", « [[ErrorData]] »).
const O = Q(OrdinaryCreateFromConstructor(newTarget, '%AggregateError.prototype%', [
'ErrorData',
]));
// 3. If message is not undefined, then
if (message !== Value.undefined) {
// a. Let msg be ? ToString(message).
const msg = Q(ToString(message));
// b. Perform ! CreateMethodProperty(O, "message", msg).
X(CreateMethodProperty(O, new Value('message'), msg));
}
// 4. Let errorsList be ? IterableToList(errors).
const errorsList = Q(IterableToList(errors));
// 5. Perform ! DefinePropertyOrThrow(O, "errors", Property Descriptor { [[Configurable]]: true, [[Enumerable]]: false, [[Writable]]: true, [[Value]]: ! CreateArrayFromList(errorsList) }).
X(DefinePropertyOrThrow(O, new Value('errors'), Descriptor({
Configurable: Value.true,
Enumerable: Value.false,
Writable: Value.true,
Value: X(CreateArrayFromList(errorsList)),
})));
// NON-SPEC
X(captureStack(O));
// 6. Return O.
return O;
}
export function BootstrapAggregateError(realmRec) {
const c = BootstrapConstructor(realmRec, AggregateErrorConstructor, 'AggregateError', 2, realmRec.Intrinsics['%AggregateError.prototype%'], []);
c.Prototype = realmRec.Intrinsics['%Error%'];
realmRec.Intrinsics['%AggregateError%'] = c;
}
@@ -1,11 +0,0 @@
import { Value } from '../value.mjs';
import { BootstrapPrototype } from './Bootstrap.mjs';
export function BootstrapAggregateErrorPrototype(realmRec) {
const proto = BootstrapPrototype(realmRec, [
['name', new Value('AggregateError')],
['message', new Value('')],
], realmRec.Intrinsics['%Error.prototype%'], 'AggregateError');
realmRec.Intrinsics['%AggregateError.prototype%'] = proto;
}
-215
View File
@@ -1,215 +0,0 @@
import {
surroundingAgent,
} from '../engine.mjs';
import {
AbruptCompletion,
Q,
ThrowCompletion, X,
} from '../completion.mjs';
import {
ArrayCreate,
Assert,
Call,
Construct,
CreateDataProperty,
CreateDataPropertyOrThrow,
Get,
GetIterator,
GetMethod,
GetPrototypeFromConstructor,
IsArray,
IsCallable,
IsConstructor,
IteratorClose,
IteratorStep,
IteratorValue,
Set,
LengthOfArrayLike,
ToObject,
ToString,
ToUint32,
} from '../abstract-ops/all.mjs';
import {
Type,
Value,
wellKnownSymbols,
} from '../value.mjs';
import { OutOfRange } from '../helpers.mjs';
import { BootstrapConstructor } from './Bootstrap.mjs';
// 22.1.1 #sec-array-constructor
function ArrayConstructor(argumentsList, { NewTarget }) {
const numberOfArgs = argumentsList.length;
if (numberOfArgs === 0) {
// 22.1.1.1 #sec-array-constructor-array
Assert(numberOfArgs === 0);
if (Type(NewTarget) === 'Undefined') {
NewTarget = surroundingAgent.activeFunctionObject;
}
const proto = GetPrototypeFromConstructor(NewTarget, '%Array.prototype%');
return ArrayCreate(new Value(0), proto);
} else if (numberOfArgs === 1) {
// 22.1.1.2 #sec-array-len
const [len] = argumentsList;
Assert(numberOfArgs === 1);
if (Type(NewTarget) === 'Undefined') {
NewTarget = surroundingAgent.activeFunctionObject;
}
const proto = GetPrototypeFromConstructor(NewTarget, '%Array.prototype%');
const array = ArrayCreate(new Value(0), proto);
let intLen;
if (Type(len) !== 'Number') {
const defineStatus = X(CreateDataProperty(array, new Value('0'), len));
Assert(defineStatus === Value.true);
intLen = new Value(1);
} else {
intLen = ToUint32(len);
if (intLen.numberValue() !== len.numberValue()) {
return surroundingAgent.Throw('RangeError', 'InvalidArrayLength', len);
}
}
Set(array, new Value('length'), intLen, Value.true);
return array;
} else if (numberOfArgs >= 2) {
// 22.1.1.3 #sec-array-items
const items = argumentsList;
Assert(numberOfArgs >= 2);
if (Type(NewTarget) === 'Undefined') {
NewTarget = surroundingAgent.activeFunctionObject;
}
const proto = GetPrototypeFromConstructor(NewTarget, '%Array.prototype%');
const array = ArrayCreate(new Value(0), proto);
let k = 0;
while (k < numberOfArgs) {
const Pk = ToString(new Value(k));
const itemK = items[k];
const defineStatus = X(CreateDataProperty(array, Pk, itemK));
Assert(defineStatus === Value.true);
k += 1;
}
Assert(X(Get(array, new Value('length'))).numberValue() === numberOfArgs);
return array;
}
throw new OutOfRange('ArrayConstructor', numberOfArgs);
}
// 22.1.2.1 #sec-array.from
function Array_from([items = Value.undefined, mapfn = Value.undefined, thisArg = Value.undefined], { thisValue }) {
const C = thisValue;
let mapping;
let A;
if (mapfn === Value.undefined) {
mapping = false;
} else {
if (IsCallable(mapfn) === Value.false) {
return surroundingAgent.Throw('TypeError', 'NotAFunction', mapfn);
}
mapping = true;
}
const usingIterator = Q(GetMethod(items, wellKnownSymbols.iterator));
if (usingIterator !== Value.undefined) {
if (IsConstructor(C) === Value.true) {
A = Q(Construct(C));
} else {
A = X(ArrayCreate(new Value(0)));
}
const iteratorRecord = Q(GetIterator(items, 'sync', usingIterator));
let k = 0;
while (true) { // eslint-disable-line no-constant-condition
if (k >= (2 ** 53) - 1) {
const error = ThrowCompletion(surroundingAgent.Throw('TypeError', 'ArrayPastSafeLength').Value);
return Q(IteratorClose(iteratorRecord, error));
}
const Pk = X(ToString(new Value(k)));
const next = Q(IteratorStep(iteratorRecord));
if (next === Value.false) {
Q(Set(A, new Value('length'), new Value(k), Value.true));
return A;
}
const nextValue = Q(IteratorValue(next));
let mappedValue;
if (mapping) {
mappedValue = Call(mapfn, thisArg, [nextValue, new Value(k)]);
if (mappedValue instanceof AbruptCompletion) {
return Q(IteratorClose(iteratorRecord, mappedValue));
}
mappedValue = mappedValue.Value;
} else {
mappedValue = nextValue;
}
const defineStatus = CreateDataPropertyOrThrow(A, Pk, mappedValue);
if (defineStatus instanceof AbruptCompletion) {
return Q(IteratorClose(iteratorRecord, defineStatus));
}
k += 1;
}
}
const arrayLike = X(ToObject(items));
const len = Q(LengthOfArrayLike(arrayLike));
if (IsConstructor(C) === Value.true) {
A = Q(Construct(C, [len]));
} else {
A = Q(ArrayCreate(len));
}
let k = 0;
while (k < len.numberValue()) {
const Pk = X(ToString(new Value(k)));
const kValue = Q(Get(arrayLike, Pk));
let mappedValue;
if (mapping === true) {
mappedValue = Q(Call(mapfn, thisArg, [kValue, new Value(k)]));
} else {
mappedValue = kValue;
}
Q(CreateDataPropertyOrThrow(A, Pk, mappedValue));
k += 1;
}
Q(Set(A, new Value('length'), len, Value.true));
return A;
}
// 22.1.2.2 #sec-array.isarray
function Array_isArray([arg = Value.undefined]) {
return Q(IsArray(arg));
}
// 22.1.2.3 #sec-array.of
function Array_of(items, { thisValue }) {
const len = items.length;
// Let items be the List of arguments passed to this function.
const C = thisValue;
let A;
if (IsConstructor(C) === Value.true) {
A = Q(Construct(C, [new Value(len)]));
} else {
A = Q(ArrayCreate(new Value(len)));
}
let k = 0;
while (k < len) {
const kValue = items[k];
const Pk = X(ToString(new Value(k)));
Q(CreateDataPropertyOrThrow(A, Pk, kValue));
k += 1;
}
Q(Set(A, new Value('length'), new Value(len), Value.true));
return A;
}
// 22.1.2.5 #sec-get-array-@@species
function Array_speciesGetter(args, { thisValue }) {
return thisValue;
}
export function BootstrapArray(realmRec) {
const proto = realmRec.Intrinsics['%Array.prototype%'];
const cons = BootstrapConstructor(realmRec, ArrayConstructor, 'Array', 1, proto, [
['from', Array_from, 1],
['isArray', Array_isArray, 1],
['of', Array_of, 0],
[wellKnownSymbols.species, [Array_speciesGetter]],
]);
realmRec.Intrinsics['%Array%'] = cons;
}
-44
View File
@@ -1,44 +0,0 @@
import { surroundingAgent } from '../engine.mjs';
import { Type, Value, wellKnownSymbols } from '../value.mjs';
import { Q } from '../completion.mjs';
import { ToIndex, AllocateArrayBuffer } from '../abstract-ops/all.mjs';
import { BootstrapConstructor } from './Bootstrap.mjs';
// #sec-arraybuffer-length
function ArrayBufferConstructor([length = Value.undefined], { NewTarget }) {
// 1. If NewTarget is undefined, throw a TypeError exception.
if (NewTarget === Value.undefined) {
return surroundingAgent.Throw('TypeError', 'ConstructorNonCallable', this);
}
// 2. Let byteLength be ? ToIndex(length).
const byteLength = Q(ToIndex(length));
// 3. Return ? AllocateArrayBuffer(NewTarget, byteLength).
return Q(AllocateArrayBuffer(NewTarget, byteLength));
}
// #sec-arraybuffer.isview
function ArrayBuffer_isView([arg = Value.undefined]) {
// 1. If Type(arg) is not Object, return false.
if (Type(arg) !== 'Object') {
return Value.false;
}
// 2. If arg has a [[ViewedArrayBuffer]] internal slot, return true.
if ('ViewedArrayBuffer' in arg) {
return Value.true;
}
// 3. Return false.
return Value.false;
}
// #sec-get-arraybuffer-@@species
function ArrayBuffer_species(a, { thisValue }) {
return thisValue;
}
export function BootstrapArrayBuffer(realmRec) {
const c = BootstrapConstructor(realmRec, ArrayBufferConstructor, 'ArrayBuffer', 1, realmRec.Intrinsics['%ArrayBuffer.prototype%'], [
['isView', ArrayBuffer_isView, 1],
[wellKnownSymbols.species, [ArrayBuffer_species]],
]);
realmRec.Intrinsics['%ArrayBuffer%'] = c;
}
@@ -1,115 +0,0 @@
import { surroundingAgent } from '../engine.mjs';
import { Value } from '../value.mjs';
import { Q } from '../completion.mjs';
import {
RequireInternalSlot, IsDetachedBuffer, IsSharedArrayBuffer,
SpeciesConstructor, Construct, ToInteger, SameValue, CopyDataBlockBytes,
} from '../abstract-ops/all.mjs';
import { BootstrapPrototype } from './Bootstrap.mjs';
// #sec-get-arraybuffer.prototype.bytelength
function ArrayBufferProto_byteLength(args, { thisValue }) {
// 1. Let O be this value.
const O = thisValue;
// 2. Perform ? RequireInternalSlot(O, [[ArrayBufferData]]).
Q(RequireInternalSlot(O, 'ArrayBufferData'));
// 3. If IsSharedArrayBuffer(O) is true, throw a TypeError exception.
if (IsSharedArrayBuffer(O) === Value.true) {
return surroundingAgent.Throw('TypeError', 'ArrayBufferShared');
}
// 4. If IsDetachedBuffer(O) is true, throw a TypeError exception.
if (IsDetachedBuffer(O) === Value.true) {
return surroundingAgent.Throw('TypeError', 'ArrayBufferDetached');
}
// 5. Let length be O.[[ArrayBufferByteLength]].
const length = O.ArrayBufferByteLength;
// 6. Return length.
return length;
}
// #sec-arraybuffer.prototype.slice
function ArrayBufferProto_slice([start = Value.undefined, end = Value.undefined], { thisValue }) {
// 1. Let O be the this value.
const O = thisValue;
// 2. Perform ? RequireInternalSlot(O, [[ArrayBufferData]]).
Q(RequireInternalSlot(O, 'ArrayBufferData'));
// 3. If IsSharedArrayBuffer(O) is true, throw a TypeError exception.
if (IsSharedArrayBuffer(O) === Value.true) {
return surroundingAgent.Throw('TypeError', 'ArrayBufferShared');
}
// 4. If IsDetachedBuffer(O) is true, throw a TypeError exception.
if (IsDetachedBuffer(O) === Value.true) {
return surroundingAgent.Throw('TypeError', 'ArrayBufferDetached');
}
// 5. Let len be O.[[ArrayBufferByteLength]].
const len = O.ArrayBufferByteLength.numberValue();
// 6. Let relativeStart be ? ToInteger(start).
const relativeStart = Q(ToInteger(start)).numberValue();
let first;
// 7. If relativeStart < 0, let first be max((len + relativeStart), 0); else let first be min(relativeStart, len).
if (relativeStart < 0) {
first = Math.max(len + relativeStart, 0);
} else {
first = Math.min(relativeStart, len);
}
let relativeEnd;
// 8. If end is undefined, let relativeEnd be len; else let relativeEnd be ? ToInteger(end).
if (end === Value.undefined) {
relativeEnd = len;
} else {
relativeEnd = Q(ToInteger(end)).numberValue();
}
let final;
// 9. If relativeEnd < 0, let final be max((len + relativeEnd), 0); else let final be min(relativeEnd, len).
if (relativeEnd < 0) {
final = Math.max(len + relativeEnd, 0);
} else {
final = Math.min(relativeEnd, len);
}
// 10. Let newLen be max(final - first, 0).
const newLen = Math.max(final - first, 0);
// 11. Let ctor be ? SpeciesConstructor(O, %ArrayBuffer%).
const ctor = Q(SpeciesConstructor(O, surroundingAgent.intrinsic('%ArrayBuffer%')));
// 12. Let new be ? Construct(ctor, « newLen »).
const newO = Q(Construct(ctor, [new Value(newLen)]));
// 13. Perform ? RequireInternalSlot(new, [[ArrayBufferData]]).
Q(RequireInternalSlot(newO, 'ArrayBufferData'));
// 14. If IsSharedArrayBuffer(new) is true, throw a TypeError exception.
if (IsSharedArrayBuffer(newO) === Value.true) {
return surroundingAgent.Throw('TypeError', 'ArrayBufferShared');
}
// 15. If IsDetachedBuffer(new) is true, throw a TypeError exception.
if (IsDetachedBuffer(newO) === Value.true) {
return surroundingAgent.Throe('TypeError', 'ArrayBufferDetached');
}
// 16. If SameValue(new, O) is true, throw a TypeError exception.
if (SameValue(newO, O) === Value.true) {
return surroundingAgent.Throw('TypeError', 'SubclassSameValue', newO);
}
// 17. If new.[[ArrayBufferByteLength]] < newLen, throw a TypeError exception.
if (newO.ArrayBufferByteLength.numberValue() < newLen) {
return surroundingAgent.Throw('TypeError', 'SubclassLengthTooSmall', newO);
}
// 18. NOTE: Side-effects of the above steps may have detached O.
// 19. If IsDetachedBuffer(O) is true, throw a TypeError exception.
if (IsDetachedBuffer(O) === Value.true) {
return surroundingAgent.Throw('TypeError', 'ArrayBufferDetached');
}
// 20. Let fromBuf be O.[[ArrayBufferData]].
const fromBuf = O.ArrayBufferData;
// 21. Let toBuf be new.[[ArrayBufferData]].
const toBuf = newO.ArrayBufferData;
// 22. Perform CopyDataBlockBytes(toBuf, 0, fromBuf, first, newLen).
CopyDataBlockBytes(toBuf, 0, fromBuf, first, newLen);
// 23. Return new.
return newO;
}
export function BootstrapArrayBufferPrototype(realmRec) {
const proto = BootstrapPrototype(realmRec, [
['byteLength', [ArrayBufferProto_byteLength]],
['slice', ArrayBufferProto_slice, 2],
], realmRec.Intrinsics['%Object.prototype%'], 'ArrayBuffer');
realmRec.Intrinsics['%ArrayBuffer.prototype%'] = proto;
}
@@ -1,95 +0,0 @@
import {
surroundingAgent,
} from '../engine.mjs';
import {
Type,
Value,
} from '../value.mjs';
import {
Assert,
CreateArrayFromList,
CreateIterResultObject,
Get,
IsDetachedBuffer,
LengthOfArrayLike,
ToString,
} from '../abstract-ops/all.mjs';
import { Q, X } from '../completion.mjs';
import { BootstrapPrototype } from './Bootstrap.mjs';
// #sec-%arrayiteratorprototype%-object
function ArrayIteratorPrototype_next(args, { thisValue }) {
// 1. Let O be the this value.
const O = thisValue;
// 2. If Type(O) is not Object, throw a TypeError exception.
if (Type(O) !== 'Object') {
return surroundingAgent.Throw('TypeError', 'NotATypeObject', 'Array Iterator', O);
}
// 3. If O does not have all of the internal slots of an Array Iterator Instance (22.1.5.3), throw a TypeError exception.
if (!('IteratedArrayLike' in O)
|| !('ArrayLikeNextIndex' in O)
|| !('ArrayLikeIterationKind' in O)) {
return surroundingAgent.Throw('TypeError', 'NotATypeObject', 'Array Iterator', O);
}
// 4. Let a be O.[[IteratedArrayLike]].
const a = O.IteratedArrayLike;
// 5. If a is undefined, return CreateIterResultObject(undefined, true).
if (a === Value.undefined) {
return CreateIterResultObject(Value.undefined, Value.true);
}
// 6. Let index be O.[[ArrayLikeNextIndex]].
const index = O.ArrayLikeNextIndex;
// 7. Let itemKind be O.[[ArrayLikeIterationKind]].
const itemKind = O.ArrayLikeIterationKind;
let len;
// 8. If a has a [[TypedArrayName]] internal slot, then
if ('TypedArrayName' in a) {
// a. If IsDetachedBuffer(a.[[ViewedArrayBuffer]]) is true, throw a TypeError exception.
if (IsDetachedBuffer(a.ViewedArrayBuffer) === Value.true) {
return surroundingAgent.Throw('TypeError', 'ArrayBufferDetached');
}
// b. Let len be a.[[ArrayLength]].
len = a.ArrayLength;
} else { // 9. Else,
// a. Let len be ? LengthOfArrayLike(a).
len = Q(LengthOfArrayLike(a));
}
// 10. If index ≥ len, then
if (index >= len.numberValue()) {
// a. Set O.[[IteratedArrayLike]] to undefined.
O.IteratedArrayLike = Value.undefined;
// b. Return CreateIterResultObject(undefined, true).
return CreateIterResultObject(Value.undefined, Value.true);
}
// 11. Set O.[[ArrayLikeNextIndex]] to index + 1.
O.ArrayLikeNextIndex = index + 1;
// 12. If itemKind is key, return CreateIterResultObject(index, false).
if (itemKind === 'key') {
return CreateIterResultObject(new Value(index), Value.false);
}
// 13. Let elementKey be ! ToString(index).
const elementKey = X(ToString(new Value(index)));
// 14. Let elementValue be ? Get(a, elementKey).
const elementValue = Q(Get(a, elementKey));
// 15. If itemKind is value, let result be elementValue.
let result;
// 15. If itemKind is value, let result be elementValue.
if (itemKind === 'value') {
result = elementValue;
} else { // 16. Else,
// a. Assert: itemKind is key+value.
Assert(itemKind === 'key+value');
// b. Let result be ! CreateArrayFromList(« index, elementValue »).
result = X(CreateArrayFromList([new Value(index), elementValue]));
}
// 17. Return CreateIterResultObject(result, false).
return CreateIterResultObject(result, Value.false);
}
export function BootstrapArrayIteratorPrototype(realmRec) {
const proto = BootstrapPrototype(realmRec, [
['next', ArrayIteratorPrototype_next, 0],
], realmRec.Intrinsics['%IteratorPrototype%'], 'Array Iterator');
realmRec.Intrinsics['%ArrayIterator.prototype%'] = proto;
}
-591
View File
@@ -1,591 +0,0 @@
import { surroundingAgent } from '../engine.mjs';
import {
Descriptor,
Type,
Value,
wellKnownSymbols,
} from '../value.mjs';
import {
ArrayCreate,
ArraySpeciesCreate,
Assert,
Call,
CreateArrayIterator,
CreateDataProperty,
CreateDataPropertyOrThrow,
DeletePropertyOrThrow,
Get,
HasProperty,
IsArray,
IsCallable,
IsConcatSpreadable,
Set,
SortCompare,
LengthOfArrayLike,
OrdinaryObjectCreate,
ToBoolean,
ToInteger,
ToObject,
ToString,
} from '../abstract-ops/all.mjs';
import { Q, X } from '../completion.mjs';
import { assignProps } from './Bootstrap.mjs';
import { ArrayProto_sortBody, BootstrapArrayPrototypeShared } from './ArrayPrototypeShared.mjs';
// 22.1.3.1 #sec-array.prototype.concat
function ArrayProto_concat(args, { thisValue }) {
const O = Q(ToObject(thisValue));
const A = Q(ArraySpeciesCreate(O, new Value(0)));
let n = 0;
const items = [O, ...args];
while (items.length > 0) {
const E = items.shift();
const spreadable = Q(IsConcatSpreadable(E));
if (spreadable === Value.true) {
let k = 0;
const len = Q(LengthOfArrayLike(E)).numberValue();
if (n + len > (2 ** 53) - 1) {
return surroundingAgent.Throw('TypeError', 'ArrayPastSafeLength');
}
while (k < len) {
const P = X(ToString(new Value(k)));
const exists = Q(HasProperty(E, P));
if (exists === Value.true) {
const subElement = Q(Get(E, P));
const nStr = X(ToString(new Value(n)));
Q(CreateDataPropertyOrThrow(A, nStr, subElement));
}
n += 1;
k += 1;
}
} else {
if (n >= (2 ** 53) - 1) {
return surroundingAgent.Throw('TypeError', 'ArrayPastSafeLength');
}
const nStr = X(ToString(new Value(n)));
Q(CreateDataPropertyOrThrow(A, nStr, E));
n += 1;
}
}
Q(Set(A, new Value('length'), new Value(n), Value.true));
return A;
}
// 22.1.3.3 #sec-array.prototype.copywithin
function ArrayProto_copyWithin([target = Value.undefined, start = Value.undefined, end = Value.undefined], { thisValue }) {
const O = Q(ToObject(thisValue));
const len = Q(LengthOfArrayLike(O));
const relativeTarget = Q(ToInteger(target));
let to;
if (relativeTarget.numberValue() < 0) {
to = Math.max(len.numberValue() + relativeTarget.numberValue(), 0);
} else {
to = Math.min(relativeTarget.numberValue(), len.numberValue());
}
const relativeStart = Q(ToInteger(start));
let from;
if (relativeStart.numberValue() < 0) {
from = Math.max(len.numberValue() + relativeStart.numberValue(), 0);
} else {
from = Math.min(relativeStart.numberValue(), len.numberValue());
}
let relativeEnd;
if (end === Value.undefined) {
relativeEnd = len;
} else {
relativeEnd = Q(ToInteger(end));
}
let final;
if (relativeEnd.numberValue() < 0) {
final = Math.max(len.numberValue() + relativeEnd.numberValue(), 0);
} else {
final = Math.min(relativeEnd.numberValue(), len.numberValue());
}
let count = Math.min(final - from, len.numberValue() - to);
let direction;
if (from < to && to < from + count) {
direction = -1;
from += count - 1;
to += count - 1;
} else {
direction = 1;
}
while (count > 0) {
const fromKey = X(ToString(new Value(from)));
const toKey = X(ToString(new Value(to)));
const fromPresent = Q(HasProperty(O, fromKey));
if (fromPresent === Value.true) {
const fromVal = Q(Get(O, fromKey));
Q(Set(O, toKey, fromVal, Value.true));
} else {
Q(DeletePropertyOrThrow(O, toKey));
}
from += direction;
to += direction;
count -= 1;
}
return O;
}
// 22.1.3.4 #sec-array.prototype.entries
function ArrayProto_entries(args, { thisValue }) {
const O = Q(ToObject(thisValue));
return CreateArrayIterator(O, 'key+value');
}
// 22.1.3.6 #sec-array.prototype.fill
function ArrayProto_fill([value = Value.undefined, start = Value.undefined, end = Value.undefined], { thisValue }) {
const O = Q(ToObject(thisValue));
const len = Q(LengthOfArrayLike(O)).numberValue();
const relativeStart = Q(ToInteger(start)).numberValue();
let k;
if (relativeStart < 0) {
k = Math.max(len + relativeStart, 0);
} else {
k = Math.min(relativeStart, len);
}
let relativeEnd;
if (Type(end) === 'Undefined') {
relativeEnd = len;
} else {
relativeEnd = Q(ToInteger(end)).numberValue();
}
let final;
if (relativeEnd < 0) {
final = Math.max(len + relativeEnd, 0);
} else {
final = Math.min(relativeEnd, len);
}
while (k < final) {
const Pk = X(ToString(new Value(k)));
Q(Set(O, Pk, value, Value.true));
k += 1;
}
return O;
}
// 22.1.3.7 #sec-array.prototype.filter
function ArrayProto_filter([callbackfn = Value.undefined, thisArg = Value.undefined], { thisValue }) {
const O = Q(ToObject(thisValue));
const len = Q(LengthOfArrayLike(O)).numberValue();
if (IsCallable(callbackfn) === Value.false) {
return surroundingAgent.Throw('TypeError', 'NotAFunction', callbackfn);
}
const A = Q(ArraySpeciesCreate(O, new Value(0)));
let k = 0;
let to = 0;
while (k < len) {
const Pk = X(ToString(new Value(k)));
const kPresent = Q(HasProperty(O, Pk));
if (kPresent === Value.true) {
const kValue = Q(Get(O, Pk));
const selected = ToBoolean(Q(Call(callbackfn, thisArg, [kValue, new Value(k), O])));
if (selected === Value.true) {
Q(CreateDataPropertyOrThrow(A, ToString(new Value(to)), kValue));
to += 1;
}
}
k += 1;
}
return A;
}
// 22.1.3.10.1 #sec-flattenintoarray
function FlattenIntoArray(target, source, sourceLen, start, depth, mapperFunction, thisArg) {
Assert(Type(target) === 'Object');
Assert(Type(source) === 'Object');
Assert(sourceLen >= 0);
Assert(start >= 0);
// Assert: _depth_ is an integer Number, *+&infin;*, or *-&infin;*.
// Assert(mapperFunction === undefined || (X(IsCallable(mapperFunction)) === Value.true && thisArg !== undefined && depth === 1));
let targetIndex = start;
let sourceIndex = 0;
while (sourceIndex < sourceLen) {
const P = X(ToString(new Value(sourceIndex)));
const exists = Q(HasProperty(source, P));
if (exists === Value.true) {
let element = Q(Get(source, P));
if (mapperFunction) {
Assert(thisArg);
element = Q(Call(mapperFunction, thisArg, [element, new Value(sourceIndex), source]));
}
let shouldFlatten = Value.false;
if (depth > 0) {
shouldFlatten = Q(IsArray(element));
}
if (shouldFlatten === Value.true) {
const elementLen = Q(LengthOfArrayLike(element)).numberValue();
targetIndex = Q(FlattenIntoArray(target, element, elementLen, targetIndex, depth - 1));
} else {
if (targetIndex >= (2 ** 53) - 1) {
return surroundingAgent.Throw('TypeError', 'OutOfRange', targetIndex);
}
Q(CreateDataPropertyOrThrow(target, X(ToString(new Value(targetIndex))), element));
targetIndex += 1;
}
}
sourceIndex += 1;
}
return targetIndex;
}
// 22.1.3.10 #sec-array.prototype.flat
function ArrayProto_flat([depth = Value.undefined], { thisValue }) {
const O = Q(ToObject(thisValue));
const sourceLen = Q(LengthOfArrayLike(O)).numberValue();
let depthNum = 1;
if (depth !== Value.undefined) {
depthNum = Q(ToInteger(depth)).numberValue();
}
const A = Q(ArraySpeciesCreate(O, new Value(0)));
Q(FlattenIntoArray(A, O, sourceLen, 0, depthNum));
return A;
}
// 22.1.3.11 #sec-array.prototype.flatmap
function ArrayProto_flatMap([mapperFunction = Value.undefined, thisArg = Value.undefined], { thisValue }) {
const O = Q(ToObject(thisValue));
const sourceLen = Q(LengthOfArrayLike(O)).numberValue();
if (X(IsCallable(mapperFunction)) === Value.false) {
return surroundingAgent.Throw('TypeError', 'NotAFunction', mapperFunction);
}
const A = Q(ArraySpeciesCreate(O, new Value(0)));
Q(FlattenIntoArray(A, O, sourceLen, 0, 1, mapperFunction, thisArg));
return A;
}
// 22.1.3.16 #sec-array.prototype.keys
function ArrayProto_keys(args, { thisValue }) {
const O = Q(ToObject(thisValue));
return CreateArrayIterator(O, 'key');
}
// 22.1.3.18 #sec-array.prototype.map
function ArrayProto_map([callbackfn = Value.undefined, thisArg = Value.undefined], { thisValue }) {
const O = Q(ToObject(thisValue));
const len = Q(LengthOfArrayLike(O));
if (IsCallable(callbackfn) === Value.false) {
return surroundingAgent.Throw('TypeError', 'NotAFunction', callbackfn);
}
const A = Q(ArraySpeciesCreate(O, len));
let k = 0;
while (k < len.numberValue()) {
const Pk = X(ToString(new Value(k)));
const kPresent = Q(HasProperty(O, Pk));
if (kPresent === Value.true) {
const kValue = Q(Get(O, Pk));
const mappedValue = Q(Call(callbackfn, thisArg, [kValue, new Value(k), O]));
Q(CreateDataPropertyOrThrow(A, Pk, mappedValue));
}
k += 1;
}
return A;
}
// 22.1.3.19 #sec-array.prototype.pop
function ArrayProto_pop(args, { thisValue }) {
const O = Q(ToObject(thisValue));
const len = Q(LengthOfArrayLike(O)).numberValue();
if (len === 0) {
Q(Set(O, new Value('length'), new Value(0), Value.true));
return Value.undefined;
} else {
const newLen = len - 1;
const index = Q(ToString(new Value(newLen)));
const element = Q(Get(O, index));
Q(DeletePropertyOrThrow(O, index));
Q(Set(O, new Value('length'), new Value(newLen), Value.true));
return element;
}
}
// 22.1.3.20 #sec-array.prototype.push
function ArrayProto_push(items, { thisValue }) {
const O = Q(ToObject(thisValue));
let len = Q(LengthOfArrayLike(O)).numberValue();
const argCount = items.length;
if (len + argCount > (2 ** 53) - 1) {
return surroundingAgent.Throw('TypeError', 'ArrayPastSafeLength');
}
while (items.length > 0) {
const E = items.shift();
Q(Set(O, X(ToString(new Value(len))), E, Value.true));
len += 1;
}
Q(Set(O, new Value('length'), new Value(len), Value.true));
return new Value(len);
}
// 22.1.3.24 #sec-array.prototype.shift
function ArrayProto_shift(args, { thisValue }) {
const O = Q(ToObject(thisValue));
const len = Q(LengthOfArrayLike(O)).numberValue();
if (len === 0) {
Q(Set(O, new Value('length'), new Value(0), Value.true));
return Value.undefined;
}
const first = Q(Get(O, new Value('0')));
let k = 1;
while (k < len) {
const from = X(ToString(new Value(k)));
const to = X(ToString(new Value(k - 1)));
const fromPresent = Q(HasProperty(O, from));
if (fromPresent === Value.true) {
const fromVal = Q(Get(O, from));
Q(Set(O, to, fromVal, Value.true));
} else {
Q(DeletePropertyOrThrow(O, to));
}
k += 1;
}
Q(DeletePropertyOrThrow(O, X(ToString(new Value(len - 1)))));
Q(Set(O, new Value('length'), new Value(len - 1), Value.true));
return first;
}
// 22.1.3.25 #sec-array.prototype.slice
function ArrayProto_slice([start = Value.undefined, end = Value.undefined], { thisValue }) {
const O = Q(ToObject(thisValue));
const len = Q(LengthOfArrayLike(O)).numberValue();
const relativeStart = Q(ToInteger(start)).numberValue();
let k;
if (relativeStart < 0) {
k = Math.max(len + relativeStart, 0);
} else {
k = Math.min(relativeStart, len);
}
let relativeEnd;
if (Type(end) === 'Undefined') {
relativeEnd = len;
} else {
relativeEnd = Q(ToInteger(end)).numberValue();
}
let final;
if (relativeEnd < 0) {
final = Math.max(len + relativeEnd, 0);
} else {
final = Math.min(relativeEnd, len);
}
const count = Math.max(final - k, 0);
const A = Q(ArraySpeciesCreate(O, new Value(count)));
let n = 0;
while (k < final) {
const Pk = X(ToString(new Value(k)));
const kPresent = Q(HasProperty(O, Pk));
if (kPresent === Value.true) {
const kValue = Q(Get(O, Pk));
const nStr = X(ToString(new Value(n)));
Q(CreateDataPropertyOrThrow(A, nStr, kValue));
}
k += 1;
n += 1;
}
Q(Set(A, new Value('length'), new Value(n), Value.true));
return A;
}
// 22.1.3.27 #sec-array.prototype.sort
function ArrayProto_sort([comparefn = Value.undefined], { thisValue }) {
if (comparefn !== Value.undefined && IsCallable(comparefn) === Value.false) {
return surroundingAgent.Throw('TypeError', 'NotAFunction', comparefn);
}
const obj = Q(ToObject(thisValue));
const len = Q(LengthOfArrayLike(obj));
return ArrayProto_sortBody(obj, len, (x, y) => SortCompare(x, y, comparefn));
}
// 22.1.3.28 #sec-array.prototype.splice
function ArrayProto_splice(args, { thisValue }) {
const [start = Value.undefined, deleteCount = Value.undefined, ...items] = args;
const O = Q(ToObject(thisValue));
const len = Q(LengthOfArrayLike(O)).numberValue();
const relativeStart = Q(ToInteger(start)).numberValue();
let actualStart;
if (relativeStart < 0) {
actualStart = Math.max(len + relativeStart, 0);
} else {
actualStart = Math.min(relativeStart, len);
}
let insertCount;
let actualDeleteCount;
if (args.length === 0) {
insertCount = 0;
actualDeleteCount = 0;
} else if (args.length === 1) {
insertCount = 0;
actualDeleteCount = len - actualStart;
} else {
insertCount = args.length - 2;
const dc = Q(ToInteger(deleteCount)).numberValue();
actualDeleteCount = Math.min(Math.max(dc, 0), len - actualStart);
}
if (len + insertCount - actualDeleteCount > (2 ** 53) - 1) {
return surroundingAgent.Throw('TypeError', 'ArrayPastSafeLength');
}
const A = Q(ArraySpeciesCreate(O, new Value(actualDeleteCount)));
let k = 0;
while (k < actualDeleteCount) {
const from = X(ToString(new Value(actualStart + k)));
const fromPresent = Q(HasProperty(O, from));
if (fromPresent === Value.true) {
const fromValue = Q(Get(O, from));
Q(CreateDataPropertyOrThrow(A, X(ToString(new Value(k))), fromValue));
}
k += 1;
}
Q(Set(A, new Value('length'), new Value(actualDeleteCount), Value.true));
const itemCount = items.length;
if (itemCount < actualDeleteCount) {
k = actualStart;
while (k < len - actualDeleteCount) {
const from = X(ToString(new Value(k + actualDeleteCount)));
const to = X(ToString(new Value(k + itemCount)));
const fromPresent = Q(HasProperty(O, from));
if (fromPresent === Value.true) {
const fromValue = Q(Get(O, from));
Q(Set(O, to, fromValue, Value.true));
} else {
Q(DeletePropertyOrThrow(O, to));
}
k += 1;
}
k = len;
while (k > len - actualDeleteCount + itemCount) {
Q(DeletePropertyOrThrow(O, X(ToString(new Value(k - 1)))));
k -= 1;
}
} else if (itemCount > actualDeleteCount) {
k = len - actualDeleteCount;
while (k > actualStart) {
const from = X(ToString(new Value(k + actualDeleteCount - 1)));
const to = X(ToString(new Value(k + itemCount - 1)));
const fromPresent = Q(HasProperty(O, from));
if (fromPresent === Value.true) {
const fromValue = Q(Get(O, from));
Q(Set(O, to, fromValue, Value.true));
} else {
Q(DeletePropertyOrThrow(O, to));
}
k -= 1;
}
}
k = actualStart;
while (items.length > 0) {
const E = items.shift();
Q(Set(O, X(ToString(new Value(k))), E, Value.true));
k += 1;
}
Q(Set(O, new Value('length'), new Value(len - actualDeleteCount + itemCount), Value.true));
return A;
}
// 22.1.3.30 #sec-array.prototype.tostring
function ArrayProto_toString(a, { thisValue }) {
const array = Q(ToObject(thisValue));
let func = Q(Get(array, new Value('join')));
if (IsCallable(func) === Value.false) {
func = surroundingAgent.intrinsic('%Object.prototype.toString%');
}
return Q(Call(func, array));
}
// 22.1.3.31 #sec-array.prototype.unshift
function ArrayProto_unshift(args, { thisValue }) {
const O = Q(ToObject(thisValue));
const len = Q(LengthOfArrayLike(O)).numberValue();
const argCount = args.length;
if (argCount > 0) {
if (len + argCount > (2 ** 53) - 1) {
return surroundingAgent.Throw('TypeError', 'ArrayPastSafeLength');
}
let k = len;
while (k > 0) {
const from = X(ToString(new Value(k - 1)));
const to = X(ToString(new Value(k + argCount - 1)));
const fromPresent = Q(HasProperty(O, from));
if (fromPresent === Value.true) {
const fromValue = Q(Get(O, from));
Q(Set(O, to, fromValue, Value.true));
} else {
Q(DeletePropertyOrThrow(O, to));
}
k -= 1;
}
let j = 0;
const items = args;
while (items.length !== 0) {
const E = items.shift();
const jStr = X(ToString(new Value(j)));
Q(Set(O, jStr, E, Value.true));
j += 1;
}
}
Q(Set(O, new Value('length'), new Value(len + argCount), Value.true));
return new Value(len + argCount);
}
// 22.1.3.32 #sec-array.prototype.values
function ArrayProto_values(args, { thisValue }) {
const O = Q(ToObject(thisValue));
return CreateArrayIterator(O, 'value');
}
export function BootstrapArrayPrototype(realmRec) {
const proto = X(ArrayCreate(new Value(0), realmRec.Intrinsics['%Object.prototype%']));
assignProps(realmRec, proto, [
['concat', ArrayProto_concat, 1],
['copyWithin', ArrayProto_copyWithin, 2],
['entries', ArrayProto_entries, 0],
['fill', ArrayProto_fill, 1],
['filter', ArrayProto_filter, 1],
['flat', ArrayProto_flat, 0],
['flatMap', ArrayProto_flatMap, 1],
['keys', ArrayProto_keys, 0],
['map', ArrayProto_map, 1],
['pop', ArrayProto_pop, 0],
['push', ArrayProto_push, 1],
['shift', ArrayProto_shift, 0],
['slice', ArrayProto_slice, 2],
['sort', ArrayProto_sort, 1],
['splice', ArrayProto_splice, 2],
['toString', ArrayProto_toString, 0],
['unshift', ArrayProto_unshift, 1],
['values', ArrayProto_values, 0],
]);
BootstrapArrayPrototypeShared(
realmRec,
proto,
() => {},
(O) => Get(O, new Value('length')),
);
proto.DefineOwnProperty(wellKnownSymbols.iterator, proto.GetOwnProperty(new Value('values')));
{
const unscopableList = OrdinaryObjectCreate(Value.null);
Assert(X(CreateDataProperty(unscopableList, new Value('copyWithin'), Value.true)) === Value.true);
Assert(X(CreateDataProperty(unscopableList, new Value('entries'), Value.true)) === Value.true);
Assert(X(CreateDataProperty(unscopableList, new Value('fill'), Value.true)) === Value.true);
Assert(X(CreateDataProperty(unscopableList, new Value('find'), Value.true)) === Value.true);
Assert(X(CreateDataProperty(unscopableList, new Value('findIndex'), Value.true)) === Value.true);
Assert(X(CreateDataProperty(unscopableList, new Value('flat'), Value.true)) === Value.true);
Assert(X(CreateDataProperty(unscopableList, new Value('flatMap'), Value.true)) === Value.true);
Assert(X(CreateDataProperty(unscopableList, new Value('includes'), Value.true)) === Value.true);
Assert(X(CreateDataProperty(unscopableList, new Value('keys'), Value.true)) === Value.true);
Assert(X(CreateDataProperty(unscopableList, new Value('values'), Value.true)) === Value.true);
X(proto.DefineOwnProperty(wellKnownSymbols.unscopables, Descriptor({
Value: unscopableList,
Writable: Value.false,
Enumerable: Value.false,
Configurable: Value.true,
})));
}
// Used in `arguments` objects.
realmRec.Intrinsics['%Array.prototype.values%'] = X(Get(proto, new Value('values')));
realmRec.Intrinsics['%Array.prototype%'] = proto;
}
@@ -1,561 +0,0 @@
import {
Assert,
Call,
DeletePropertyOrThrow,
Get,
HasOwnProperty,
HasProperty,
Invoke,
IsCallable,
SameValueZero,
Set,
StrictEqualityComparison,
ToBoolean,
ToInteger,
ToLength,
ToObject,
ToString,
} from '../abstract-ops/all.mjs';
import { Q, X } from '../completion.mjs';
import { surroundingAgent } from '../engine.mjs';
import { Type, Value } from '../value.mjs';
import { assignProps } from './Bootstrap.mjs';
// Algorithms and methods shared between %Array.prototype% and
// %TypedArray.prototype%.
// 22.1.3.27 #sec-array.prototype.sort
// 22.2.3.26 #sec-%typedarray%.prototype.sort
//
// If internalMethodsRestricted is true, then Asserts are used to ensure that
// "The only internal methods of the this object that the algorithm may call
// are [[Get]] and [[Set]]," a requirement of %TypedArray%.prototype.sort.
export function ArrayProto_sortBody(obj, len, SortCompare, internalMethodsRestricted = false) {
len = len.numberValue();
// Collect all elements. Count how many holes we have for error checking.
const collected = [];
let holes = 0;
for (let k = 0; k < len; k += 1) {
const curProp = X(ToString(new Value(k)));
const prop = Q(Get(obj, curProp));
if (prop === Value.undefined) {
Assert(!internalMethodsRestricted);
const hasOwn = Q(HasOwnProperty(obj, curProp));
if (hasOwn === Value.false) {
holes += 1;
} else {
collected.push(prop);
}
} else {
collected.push(prop);
}
}
if (internalMethodsRestricted) {
Assert(holes === 0);
}
Assert(collected.length + holes === len);
// Get rid of holes by deleting properties at the end.
// See Note 1: Because non-existent property values always compare greater
// than undefined property values, and undefined always compares greater
// than any other value, undefined property values always sort to the end
// of the result, followed by non-existent property values.
for (let k = collected.length; k < len; k += 1) {
const curProp = X(ToString(new Value(k)));
Q(DeletePropertyOrThrow(obj, curProp));
}
// Mergesort.
const lBuffer = [];
const rBuffer = [];
for (let step = 1; step < collected.length; step *= 2) {
for (let start = 0; start < collected.length - 1; start += 2 * step) {
const sizeLeft = step;
const mid = start + sizeLeft;
const sizeRight = Math.min(step, collected.length - mid);
if (sizeRight < 0) {
continue;
}
// Merge.
for (let l = 0; l < sizeLeft; l += 1) {
lBuffer[l] = collected[start + l];
}
for (let r = 0; r < sizeRight; r += 1) {
rBuffer[r] = collected[mid + r];
}
{
let l = 0;
let r = 0;
let o = start;
while (l < sizeLeft && r < sizeRight) {
const cmp = Q(SortCompare(lBuffer[l], rBuffer[r])).numberValue();
if (cmp <= 0) {
collected[o] = lBuffer[l];
o += 1;
l += 1;
} else {
collected[o] = rBuffer[r];
o += 1;
r += 1;
}
}
while (l < sizeLeft) {
collected[o] = lBuffer[l];
o += 1;
l += 1;
}
while (r < sizeRight) {
collected[o] = rBuffer[r];
o += 1;
r += 1;
}
}
}
}
// Copy the sorted results back to the array.
for (let k = 0; k < collected.length; k += 1) {
const curProp = X(ToString(new Value(k)));
Q(Set(obj, curProp, collected[k], Value.true));
}
return obj;
}
export function BootstrapArrayPrototypeShared(realmRec, proto, priorToEvaluatingAlgorithm, objectToLength) {
// 22.1.3.5 #sec-array.prototype.every
// 22.2.3.7 #sec-%typedarray%.prototype.every
function ArrayProto_every([callbackFn = Value.undefined, thisArg = Value.undefined], { thisValue }) {
Q(priorToEvaluatingAlgorithm(thisValue));
const O = Q(ToObject(thisValue));
const lenProp = Q(objectToLength(O));
const len = Q(ToLength(lenProp));
if (IsCallable(callbackFn) === Value.false) {
return surroundingAgent.Throw('TypeError', 'NotAFunction', callbackFn);
}
let k = 0;
while (k < len.numberValue()) {
const Pk = X(ToString(new Value(k)));
const kPresent = Q(HasProperty(O, Pk));
if (kPresent === Value.true) {
const kValue = Q(Get(O, Pk));
const testResult = ToBoolean(Q(Call(callbackFn, thisArg, [kValue, new Value(k), O])));
if (testResult === Value.false) {
return Value.false;
}
}
k += 1;
}
return Value.true;
}
// 22.1.3.8 #sec-array.prototype.find
// 22.2.3.10 #sec-%typedarray%.prototype.find
function ArrayProto_find([predicate = Value.undefined, thisArg = Value.undefined], { thisValue }) {
Q(priorToEvaluatingAlgorithm(thisValue));
const O = Q(ToObject(thisValue));
const lenProp = Q(objectToLength(O));
const len = Q(ToLength(lenProp)).numberValue();
if (IsCallable(predicate) === Value.false) {
return surroundingAgent.Throw('TypeError', 'NotAFunction', predicate);
}
let k = 0;
while (k < len) {
const Pk = X(ToString(new Value(k)));
const kValue = Q(Get(O, Pk));
const testResult = ToBoolean(Q(Call(predicate, thisArg, [kValue, new Value(k), O])));
if (testResult === Value.true) {
return kValue;
}
k += 1;
}
return Value.undefined;
}
// 22.1.3.9 #sec-array.prototype.findindex
// 22.2.3.11 #sec-%typedarray%.prototype.findindex
function ArrayProto_findIndex([predicate = Value.undefined, thisArg = Value.undefined], { thisValue }) {
Q(priorToEvaluatingAlgorithm(thisValue));
const O = Q(ToObject(thisValue));
const lenProp = Q(objectToLength(O));
const len = Q(ToLength(lenProp)).numberValue();
if (IsCallable(predicate) === Value.false) {
return surroundingAgent.Throw('TypeError', 'NotAFunction', predicate);
}
let k = 0;
while (k < len) {
const Pk = X(ToString(new Value(k)));
const kValue = Q(Get(O, Pk));
const testResult = ToBoolean(Q(Call(predicate, thisArg, [kValue, new Value(k), O])));
if (testResult === Value.true) {
return new Value(k);
}
k += 1;
}
return new Value(-1);
}
// 22.1.3.12 #sec-array.prototype.foreach
// 22.2.3.12 #sec-%typedarray%.prototype.foreach
function ArrayProto_forEach([callbackfn = Value.undefined, thisArg = Value.undefined], { thisValue }) {
Q(priorToEvaluatingAlgorithm(thisValue));
const O = Q(ToObject(thisValue));
const lenProp = Q(objectToLength(O));
const len = Q(ToLength(lenProp)).numberValue();
if (IsCallable(callbackfn) === Value.false) {
return surroundingAgent.Throw('TypeError', 'NotAFunction', callbackfn);
}
let k = 0;
while (k < len) {
const Pk = X(ToString(new Value(k)));
const kPresent = Q(HasProperty(O, Pk));
if (kPresent === Value.true) {
const kValue = Q(Get(O, Pk));
Q(Call(callbackfn, thisArg, [kValue, new Value(k), O]));
}
k += 1;
}
return Value.undefined;
}
// 22.1.3.13 #sec-array.prototype.includes
// 22.2.3.13 #sec-%typedarray%.prototype.includes
function ArrayProto_includes([searchElement = Value.undefined, fromIndex = Value.undefined], { thisValue }) {
Q(priorToEvaluatingAlgorithm(thisValue));
const O = Q(ToObject(thisValue));
const lenProp = Q(objectToLength(O));
const len = Q(ToLength(lenProp)).numberValue();
if (len === 0) {
return Value.false;
}
const n = Q(ToInteger(fromIndex)).numberValue();
if (fromIndex === Value.undefined) {
Assert(n === 0);
}
let k;
if (n >= 0) {
k = n;
} else {
k = len + n;
if (k < 0) {
k = 0;
}
}
while (k < len) {
const kStr = X(ToString(new Value(k)));
const elementK = Q(Get(O, kStr));
if (SameValueZero(searchElement, elementK) === Value.true) {
return Value.true;
}
k += 1;
}
return Value.false;
}
// 22.1.3.14 #sec-array.prototype.indexof
// 22.2.3.14 #sec-%typedarray%.prototype.indexof
function ArrayProto_indexOf([searchElement = Value.undefined, fromIndex = Value.undefined], { thisValue }) {
Q(priorToEvaluatingAlgorithm(thisValue));
const O = Q(ToObject(thisValue));
const lenProp = Q(objectToLength(O));
const len = Q(ToLength(lenProp)).numberValue();
if (len === 0) {
return new Value(-1);
}
const n = Q(ToInteger(fromIndex)).numberValue();
if (fromIndex === Value.undefined) {
Assert(n === 0);
}
if (n >= len) {
return new Value(-1);
}
let k;
if (n >= 0) {
k = n;
} else {
k = len + n;
if (k < 0) {
k = 0;
}
}
while (k < len) {
const kStr = X(ToString(new Value(k)));
const kPresent = Q(HasProperty(O, kStr));
if (kPresent === Value.true) {
const elementK = Q(Get(O, kStr));
const same = StrictEqualityComparison(searchElement, elementK);
if (same === Value.true) {
return new Value(k);
}
}
k += 1;
}
return new Value(-1);
}
// 22.1.3.15 #sec-array.prototype.join
// 22.2.3.15 #sec-%typedarray%.prototype.join
function ArrayProto_join([separator = Value.undefined], { thisValue }) {
Q(priorToEvaluatingAlgorithm(thisValue));
const O = Q(ToObject(thisValue));
const lenProp = Q(objectToLength(O));
const len = Q(ToLength(lenProp)).numberValue();
let sep;
if (Type(separator) === 'Undefined') {
sep = ',';
} else {
sep = Q(ToString(separator)).stringValue();
}
let R = '';
let k = 0;
while (k < len) {
if (k > 0) {
R = `${R}${sep}`;
}
const kStr = X(ToString(new Value(k)));
const element = Q(Get(O, kStr));
let next;
if (Type(element) === 'Undefined' || Type(element) === 'Null') {
next = '';
} else {
next = Q(ToString(element)).stringValue();
}
R = `${R}${next}`;
k += 1;
}
return new Value(R);
}
// 22.1.3.17 #sec-array.prototype.lastindexof
// 22.2.3.17 #sec-%typedarray%.prototype.lastindexof
function ArrayProto_lastIndexOf([searchElement = Value.undefined, fromIndex], { thisValue }) {
Q(priorToEvaluatingAlgorithm(thisValue));
const O = Q(ToObject(thisValue));
const lenProp = Q(objectToLength(O));
const len = Q(ToLength(lenProp)).numberValue();
if (len === 0) {
return new Value(-1);
}
let n;
if (fromIndex !== undefined) {
n = Q(ToInteger(fromIndex)).numberValue();
} else {
n = len - 1;
}
let k;
if (n >= 0) {
k = Math.min(n, len - 1);
} else {
k = len + n;
}
while (k >= 0) {
const kStr = X(ToString(new Value(k)));
const kPresent = Q(HasProperty(O, kStr));
if (kPresent === Value.true) {
const elementK = Q(Get(O, kStr));
const same = StrictEqualityComparison(searchElement, elementK);
if (same === Value.true) {
return new Value(k);
}
}
k -= 1;
}
return new Value(-1);
}
// 22.1.3.21 #sec-array.prototype.reduce
// 22.2.3.20 #sec-%typedarray%.prototype.reduce
function ArrayProto_reduce([callbackfn = Value.undefined, initialValue], { thisValue }) {
Q(priorToEvaluatingAlgorithm(thisValue));
const O = Q(ToObject(thisValue));
const lenProp = Q(objectToLength(O));
const len = Q(ToLength(lenProp)).numberValue();
if (IsCallable(callbackfn) === Value.false) {
return surroundingAgent.Throw('TypeError', 'NotAFunction', callbackfn);
}
if (len === 0 && initialValue === undefined) {
return surroundingAgent.Throw('TypeError', 'ArrayEmptyReduce');
}
let k = 0;
let accumulator = Value.undefined;
if (initialValue !== undefined) {
accumulator = initialValue;
} else {
let kPresent = false;
while (kPresent === false && k < len) {
const Pk = X(ToString(new Value(k)));
kPresent = Q(HasProperty(O, Pk)) === Value.true;
if (kPresent === true) {
accumulator = Q(Get(O, Pk));
}
k += 1;
}
if (kPresent === false) {
return surroundingAgent.Throw('TypeError', 'ArrayEmptyReduce');
}
}
while (k < len) {
const Pk = X(ToString(new Value(k)));
const kPresent = Q(HasProperty(O, Pk));
if (kPresent === Value.true) {
const kValue = Q(Get(O, Pk));
accumulator = Q(Call(callbackfn, Value.undefined, [accumulator, kValue, new Value(k), O]));
}
k += 1;
}
return accumulator;
}
// 22.1.3.22 #sec-array.prototype.reduceright
// 22.2.3.21 #sec-%typedarray%.prototype.reduceright
function ArrayProto_reduceRight([callbackfn = Value.undefined, initialValue], { thisValue }) {
Q(priorToEvaluatingAlgorithm(thisValue));
const O = Q(ToObject(thisValue));
const lenProp = Q(objectToLength(O));
const len = Q(ToLength(lenProp)).numberValue();
if (IsCallable(callbackfn) === Value.false) {
return surroundingAgent.Throw('TypeError', 'NotAFunction', callbackfn);
}
if (len === 0 && initialValue === undefined) {
return surroundingAgent.Throw('TypeError', 'ArrayEmptyReduce');
}
let k = len - 1;
let accumulator = Value.undefined;
if (initialValue !== undefined) {
accumulator = initialValue;
} else {
let kPresent = false;
while (kPresent === false && k >= 0) {
const Pk = X(ToString(new Value(k)));
kPresent = Q(HasProperty(O, Pk)) === Value.true;
if (kPresent === true) {
accumulator = Q(Get(O, Pk));
}
k -= 1;
}
if (kPresent === false) {
return surroundingAgent.Throw('TypeError', 'ArrayEmptyReduce');
}
}
while (k >= 0) {
const Pk = X(ToString(new Value(k)));
const kPresent = Q(HasProperty(O, Pk));
if (kPresent === Value.true) {
const kValue = Q(Get(O, Pk));
accumulator = Q(Call(callbackfn, Value.undefined, [accumulator, kValue, new Value(k), O]));
}
k -= 1;
}
return accumulator;
}
// 22.1.3.23 #sec-array.prototype.reverse
// 22.2.3.22 #sec-%typedarray%.prototype.reverse
function ArrayProto_reverse(args, { thisValue }) {
Q(priorToEvaluatingAlgorithm(thisValue));
const O = Q(ToObject(thisValue));
const lenProp = Q(objectToLength(O));
const len = Q(ToLength(lenProp)).numberValue();
const middle = Math.floor(len / 2);
let lower = 0;
while (lower !== middle) {
const upper = len - lower - 1;
const upperP = X(ToString(new Value(upper)));
const lowerP = X(ToString(new Value(lower)));
const lowerExists = Q(HasProperty(O, lowerP));
let lowerValue;
let upperValue;
if (lowerExists === Value.true) {
lowerValue = Q(Get(O, lowerP));
}
const upperExists = Q(HasProperty(O, upperP));
if (upperExists === Value.true) {
upperValue = Q(Get(O, upperP));
}
if (lowerExists === Value.true && upperExists === Value.true) {
Q(Set(O, lowerP, upperValue, Value.true));
Q(Set(O, upperP, lowerValue, Value.true));
} else if (lowerExists === Value.false && upperExists === Value.true) {
Q(Set(O, lowerP, upperValue, Value.true));
Q(DeletePropertyOrThrow(O, upperP));
} else if (lowerExists === Value.true && upperExists === Value.false) {
Q(DeletePropertyOrThrow(O, lowerP));
Q(Set(O, upperP, lowerValue, Value.true));
} else {
// no further action is required
}
lower += 1;
}
return O;
}
// 22.1.3.26 #sec-array.prototype.some
// 22.2.3.25 #sec-%typedarray%.prototype.some
function ArrayProto_some([callbackfn = Value.undefined, thisArg = Value.undefined], { thisValue }) {
Q(priorToEvaluatingAlgorithm(thisValue));
const O = Q(ToObject(thisValue));
const lenProp = Q(objectToLength(O));
const len = Q(ToLength(lenProp)).numberValue();
if (IsCallable(callbackfn) === Value.false) {
return surroundingAgent.Throw('TypeError', 'NotAFunction', callbackfn);
}
let k = 0;
while (k < len) {
const Pk = X(ToString(new Value(k)));
const kPresent = Q(HasProperty(O, Pk));
if (kPresent === Value.true) {
const kValue = Q(Get(O, Pk));
const testResult = ToBoolean(Q(Call(callbackfn, thisArg, [kValue, new Value(k), O])));
if (testResult === Value.true) {
return Value.true;
}
}
k += 1;
}
return Value.false;
}
// 22.1.3.29 #sec-array.prototype.tolocalestring
// 22.2.3.28 #sec-%typedarray%.prototype.tolocalestring
function ArrayProto_toLocaleString(args, { thisValue }) {
Q(priorToEvaluatingAlgorithm(thisValue));
const array = Q(ToObject(thisValue));
const lenProp = Q(objectToLength(array));
const len = Q(ToLength(lenProp)).numberValue();
const separator = ', ';
let R = '';
let k = 0;
while (k < len) {
if (k > 0) {
R = `${R}${separator}`;
}
const kStr = X(ToString(new Value(k)));
const nextElement = Q(Get(array, kStr));
if (nextElement !== Value.undefined && nextElement !== Value.null) {
const S = Q(ToString(Q(Invoke(nextElement, new Value('toLocaleString'))))).stringValue();
R = `${R}${S}`;
}
k += 1;
}
return new Value(R);
}
assignProps(realmRec, proto, [
['every', ArrayProto_every, 1],
['find', ArrayProto_find, 1],
['findIndex', ArrayProto_findIndex, 1],
['forEach', ArrayProto_forEach, 1],
['includes', ArrayProto_includes, 1],
['indexOf', ArrayProto_indexOf, 1],
['join', ArrayProto_join, 1],
['lastIndexOf', ArrayProto_lastIndexOf, 1],
['reduce', ArrayProto_reduce, 1],
['reduceRight', ArrayProto_reduceRight, 1],
['reverse', ArrayProto_reverse, 0],
['some', ArrayProto_some, 1],
['toLocaleString', ArrayProto_toLocaleString, 0],
]);
}
@@ -1,140 +0,0 @@
import { surroundingAgent } from '../engine.mjs';
import {
AsyncFromSyncIteratorContinuation,
Call,
CreateIterResultObject,
GetMethod,
IteratorNext,
NewPromiseCapability,
Assert,
} from '../abstract-ops/all.mjs';
import { Type, Value } from '../value.mjs';
import { IfAbruptRejectPromise, X } from '../completion.mjs';
import { BootstrapPrototype } from './Bootstrap.mjs';
// #sec-%asyncfromsynciteratorprototype%.next
function AsyncFromSyncIteratorPrototype_next([value], { thisValue }) {
// 1. Let O be the this value.
const O = thisValue;
// 2. Assert: Type(O) is Object and O has a [[SyncIteratorRecord]] internal slot.
Assert(Type(O) === 'Object' && 'SyncIteratorRecord' in O);
// 3. Let promiseCapability be ! NewPromiseCapability(%Promise%).
const promiseCapability = X(NewPromiseCapability(surroundingAgent.intrinsic('%Promise%')));
// 4. Let syncIteratorRecord be O.[[SyncIteratorRecord]].
const syncIteratorRecord = O.SyncIteratorRecord;
// 5. If value is present, then
let result;
if (value !== undefined) {
// a. Let result be IteratorNext(syncIteratorRecord, value).
result = IteratorNext(syncIteratorRecord, value);
} else { // 6. Else,
// a. Let result be IteratorNext(syncIteratorRecord).
result = IteratorNext(syncIteratorRecord);
}
// 7. IfAbruptRejectPromise(result, promiseCapability).
IfAbruptRejectPromise(result, promiseCapability);
// 8. Return ! AsyncFromSyncIteratorContinuation(result, promiseCapability).
return X(AsyncFromSyncIteratorContinuation(result, promiseCapability));
}
// #sec-%asyncfromsynciteratorprototype%.return
function AsyncFromSyncIteratorPrototype_return([value], { thisValue }) {
// 1. Let O be the this value.
const O = thisValue;
// 2. Assert: Type(O) is Object and O has a [[SyncIteratorRecord]] internal slot.
Assert(Type(O) === 'Object' && 'SyncIteratorRecord' in O);
// 3. Let promiseCapability be ! NewPromiseCapability(%Promise%).
const promiseCapability = X(NewPromiseCapability(surroundingAgent.intrinsic('%Promise%')));
// 4. Let syncIterator be O.[[SyncIteratorRecord]].[[Iterator]].
const syncIterator = O.SyncIteratorRecord.Iterator;
// 5. Let return be GetMethod(syncIterator, "return").
const ret = GetMethod(syncIterator, new Value('return'));
// 6. IfAbruptRejectPromise(return, promiseCapability).
IfAbruptRejectPromise(ret, promiseCapability);
// 7. If return is undefined, then
if (ret === Value.undefined) {
// a. Let iterResult be ! CreateIterResultObject(value, true).
const iterResult = X(CreateIterResultObject(value, Value.true));
// b. Perform ! Call(promiseCapability.[[Resolve]], undefined, « iterResult »).
X(Call(promiseCapability.Resolve, Value.undefined, [iterResult]));
// c. Return promiseCapability.[[Promise]].
return promiseCapability.Promise;
}
// 8. If value is present, then
let result;
if (value !== undefined) {
// a. Let result be Call(return, syncIterator, « value »).
result = Call(ret, syncIterator, [value]);
} else { // 9. Else,
// a. Let result be Call(return, syncIterator).
result = Call(ret, syncIterator);
}
// 10. IfAbruptRejectPromise(result, promiseCapability).
IfAbruptRejectPromise(result, promiseCapability);
// 11. If Type(result) is not Object, then
if (Type(result) !== 'Object') {
// a. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).
X(Call(promiseCapability.Reject, Value.undefined, [
surroundingAgent.Throw('TypeError', 'NotAnObject', result).Value,
]));
// b. Return promiseCapability.[[Promise]].
return promiseCapability.Promise;
}
// 12. Return ! AsyncFromSyncIteratorContinuation(result, promiseCapability).
return X(AsyncFromSyncIteratorContinuation(result, promiseCapability));
}
// #sec-%asyncfromsynciteratorprototype%.throw
function AsyncFromSyncIteratorPrototype_throw([value], { thisValue }) {
// 1. Let O be this value.
const O = thisValue;
// 2. Assert: Type(O) is Object and O has a [[SyncIteratorRecord]] internal slot.
Assert(Type(O) === 'Object' && 'SyncIteratorRecord' in O);
// 3. Let promiseCapability be ! NewPromiseCapability(%Promise%).
const promiseCapability = X(NewPromiseCapability(surroundingAgent.intrinsic('%Promise%')));
// 4. Let syncIterator be O.[[SyncIteratorRecord]].[[Iterator]].
const syncIterator = O.SyncIteratorRecord.Iterator;
// 5. Let throw be GetMethod(syncIterator, "throw").
const thr = GetMethod(syncIterator, new Value('throw'));
// 6. IfAbruptRejectPromise(throw, promiseCapability).
IfAbruptRejectPromise(thr, promiseCapability);
// 7. If throw is undefined, then
if (thr === Value.undefined) {
// a. Perform ! Call(promiseCapability.[[Reject]], undefined, « value »).
X(Call(promiseCapability.Reject, Value.undefined, [value]));
// b. Return promiseCapability.[[Promise]].
return promiseCapability.Promise;
}
// 8. If value is present, then
let result;
if (value !== undefined) {
// a. Let result be Call(throw, syncIterator, « value »).
result = Call(thr, syncIterator, [value]);
} else { // 9. Else,
// a. Let result be Call(throw, syncIterator).
result = Call(thr, syncIterator);
}
// 10. IfAbruptRejectPromise(result, promiseCapability).
IfAbruptRejectPromise(result, promiseCapability);
// 11. If Type(result) is not Object, then
if (Type(result) !== 'Object') {
// a. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).
X(Call(promiseCapability.Reject, Value.undefined, [
surroundingAgent.Throw('TypeError', 'NotAnObject', result).Value,
]));
// b. Return promiseCapability.[[Promise]].
return promiseCapability.Promise;
}
// 12. Return ! AsyncFromSyncIteratorContinuation(result, promiseCapability).
return X(AsyncFromSyncIteratorContinuation(result, promiseCapability));
}
export function BootstrapAsyncFromSyncIteratorPrototype(realmRec) {
const proto = BootstrapPrototype(realmRec, [
['next', AsyncFromSyncIteratorPrototype_next, 0],
['return', AsyncFromSyncIteratorPrototype_return, 0],
['throw', AsyncFromSyncIteratorPrototype_throw, 0],
], realmRec.Intrinsics['%AsyncIteratorPrototype%']);
realmRec.Intrinsics['%AsyncFromSyncIteratorPrototype%'] = proto;
}
@@ -1,28 +0,0 @@
import { surroundingAgent } from '../engine.mjs';
import { Q } from '../completion.mjs';
import { CreateDynamicFunction } from '../runtime-semantics/all.mjs';
import { Descriptor, Value } from '../value.mjs';
import { BootstrapConstructor } from './Bootstrap.mjs';
// #sec-async-function-constructor-arguments
function AsyncFunctionConstructor(args, { NewTarget }) {
// 1. Let C be the active function object.
const C = surroundingAgent.activeFunctionObject;
// 2. Let args be the argumentsList that was passed to this function by [[Call]] or [[Construct]].
// 3. Return CreateDynamicFunction(C, NewTarget, async, args).
return Q(CreateDynamicFunction(C, NewTarget, 'async', args));
}
export function BootstrapAsyncFunction(realmRec) {
const cons = BootstrapConstructor(realmRec, AsyncFunctionConstructor, 'AsyncFunction', 1, realmRec.Intrinsics['%AsyncFunction.prototype%'], []);
cons.DefineOwnProperty(new Value('prototype'), Descriptor({
Writable: Value.false,
Enumerable: Value.false,
Configurable: Value.false,
}));
cons.Prototype = realmRec.Intrinsics['%Function%'];
realmRec.Intrinsics['%AsyncFunction%'] = cons;
}
@@ -1,7 +0,0 @@
import { BootstrapPrototype } from './Bootstrap.mjs';
export function BootstrapAsyncFunctionPrototype(realmRec) {
const proto = BootstrapPrototype(realmRec, [], realmRec.Intrinsics['%Function.prototype%'], 'AsyncFunction');
realmRec.Intrinsics['%AsyncFunction.prototype%'] = proto;
}
@@ -1,18 +0,0 @@
import { X } from '../completion.mjs';
import { Descriptor, Value } from '../value.mjs';
import { BootstrapPrototype } from './Bootstrap.mjs';
export function BootstrapAsyncGenerator(realmRec) {
const proto = BootstrapPrototype(realmRec, [
['prototype', realmRec.Intrinsics['%AsyncGenerator.prototype%'], undefined, { Writable: Value.false }],
], realmRec.Intrinsics['%Function.prototype%'], 'AsyncGeneratorFunction');
X(realmRec.Intrinsics['%AsyncGenerator.prototype%'].DefineOwnProperty(new Value('constructor'), Descriptor({
Value: proto,
Writable: Value.false,
Enumerable: Value.false,
Configurable: Value.true,
})));
realmRec.Intrinsics['%AsyncGeneratorFunction.prototype%'] = proto;
}
@@ -1,34 +0,0 @@
import { surroundingAgent } from '../engine.mjs';
import { Q, X } from '../completion.mjs';
import { CreateDynamicFunction } from '../runtime-semantics/all.mjs';
import { Descriptor, Value } from '../value.mjs';
import { BootstrapConstructor } from './Bootstrap.mjs';
// #sec-asyncgeneratorfunction
function AsyncGeneratorFunctionConstructor(args, { NewTarget }) {
// 1. Let C be the active function object.
const C = surroundingAgent.activeFunctionObject;
// 2. Let args be the argumentsList that was passed to this function by [[Call]] or [[Construct]].
// 3. Return ? CreateDynamicFunction(C, NewTarget, asyncGenerator, args).
return Q(CreateDynamicFunction(C, NewTarget, 'asyncGenerator', args));
}
export function BootstrapAsyncGeneratorFunction(realmRec) {
const cons = BootstrapConstructor(realmRec, AsyncGeneratorFunctionConstructor, 'AsyncGeneratorFunction', 1, realmRec.Intrinsics['%AsyncGeneratorFunction.prototype%'], []);
X(cons.DefineOwnProperty(new Value('prototype'), Descriptor({
Writable: Value.false,
Enumerable: Value.false,
Configurable: Value.false,
})));
X(realmRec.Intrinsics['%AsyncGeneratorFunction.prototype%'].DefineOwnProperty(new Value('constructor'), Descriptor({
Writable: Value.false,
Enumerable: Value.false,
Configurable: Value.true,
})));
cons.Prototype = realmRec.Intrinsics['%Function%'];
realmRec.Intrinsics['%AsyncGeneratorFunction%'] = cons;
}
@@ -1,49 +0,0 @@
import {
X,
Completion,
NormalCompletion,
ThrowCompletion,
} from '../completion.mjs';
import { Value } from '../value.mjs';
import { AsyncGeneratorEnqueue } from '../abstract-ops/all.mjs';
import { BootstrapPrototype } from './Bootstrap.mjs';
// #sec-asyncgenerator-prototype-next
function AsyncGeneratorPrototype_next([value = Value.undefined], { thisValue }) {
// 1. Let generator be the this value.
const generator = thisValue;
// 2. Let completion be NormalCompletion(value).
const completion = NormalCompletion(value);
// 3. Return ! AsyncGeneratorEnqueue(generator, completion).
return X(AsyncGeneratorEnqueue(generator, completion));
}
// #sec-asyncgenerator-prototype-return
function AsyncGeneratorPrototype_return([value = Value.undefined], { thisValue }) {
// 1. Let generator be the this value.
const generator = thisValue;
// 2. Let completion be Completion { [[Type]]: return, [[Value]]: value, [[Target]]: empty }.
const completion = new Completion({ Type: 'return', Value: value, Target: undefined });
// 3. Return ! AsyncGeneratorEnqueue(generator, completion).
return X(AsyncGeneratorEnqueue(generator, completion));
}
// #sec-asyncgenerator-prototype-throw
function AsyncGeneratorPrototype_throw([exception = Value.undefined], { thisValue }) {
// 1. Let generator be the this value.
const generator = thisValue;
// 2. Let completion be ThrowCompletion(exception).
const completion = ThrowCompletion(exception);
// 3. Return ! AsyncGeneratorEnqueue(generator, completion).
return X(AsyncGeneratorEnqueue(generator, completion));
}
export function BootstrapAsyncGeneratorPrototype(realmRec) {
const proto = BootstrapPrototype(realmRec, [
['next', AsyncGeneratorPrototype_next, 1],
['return', AsyncGeneratorPrototype_return, 1],
['throw', AsyncGeneratorPrototype_throw, 1],
], realmRec.Intrinsics['%AsyncIteratorPrototype%'], 'AsyncGenerator');
realmRec.Intrinsics['%AsyncGenerator.prototype%'] = proto;
}
@@ -1,16 +0,0 @@
import { wellKnownSymbols } from '../value.mjs';
import { BootstrapPrototype } from './Bootstrap.mjs';
// #sec-asynciteratorprototype-asynciterator
function AsyncIteratorPrototype_asyncIterator(args, { thisValue }) {
// 1. Return the this value.
return thisValue;
}
export function BootstrapAsyncIteratorPrototype(realmRec) {
const proto = BootstrapPrototype(realmRec, [
[wellKnownSymbols.asyncIterator, AsyncIteratorPrototype_asyncIterator, 0],
], realmRec.Intrinsics['%Object.prototype%']);
realmRec.Intrinsics['%AsyncIteratorPrototype%'] = proto;
}
-53
View File
@@ -1,53 +0,0 @@
import { surroundingAgent } from '../engine.mjs';
import { Type, Value } from '../value.mjs';
import { ToPrimitive, ToBigInt, ToIndex } from '../abstract-ops/all.mjs';
import { NumberToBigInt } from '../runtime-semantics/all.mjs';
import { Q } from '../completion.mjs';
import { BootstrapConstructor } from './Bootstrap.mjs';
// #sec-bigint-constructor
function BigIntConstructor([value], { NewTarget }) {
// 1. If NewTarget is not undefined, throw a TypeError exception.
if (NewTarget !== Value.undefined) {
return surroundingAgent.Throw('TypeError', 'NotAConstructor', 'BigInt');
}
// 2. Let prim be ? ToPrimitive(value, hint 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') {
return Q(NumberToBigInt(prim));
} else {
return Q(ToBigInt(value));
}
}
// #sec-bigint.asintn
function BigInt_asIntN([bits = Value.undefined, bigint = Value.undefined]) {
// 1. Set bits to ? ToIndex(bits).
bits = Q(ToIndex(bits));
// 2. Set bigint to ? ToBigInt(bigint).
bigint = Q(ToBigInt(bigint));
// 3. Let mod be the BigInt value that represents bigint modulo 2bits.
// 4. If mod ≥ 2^bits - 1, return mod - 2^bits; otherwise, return mod.
return new Value(BigInt.asIntN(bits.numberValue(), bigint.bigintValue()));
}
// #sec-bigint.asuintn
function BigInt_asUintN([bits = Value.undefined, bigint = Value.undefined]) {
// 1. Set bits to ? ToIndex(bits).
bits = Q(ToIndex(bits));
// 2. Set bigint to ? ToBigInt(bigint).
bigint = Q(ToBigInt(bigint));
// 3. Return the BigInt value that represents bigint modulo 2^bits.
return new Value(BigInt.asUintN(bits.numberValue(), bigint.bigintValue()));
}
export function BootstrapBigInt(realmRec) {
const bigintConstructor = BootstrapConstructor(realmRec, BigIntConstructor, 'BigInt', 1, realmRec.Intrinsics['%BigInt.prototype%'], [
['asIntN', BigInt_asIntN, 2],
['asUintN', BigInt_asUintN, 2],
]);
realmRec.Intrinsics['%BigInt%'] = bigintConstructor;
}
@@ -1,74 +0,0 @@
import { surroundingAgent } from '../engine.mjs';
import { Type, Value } from '../value.mjs';
import { Assert, ToInteger, ToString } from '../abstract-ops/all.mjs';
import { Q, X } from '../completion.mjs';
import { BootstrapPrototype } from './Bootstrap.mjs';
// #sec-thisbigintvalue
function thisBigIntValue(value) {
// 1. If Type(value) is BigInt, return value.
if (Type(value) === 'BigInt') {
return value;
}
// 2. If Type(value) is Object and value has a [[BigIntData]] internal slot, then
if (Type(value) === 'Object' && 'BigIntData' in value) {
// a. Assert: Type(value.[[BigIntData]]) is BigInt.
Assert(Type(value.BigIntData) === 'BigInt');
// b. Return value.[[BigIntData]].
return value.BigIntData;
}
// 3. Throw a TypeError exception.
return surroundingAgent.Throw('TypeError', 'NotATypeObject', 'BigInt', value);
}
// #sec-bigint.prototype.tolocalestring
function BigIntProto_toLocalString(args, { thisValue }) {
return BigIntProto_toString(args, { thisValue });
}
// #sec-bigint.prototype.tostring
function BigIntProto_toString([radix], { thisValue }) {
// 1. Let x be ? thisBigIntValue(this value).
const x = Q(thisBigIntValue(thisValue));
// 2. If radix is not present, let radixNumber be 10.
let radixNumber;
if (radix === undefined) {
radixNumber = 10;
} else if (radix === Value.undefined) {
// 3. Else if radix is undefined, let radixNumber be 10.
radixNumber = 10;
} else {
// 4. Else, let radixNumber be ? ToInteger(radix).
radixNumber = Q(ToInteger(radix)).numberValue();
}
// 5. If radixNumber < 2 or radixNumber > 36, throw a RangeError exception.
if (radixNumber < 2 || radixNumber > 36) {
return surroundingAgent.Throw('RangeError', 'InvalidRadix');
}
// 6. If radixNumber = 10, return ! ToString(x).
if (radixNumber === 10) {
return X(ToString(x));
}
// 7. Return the String representation of this Number value using the radix specified by
// radixNumber. Letters a-z are used for digits with values 10 through 35. The precise
// algorithm is implementation-dependent, however the algorithm should be a
// generalization of that specified in 6.1.6.2.23.
// TODO: Implementation stringification
return new Value(x.bigintValue().toString(radixNumber));
}
// #sec-bigint.prototype.tostring
function BigIntProto_valueOf(args, { thisValue }) {
// Return ? thisBigIntValue(this value).
return Q(thisBigIntValue(thisValue));
}
export function BootstrapBigIntPrototype(realmRec) {
const proto = BootstrapPrototype(realmRec, [
['toLocaleString', BigIntProto_toLocalString, 0],
['toString', BigIntProto_toString, 0],
['valueOf', BigIntProto_valueOf, 0],
], realmRec.Intrinsics['%Object.prototype%'], 'BigInt');
realmRec.Intrinsics['%BigInt.prototype%'] = proto;
}
-32
View File
@@ -1,32 +0,0 @@
import {
OrdinaryCreateFromConstructor,
ToBoolean,
} from '../abstract-ops/all.mjs';
import { Value } from '../value.mjs';
import { Q, X } from '../completion.mjs';
import { BootstrapConstructor } from './Bootstrap.mjs';
// #sec-boolean-constructor-boolean-value
function BooleanConstructor([value = Value.undefined], { NewTarget }) {
// 1. Let b be ! ToBoolean(value).
const b = X(ToBoolean(value));
// 2. If NewTarget is undefined, return b.
if (NewTarget === Value.undefined) {
return b;
}
// 3. Let O be ? OrdinaryCreateFromConstructor(NewTarget, "%Boolean.prototype%", « [[BooleanData]] »).
const O = Q(OrdinaryCreateFromConstructor(NewTarget, '%Boolean.prototype%', ['BooleanData']));
// 4. Set O.[[BooleanData]] to b.
O.BooleanData = b;
// 5. Return O.
return O;
}
export function BootstrapBoolean(realmRec) {
const cons = BootstrapConstructor(
realmRec, BooleanConstructor, 'Boolean', 1,
realmRec.Intrinsics['%Boolean.prototype%'], [],
);
realmRec.Intrinsics['%Boolean%'] = cons;
}
@@ -1,53 +0,0 @@
import {
Type,
Value,
} from '../value.mjs';
import {
surroundingAgent,
} from '../engine.mjs';
import { Assert } from '../abstract-ops/all.mjs';
import { Q } from '../completion.mjs';
import { BootstrapPrototype } from './Bootstrap.mjs';
function thisBooleanValue(value) {
if (Type(value) === 'Boolean') {
return value;
}
if (Type(value) === 'Object' && 'BooleanData' in value) {
const b = value.BooleanData;
Assert(Type(b) === 'Boolean');
return b;
}
return surroundingAgent.Throw('TypeError', 'NotATypeObject', 'Boolean', value);
}
// #sec-boolean.prototype.tostring
function BooleanProto_toString(argList, { thisValue }) {
// 1. Let b be ? thisBooleanValue(this value).
const b = Q(thisBooleanValue(thisValue));
// 2. If b is true, return "true"; else return "false".
if (b === Value.true) {
return new Value('true');
}
return new Value('false');
}
// #sec-boolean.prototype.valueof
function BooleanProto_valueOf(argList, { thisValue }) {
// 1. Return ? thisBooleanValue(this value).
return Q(thisBooleanValue(thisValue));
}
export function BootstrapBooleanPrototype(realmRec) {
const proto = BootstrapPrototype(realmRec, [
['toString', BooleanProto_toString, 0],
['valueOf', BooleanProto_valueOf, 0],
], realmRec.Intrinsics['%Object.prototype%']);
proto.BooleanData = Value.false;
realmRec.Intrinsics['%Boolean.prototype%'] = proto;
}
-116
View File
@@ -1,116 +0,0 @@
import {
Assert,
CreateBuiltinFunction,
OrdinaryObjectCreate,
SetFunctionLength,
SetFunctionName,
} from '../abstract-ops/all.mjs';
import {
Descriptor,
Value,
wellKnownSymbols,
} from '../value.mjs';
import { X } from '../completion.mjs';
// 17 #sec-ecmascript-standard-built-in-objects
export function assignProps(realmRec, obj, props) {
for (const item of props) {
if (item === undefined) {
continue;
}
const [n, v, len, descriptor] = item;
const name = n instanceof Value ? n : new Value(n);
if (Array.isArray(v)) {
// Every accessor property described in clauses 18 through 26 and in
// Annex B.2 has the attributes { [[Enumerable]]: false,
// [[Configurable]]: true } unless otherwise specified. If only a get
// accessor function is described, the set accessor function is the
// default value, undefined. If only a set accessor is described the get
// accessor is the default value, undefined.
let [
getter = Value.undefined,
setter = Value.undefined,
] = v;
if (typeof getter === 'function') {
getter = CreateBuiltinFunction(getter, [], realmRec);
X(SetFunctionName(getter, name, new Value('get')));
X(SetFunctionLength(getter, new Value(0)));
}
if (typeof setter === 'function') {
setter = CreateBuiltinFunction(setter, [], realmRec);
X(SetFunctionName(setter, name, new Value('set')));
X(SetFunctionLength(setter, new Value(1)));
}
X(obj.DefineOwnProperty(name, Descriptor({
Get: getter,
Set: setter,
Enumerable: Value.false,
Configurable: Value.true,
...descriptor,
})));
} else {
// Every other data property described in clauses 18 through 26 and in
// Annex B.2 has the attributes { [[Writable]]: true, [[Enumerable]]:
// false, [[Configurable]]: true } unless otherwise specified.
let value;
if (typeof v === 'function') {
Assert(typeof len === 'number');
value = CreateBuiltinFunction(v, [], realmRec);
X(SetFunctionName(value, name));
X(SetFunctionLength(value, new Value(len)));
} else {
value = v;
}
obj.properties.set(name, Descriptor({
Value: value,
Writable: Value.true,
Enumerable: Value.false,
Configurable: Value.true,
...descriptor,
}));
}
}
}
export function BootstrapPrototype(realmRec, props, Prototype, stringTag) {
Assert(Prototype !== undefined);
const proto = OrdinaryObjectCreate(Prototype);
assignProps(realmRec, proto, props);
if (stringTag !== undefined) {
X(proto.DefineOwnProperty(wellKnownSymbols.toStringTag, Descriptor({
Value: new Value(stringTag),
Writable: Value.false,
Enumerable: Value.false,
Configurable: Value.true,
})));
}
return proto;
}
export function BootstrapConstructor(realmRec, Constructor, name, length, Prototype, props = []) {
const cons = CreateBuiltinFunction(Constructor, [], realmRec, undefined, Value.true);
SetFunctionName(cons, new Value(name));
SetFunctionLength(cons, new Value(length));
X(cons.DefineOwnProperty(new Value('prototype'), Descriptor({
Value: Prototype,
Writable: Value.false,
Enumerable: Value.false,
Configurable: Value.false,
})));
X(Prototype.DefineOwnProperty(new Value('constructor'), Descriptor({
Value: cons,
Writable: Value.true,
Enumerable: Value.false,
Configurable: Value.true,
})));
assignProps(realmRec, cons, props);
return cons;
}
-65
View File
@@ -1,65 +0,0 @@
import { surroundingAgent } from '../engine.mjs';
import {
IsDetachedBuffer,
OrdinaryCreateFromConstructor,
ToIndex,
RequireInternalSlot,
} from '../abstract-ops/all.mjs';
import { Value } from '../value.mjs';
import { Q } from '../completion.mjs';
import { BootstrapConstructor } from './Bootstrap.mjs';
// #sec-dataview-constructor
function DataViewConstructor([buffer = Value.undefined, byteOffset = Value.undefined, byteLength = Value.undefined], { NewTarget }) {
// 1. If NewTarget is undefined, throw a TypeError exception.
if (NewTarget === Value.undefined) {
return surroundingAgent.Throw('TypeError', 'ConstructorNonCallable', this);
}
// 2. Perform ? RequireInternalSlot(buffer, [[ArrayBufferData]]).
Q(RequireInternalSlot(buffer, 'ArrayBufferData'));
// 3. Let offset be ? ToIndex(byteOffset).
const offset = Q(ToIndex(byteOffset)).numberValue();
// 4. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
if (IsDetachedBuffer(buffer) === Value.true) {
return surroundingAgent.Throw('TypeError', 'ArrayBufferDetached');
}
// 5. Let bufferByteLength be buffer.[[ArrayBufferByteLength]].
const bufferByteLength = buffer.ArrayBufferByteLength.numberValue();
// 6. If offset > bufferByteLength, throw a RangeError exception.
if (offset > bufferByteLength) {
return surroundingAgent.Throw('RangeError', 'DataViewOOB');
}
let viewByteLength;
// 7. If byteLength is undefined, then
if (byteLength === Value.undefined) {
// a. Let viewByteLength be bufferByteLength - offset.
viewByteLength = bufferByteLength - offset;
} else {
// a. Let viewByteLength be ? ToIndex(byteLength).
viewByteLength = Q(ToIndex(byteLength)).numberValue();
// b. If offset + viewByteLength > bufferByteLength, throw a RangeError exception.
if (offset + viewByteLength > bufferByteLength) {
return surroundingAgent.Throw('RangeError', 'DataViewOOB');
}
}
// 9. Let O be ? OrdinaryCreateFromConstructor(NewTarget, "%DataView.prototype%", « [[DataView]], [[ViewedArrayBuffer]], [[ByteLength]], [[ByteOffset]] »).
const O = Q(OrdinaryCreateFromConstructor(NewTarget, '%DataView.prototype%', ['DataView', 'ViewedArrayBuffer', 'ByteLength', 'ByteOffset']));
// 10. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
if (IsDetachedBuffer(buffer) === Value.true) {
return surroundingAgent.Throw('TypeError', 'ArrayBufferDetached');
}
// 11. Set O.[[ViewedArrayBuffer]] to buffer.
O.ViewedArrayBuffer = buffer;
// 12. Set O.[[ByteLength]] to viewByteLength.
O.ByteLength = new Value(viewByteLength);
// 13. Set O.[[ByteOffset]] to offset.
O.ByteOffset = new Value(offset);
// 14. Return O.
return O;
}
export function BootstrapDataView(realmRec) {
const dvConstructor = BootstrapConstructor(realmRec, DataViewConstructor, 'DataView', 1, realmRec.Intrinsics['%DataView.prototype%'], []);
realmRec.Intrinsics['%DataView%'] = dvConstructor;
}
@@ -1,267 +0,0 @@
import {
Assert,
GetViewValue,
SetViewValue,
IsDetachedBuffer,
RequireInternalSlot,
} from '../abstract-ops/all.mjs';
import { Q } from '../completion.mjs';
import { surroundingAgent } from '../engine.mjs';
import { Value } from '../value.mjs';
import { BootstrapPrototype } from './Bootstrap.mjs';
// #sec-get-dataview.prototype.buffer
function DataViewProto_buffer(args, { thisValue }) {
// 1. Let O be the this value.
const O = thisValue;
// 2. Perform ? RequireInternalSlot(O, [[DataView]]).
Q(RequireInternalSlot(O, 'DataView'));
// 3. Assert: O has a [[ViewedArrayBuffer]] internal slot.
Assert('ViewedArrayBuffer' in O);
// 4. Let buffer be O.[[ViewedArrayBuffer]].
const buffer = O.ViewedArrayBuffer;
// 5. Return buffer.
return buffer;
}
// #sec-get-dataview.prototype.bytelength
function DataViewProto_byteLength(args, { thisValue }) {
// 1. Let O be the this value.
const O = thisValue;
// 2. Perform ? RequireInternalSlot(O, [[DataView]]).
Q(RequireInternalSlot(O, 'DataView'));
// 3. Assert: O has a [[ViewedArrayBuffer]] internal slot.
Assert('ViewedArrayBuffer' in O);
// 4. Let buffer be O.[[ViewedArrayBuffer]].
const buffer = O.ViewedArrayBuffer;
// 5. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
if (IsDetachedBuffer(buffer) === Value.true) {
return surroundingAgent.Throw('TypeError', 'ArrayBufferDetached');
}
// 6. Let size be O.[[ByteLength]].
const size = O.ByteLength;
// 7. Return size.
return size;
}
// #sec-get-dataview.prototype.byteoffset
function DataViewProto_byteOffset(args, { thisValue }) {
// 1. Let O be the this value.
const O = thisValue;
// 2. Perform ? RequireInternalSlot(O, [[DataView]]).
Q(RequireInternalSlot(O, 'DataView'));
// 3. Assert: O has a [[ViewedArrayBuffer]] internal slot.
Assert('ViewedArrayBuffer' in O);
// 4. Let buffer be O.[[ViewedArrayBuffer]].
const buffer = O.ViewedArrayBuffer;
// 5. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
if (IsDetachedBuffer(buffer) === Value.true) {
return surroundingAgent.Throw('TypeError', 'ArrayBufferDetached');
}
// 6. Let offset be O.[[ByteOffset]].
const offset = O.ByteOffset;
// 7. Return offset.
return offset;
}
// #sec-dataview.prototype.getbigint64
function DataViewProto_getBigInt64([byteOffset = Value.undefined, littleEndian = Value.undefined], { thisValue }) {
// 1. Let v be the this value.
const v = thisValue;
// 2. Return ? GetViewValue(v, byteOffset, littleEndian, BigInt64).
return Q(GetViewValue(v, byteOffset, littleEndian, 'BigInt64'));
}
// #sec-dataview.prototype.getbiguint64
function DataViewProto_getBigUint64([byteOffset = Value.undefined, littleEndian = Value.undefined], { thisValue }) {
// 1. Let v be the this value.
const v = thisValue;
// 2. Return ? GetViewValue(v, byteOffset, littleEndian, BigUint64).
return Q(GetViewValue(v, byteOffset, littleEndian, 'BigUint64'));
}
// 24.3.4.5 #sec-dataview.prototype.getfloat32
function DataViewProto_getFloat32([byteOffset = Value.undefined, littleEndian], { thisValue }) {
const v = thisValue;
if (littleEndian === undefined) {
littleEndian = Value.false;
}
return Q(GetViewValue(v, byteOffset, littleEndian, 'Float32'));
}
// 24.3.4.6 #sec-dataview.prototype.getfloat64
function DataViewProto_getFloat64([byteOffset = Value.undefined, littleEndian], { thisValue }) {
const v = thisValue;
if (littleEndian === undefined) {
littleEndian = Value.false;
}
return Q(GetViewValue(v, byteOffset, littleEndian, 'Float64'));
}
// 24.3.4.7 #sec-dataview.prototype.getint8
function DataViewProto_getInt8([byteOffset = Value.undefined], { thisValue }) {
const v = thisValue;
return Q(GetViewValue(v, byteOffset, Value.true, 'Int8'));
}
// 24.3.4.8 #sec-dataview.prototype.getint16
function DataViewProto_getInt16([byteOffset = Value.undefined, littleEndian], { thisValue }) {
const v = thisValue;
if (littleEndian === undefined) {
littleEndian = Value.false;
}
return Q(GetViewValue(v, byteOffset, littleEndian, 'Int16'));
}
// 24.3.4.9 #sec-dataview.prototype.getint32
function DataViewProto_getInt32([byteOffset = Value.undefined, littleEndian], { thisValue }) {
const v = thisValue;
if (littleEndian === undefined) {
littleEndian = Value.false;
}
return Q(GetViewValue(v, byteOffset, littleEndian, 'Int32'));
}
// 24.3.4.10 #sec-dataview.prototype.getuint8
function DataViewProto_getUint8([byteOffset = Value.undefined], { thisValue }) {
const v = thisValue;
return Q(GetViewValue(v, byteOffset, Value.true, 'Uint8'));
}
// 24.3.4.11 #sec-dataview.prototype.getuint16
function DataViewProto_getUint16([byteOffset = Value.undefined, littleEndian], { thisValue }) {
const v = thisValue;
if (littleEndian === undefined) {
littleEndian = Value.false;
}
return Q(GetViewValue(v, byteOffset, littleEndian, 'Uint16'));
}
// 24.3.4.12 #sec-dataview.prototype.getuint32
function DataViewProto_getUint32([byteOffset = Value.undefined, littleEndian], { thisValue }) {
const v = thisValue;
if (littleEndian === undefined) {
littleEndian = Value.false;
}
return Q(GetViewValue(v, byteOffset, littleEndian, 'Uint32'));
}
// #sec-dataview.prototype.setbigint64
function DataViewProto_setBigInt64([byteOffset = Value.undefined, value = Value.undefined, littleEndian], { thisValue }) {
// 1. Let v be the this value.
const v = thisValue;
// 2. If littleEndian is not present, set littleEndian to undefined.
if (littleEndian === undefined) {
littleEndian = Value.undefined;
}
// 3. Return ? SetViewValue(v, byteOffset, littleEndian, BigInt64, value).
return Q(SetViewValue(v, byteOffset, littleEndian, 'BigInt64', value));
}
// #sec-dataview.prototype.setbiguint64
function DataViewProto_setBigUint64([byteOffset = Value.undefined, value = Value.undefined, littleEndian], { thisValue }) {
// 1. Let v be the this value.
const v = thisValue;
// 2. If littleEndian is not present, set littleEndian to undefined.
if (littleEndian === undefined) {
littleEndian = Value.undefined;
}
// 3. Return ? SetViewValue(v, byteOffset, littleEndian, BigUint64, value).
return Q(SetViewValue(v, byteOffset, littleEndian, 'BigUint64', value));
}
// 24.3.4.13 #sec-dataview.prototype.setfloat32
function DataViewProto_setFloat32([byteOffset = Value.undefined, value = Value.undefined, littleEndian], { thisValue }) {
const v = thisValue;
if (littleEndian === undefined) {
littleEndian = Value.false;
}
return Q(SetViewValue(v, byteOffset, littleEndian, 'Float32', value));
}
// 24.3.4.14 #sec-dataview.prototype.setfloat64
function DataViewProto_setFloat64([byteOffset = Value.undefined, value = Value.undefined, littleEndian], { thisValue }) {
const v = thisValue;
if (littleEndian === undefined) {
littleEndian = Value.false;
}
return Q(SetViewValue(v, byteOffset, littleEndian, 'Float64', value));
}
// 24.3.4.15 #sec-dataview.prototype.setint8
function DataViewProto_setInt8([byteOffset = Value.undefined, value = Value.undefined], { thisValue }) {
const v = thisValue;
return Q(SetViewValue(v, byteOffset, Value.true, 'Int8', value));
}
// 24.3.4.16 #sec-dataview.prototype.setint16
function DataViewProto_setInt16([byteOffset = Value.undefined, value = Value.undefined, littleEndian], { thisValue }) {
const v = thisValue;
if (littleEndian === undefined) {
littleEndian = Value.false;
}
return Q(SetViewValue(v, byteOffset, littleEndian, 'Int16', value));
}
// 24.3.4.17 #sec-dataview.prototype.setint32
function DataViewProto_setInt32([byteOffset = Value.undefined, value = Value.undefined, littleEndian], { thisValue }) {
const v = thisValue;
if (littleEndian === undefined) {
littleEndian = Value.false;
}
return Q(SetViewValue(v, byteOffset, littleEndian, 'Int32', value));
}
// 24.3.4.18 #sec-dataview.prototype.setuint8
function DataViewProto_setUint8([byteOffset = Value.undefined, value = Value.undefined], { thisValue }) {
const v = thisValue;
return Q(SetViewValue(v, byteOffset, Value.true, 'Uint8', value));
}
// 24.3.4.19 #sec-dataview.prototype.setuint16
function DataViewProto_setUint16([byteOffset = Value.undefined, value = Value.undefined, littleEndian], { thisValue }) {
const v = thisValue;
if (littleEndian === undefined) {
littleEndian = Value.false;
}
return Q(SetViewValue(v, byteOffset, littleEndian, 'Uint16', value));
}
// 24.3.4.20 #sec-dataview.prototype.setuint32
function DataViewProto_setUint32([byteOffset = Value.undefined, value = Value.undefined, littleEndian], { thisValue }) {
const v = thisValue;
if (littleEndian === undefined) {
littleEndian = Value.false;
}
return Q(SetViewValue(v, byteOffset, littleEndian, 'Uint32', value));
}
export function BootstrapDataViewPrototype(realmRec) {
const proto = BootstrapPrototype(realmRec, [
['buffer', [DataViewProto_buffer]],
['byteLength', [DataViewProto_byteLength]],
['byteOffset', [DataViewProto_byteOffset]],
['getBigInt64', DataViewProto_getBigInt64, 1],
['getBigUint64', DataViewProto_getBigUint64, 1],
['getFloat32', DataViewProto_getFloat32, 1],
['getFloat64', DataViewProto_getFloat64, 1],
['getInt8', DataViewProto_getInt8, 1],
['getInt16', DataViewProto_getInt16, 1],
['getInt32', DataViewProto_getInt32, 1],
['getUint8', DataViewProto_getUint8, 1],
['getUint16', DataViewProto_getUint16, 1],
['getUint32', DataViewProto_getUint32, 1],
['setBigInt64', DataViewProto_setBigInt64, 2],
['setBigUint64', DataViewProto_setBigUint64, 2],
['setFloat32', DataViewProto_setFloat32, 2],
['setFloat64', DataViewProto_setFloat64, 2],
['setInt8', DataViewProto_setInt8, 2],
['setInt16', DataViewProto_setInt16, 2],
['setInt32', DataViewProto_setInt32, 2],
['setUint8', DataViewProto_setUint8, 2],
['setUint16', DataViewProto_setUint16, 2],
['setUint32', DataViewProto_setUint32, 2],
], realmRec.Intrinsics['%Object.prototype%'], 'DataView');
realmRec.Intrinsics['%DataView.prototype%'] = proto;
}
-204
View File
@@ -1,204 +0,0 @@
import {
Assert,
OrdinaryCreateFromConstructor,
ToPrimitive,
ToNumber,
ToInteger,
ToString,
MakeDate,
MakeDay,
MakeTime,
UTC,
TimeClip,
} from '../abstract-ops/all.mjs';
import { Value, Type } from '../value.mjs';
import {
AbruptCompletion,
Q, X,
} from '../completion.mjs';
import { BootstrapConstructor } from './Bootstrap.mjs';
import { ToDateString, thisTimeValue } from './DatePrototype.mjs';
// #sec-date-constructor
function DateConstructor(args, { NewTarget }) {
const numberOfArgs = args.length;
if (numberOfArgs >= 2) {
// 20.3.2.1 #sec-date-year-month-date-hours-minutes-seconds-ms
const [year, month, date, hours, minutes, seconds, ms] = args;
Assert(numberOfArgs >= 2);
if (NewTarget === Value.undefined) {
const now = Date.now();
return ToDateString(new Value(now));
} else {
const y = Q(ToNumber(year));
const m = Q(ToNumber(month));
let dt;
if (date !== undefined) {
dt = Q(ToNumber(date));
} else {
dt = new Value(1);
}
let h;
if (hours !== undefined) {
h = Q(ToNumber(hours));
} else {
h = new Value(0);
}
let min;
if (minutes !== undefined) {
min = Q(ToNumber(minutes));
} else {
min = new Value(0);
}
let s;
if (seconds !== undefined) {
s = Q(ToNumber(seconds));
} else {
s = new Value(0);
}
let milli;
if (ms !== undefined) {
milli = Q(ToNumber(ms));
} else {
milli = new Value(0);
}
let yr;
if (y.isNaN()) {
yr = new Value(NaN);
} else {
const yi = X(ToInteger(y)).numberValue();
if (yi >= 0 && yi <= 99) {
yr = new Value(1900 + yi);
} else {
yr = y;
}
}
const finalDate = MakeDate(MakeDay(yr, m, dt), MakeTime(h, min, s, milli));
const O = Q(OrdinaryCreateFromConstructor(NewTarget, '%Date.prototype%', ['DateValue']));
O.DateValue = TimeClip(UTC(finalDate));
return O;
}
} else if (numberOfArgs === 1) {
const [value] = args;
// 20.3.2.2 #sec-date-value
Assert(numberOfArgs === 1);
if (NewTarget === Value.undefined) {
const now = Date.now();
return ToDateString(new Value(now));
} else {
let tv;
if (Type(value) === 'Object' && 'DateValue' in value) {
tv = thisTimeValue(value);
} else {
const v = Q(ToPrimitive(value));
if (Type(v) === 'String') {
// Assert: The next step never returns an abrupt completion because Type(v) is String.
tv = parseDate(v);
} else {
tv = Q(ToNumber(v));
}
}
const O = Q(OrdinaryCreateFromConstructor(NewTarget, '%Date.prototype%', ['DateValue']));
O.DateValue = TimeClip(tv);
return O;
}
} else {
// 20.3.2.3 #sec-date-constructor-date
Assert(numberOfArgs === 0);
if (NewTarget === Value.undefined) {
const now = Date.now();
return ToDateString(new Value(now));
} else {
const O = Q(OrdinaryCreateFromConstructor(NewTarget, '%Date.prototype%', ['DateValue']));
O.DateValue = new Value(Date.now());
return O;
}
}
}
// 20.3.3.1 #sec-date.now
function Date_now() {
const now = Date.now();
return new Value(now);
}
// 20.3.3.2 #sec-date.parse
function Date_parse([string = Value.undefined]) {
const str = ToString(string);
if (str instanceof AbruptCompletion) {
return str;
}
return parseDate(str);
}
// 20.3.3.4 #sec-date.utc
function Date_UTC([year = Value.undefined, month, date, hours, minutes, seconds, ms]) {
const y = Q(ToNumber(year));
let m;
if (month !== undefined) {
m = Q(ToNumber(month));
} else {
m = new Value(0);
}
let dt;
if (date !== undefined) {
dt = Q(ToNumber(date));
} else {
dt = new Value(1);
}
let h;
if (hours !== undefined) {
h = Q(ToNumber(hours));
} else {
h = new Value(0);
}
let min;
if (minutes !== undefined) {
min = Q(ToNumber(minutes));
} else {
min = new Value(0);
}
let s;
if (seconds !== undefined) {
s = Q(ToNumber(seconds));
} else {
s = new Value(0);
}
let milli;
if (ms !== undefined) {
milli = Q(ToNumber(ms));
} else {
milli = new Value(0);
}
let yr;
if (y.isNaN()) {
yr = new Value(NaN);
} else {
const yi = X(ToInteger(y)).numberValue();
if (yi >= 0 && yi <= 99) {
yr = new Value(1900 + yi);
} else {
yr = y;
}
}
return TimeClip(MakeDate(MakeDay(yr, m, dt), MakeTime(h, min, s, milli)));
}
function parseDate(dateTimeString) {
// 20.3.1.15 #sec-date-time-string-format
// TODO: implement parsing without the host.
const parsed = Date.parse(dateTimeString.stringValue());
return new Value(parsed);
}
export function BootstrapDate(realmRec) {
const cons = BootstrapConstructor(realmRec, DateConstructor, 'Date', 7, realmRec.Intrinsics['%Date.prototype%'], [
['now', Date_now, 0],
['parse', Date_parse, 1],
['UTC', Date_UTC, 7],
]);
realmRec.Intrinsics['%Date%'] = cons;
}
-705
View File
@@ -1,705 +0,0 @@
import { surroundingAgent } from '../engine.mjs';
import {
Assert,
DateFromTime,
Day,
HourFromTime,
Invoke,
LocalTime,
LocalTZA,
MakeDate,
MakeDay,
MakeTime,
MinFromTime,
MonthFromTime,
msFromTime,
msPerMinute,
OrdinaryToPrimitive,
SecFromTime,
TimeClip,
TimeWithinDay,
ToNumber,
ToPrimitive,
ToObject,
UTC,
WeekDay,
YearFromTime,
} from '../abstract-ops/all.mjs';
import {
Type,
Value,
wellKnownSymbols,
} from '../value.mjs';
import { Q, X } from '../completion.mjs';
import { StringPad } from '../runtime-semantics/all.mjs';
import { BootstrapPrototype } from './Bootstrap.mjs';
export function thisTimeValue(value) {
if (Type(value) === 'Object' && 'DateValue' in value) {
return value.DateValue;
}
return surroundingAgent.Throw('TypeError', 'NotATypeObject', 'Date', value);
}
// 20.3.4.2 #sec-date.prototype.getdate
function DateProto_getDate(args, { thisValue }) {
const t = Q(thisTimeValue(thisValue));
if (t.isNaN()) {
return new Value(NaN);
}
return DateFromTime(LocalTime(t));
}
// 20.3.4.3 #sec-date.prototype.getday
function DateProto_getDay(args, { thisValue }) {
const t = Q(thisTimeValue(thisValue));
if (t.isNaN()) {
return new Value(NaN);
}
return WeekDay(LocalTime(t));
}
// 20.3.4.4 #sec-date.prototype.getfullyear
function DateProto_getFullYear(args, { thisValue }) {
const t = Q(thisTimeValue(thisValue));
if (t.isNaN()) {
return new Value(NaN);
}
return YearFromTime(LocalTime(t));
}
// 20.3.4.5 #sec-date.prototype.gethours
function DateProto_getHours(args, { thisValue }) {
const t = Q(thisTimeValue(thisValue));
if (t.isNaN()) {
return new Value(NaN);
}
return HourFromTime(LocalTime(t));
}
// 20.3.4.6 #sec-date.prototype.getmilliseconds
function DateProto_getMilliseconds(args, { thisValue }) {
const t = Q(thisTimeValue(thisValue));
if (t.isNaN()) {
return new Value(NaN);
}
return msFromTime(LocalTime(t));
}
// 20.3.4.7 #sec-date.prototype.getminutes
function DateProto_getMinutes(args, { thisValue }) {
const t = Q(thisTimeValue(thisValue));
if (t.isNaN()) {
return new Value(NaN);
}
return MinFromTime(LocalTime(t));
}
// 20.3.4.8 #sec-date.prototype.getmonth
function DateProto_getMonth(args, { thisValue }) {
const t = Q(thisTimeValue(thisValue));
if (t.isNaN()) {
return new Value(NaN);
}
return MonthFromTime(LocalTime(t));
}
// 20.3.4.9 #sec-date.prototype.getseconds
function DateProto_getSeconds(args, { thisValue }) {
const t = Q(thisTimeValue(thisValue));
if (t.isNaN()) {
return new Value(NaN);
}
return SecFromTime(LocalTime(t));
}
// 20.3.4.10 #sec-date.prototype.gettime
function DateProto_getTime(args, { thisValue }) {
return Q(thisTimeValue(thisValue));
}
// 20.3.4.11 #sec-date.prototype.gettimezoneoffset
function DateProto_getTimezoneOffset(args, { thisValue }) {
const t = Q(thisTimeValue(thisValue));
if (t.isNaN()) {
return new Value(NaN);
}
return new Value((t.numberValue() - LocalTime(t).numberValue()) / msPerMinute);
}
// 20.3.4.12 #sec-date.prototype.getutcdate
function DateProto_getUTCDate(args, { thisValue }) {
const t = Q(thisTimeValue(thisValue));
if (t.isNaN()) {
return new Value(NaN);
}
return DateFromTime(t);
}
// 20.3.4.13 #sec-date.prototype.getutcday
function DateProto_getUTCDay(args, { thisValue }) {
const t = Q(thisTimeValue(thisValue));
if (t.isNaN()) {
return new Value(NaN);
}
return WeekDay(t);
}
// 20.3.4.14 #sec-date.prototype.getutcfullyear
function DateProto_getUTCFullYear(args, { thisValue }) {
const t = Q(thisTimeValue(thisValue));
if (t.isNaN()) {
return new Value(NaN);
}
return YearFromTime(t);
}
// 20.3.4.15 #sec-date.prototype.getutchours
function DateProto_getUTCHours(args, { thisValue }) {
const t = Q(thisTimeValue(thisValue));
if (t.isNaN()) {
return new Value(NaN);
}
return HourFromTime(t);
}
// 20.3.4.16 #sec-date.prototype.getutcmilliseconds
function DateProto_getUTCMilliseconds(args, { thisValue }) {
const t = Q(thisTimeValue(thisValue));
if (t.isNaN()) {
return new Value(NaN);
}
return msFromTime(t);
}
// 20.3.4.17 #sec-date.prototype.getutcminutes
function DateProto_getUTCMinutes(args, { thisValue }) {
const t = Q(thisTimeValue(thisValue));
if (t.isNaN()) {
return new Value(NaN);
}
return MinFromTime(t);
}
// 20.3.4.18 #sec-date.prototype.getutcmonth
function DateProto_getUTCMonth(args, { thisValue }) {
const t = Q(thisTimeValue(thisValue));
if (t.isNaN()) {
return new Value(NaN);
}
return MonthFromTime(t);
}
// 20.3.4.19 #sec-date.prototype.getutcseconds
function DateProto_getUTCSeconds(args, { thisValue }) {
const t = Q(thisTimeValue(thisValue));
if (t.isNaN()) {
return new Value(NaN);
}
return SecFromTime(t);
}
// 20.3.4.20 #sec-date.prototype.setdate
function DateProto_setDate([date = Value.undefined], { thisValue }) {
const t = LocalTime(Q(thisTimeValue(thisValue)));
const dt = Q(ToNumber(date));
const newDate = MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), dt), TimeWithinDay(t));
const u = TimeClip(UTC(newDate));
thisValue.DateValue = u;
return u;
}
// 20.3.4.21 #sec-date.prototype.setfullyear
function DateProto_setFullYear([year = Value.undefined, month, date], { thisValue }) {
let t = Q(thisTimeValue(thisValue));
t = t.isNaN() ? new Value(0) : LocalTime(t);
const y = Q(ToNumber(year));
let m;
if (month !== undefined) {
m = Q(ToNumber(month));
} else {
m = MonthFromTime(t);
}
let dt;
if (date !== undefined) {
dt = Q(ToNumber(date));
} else {
dt = DateFromTime(t);
}
const newDate = MakeDate(MakeDay(y, m, dt), TimeWithinDay(t));
const u = TimeClip(UTC(newDate));
thisValue.DateValue = u;
return u;
}
// 20.3.4.22 #sec-date.prototype.sethours
function DateProto_setHours([hour = Value.undefined, min, sec, ms], { thisValue }) {
const t = LocalTime(Q(thisTimeValue(thisValue)));
const h = Q(ToNumber(hour));
let m;
if (min !== undefined) {
m = Q(ToNumber(min));
} else {
m = MinFromTime(t);
}
let s;
if (sec !== undefined) {
s = Q(ToNumber(sec));
} else {
s = SecFromTime(t);
}
let milli;
if (ms !== undefined) {
milli = Q(ToNumber(ms));
} else {
milli = msFromTime(t);
}
const date = MakeDate(Day(t), MakeTime(h, m, s, milli));
const u = TimeClip(UTC(date));
thisValue.DateValue = u;
return u;
}
// 20.3.4.23 #sec-date.prototype.setmilliseconds
function DateProto_setMilliseconds([ms = Value.undefined], { thisValue }) {
const t = LocalTime(Q(thisTimeValue(thisValue)));
ms = Q(ToNumber(ms));
const time = MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms);
const u = TimeClip(UTC(MakeDate(Day(t), time)));
thisValue.DateValue = u;
return u;
}
// 20.3.4.24 #sec-date.prototype.setminutes
function DateProto_setMinutes([min = Value.undefined, sec, ms], { thisValue }) {
// 1. Let t be LocalTime(? thisTimeValue(this value)).
const t = LocalTime(Q(thisTimeValue(thisValue)));
// 2. Let m be ? ToNumber(min).
const m = Q(ToNumber(min));
let s;
// 3. If sec is not present, let s be SecFromTime(t); otherwise, let s be ? ToNumber(sec).
if (sec !== undefined) {
s = Q(ToNumber(sec));
} else {
s = SecFromTime(t);
}
let milli;
// 4. If ms is not present, let milli be msFromTime(t); otherwise, let milli be ? ToNumber(ms).
if (ms !== undefined) {
milli = Q(ToNumber(ms));
} else {
milli = msFromTime(t);
}
// 5. Let date be MakeDate(Day(t), MakeTime(HourFromTime(t), m, s, milli)).
const date = MakeDate(Day(t), MakeTime(HourFromTime(t), m, s, milli));
// 6. Let u be TimeClip(UTC(date)).
const u = TimeClip(UTC(date));
// 7. Set the [[DateValue]] internal slot of this Date object to u.
thisValue.DateValue = u;
// 8. Return u.
return u;
}
// 20.3.4.25 #sec-date.prototype.setmonth
function DateProto_setMonth([month = Value.undefined, date], { thisValue }) {
const t = LocalTime(Q(thisTimeValue(thisValue)));
const m = Q(ToNumber(month));
let dt;
if (date !== undefined) {
dt = Q(ToNumber(date));
} else {
dt = DateFromTime(t);
}
const newDate = MakeDate(MakeDay(YearFromTime(t), m, dt), TimeWithinDay(t));
const u = TimeClip(UTC(newDate));
thisValue.DateValue = u;
return u;
}
// 20.3.4.26 #sec-date.prototype.setseconds
function DateProto_setSeconds([sec = Value.undefined, ms], { thisValue }) {
const t = LocalTime(Q(thisTimeValue(thisValue)));
const s = Q(ToNumber(sec));
let milli;
if (ms !== undefined) {
milli = Q(ToNumber(ms));
} else {
milli = msFromTime(t);
}
const date = MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), s, milli));
const u = TimeClip(UTC(date));
thisValue.DateValue = u;
return u;
}
// 20.3.4.27 #sec-date.prototype.settime
function DateProto_setTime([time = Value.undefined], { thisValue }) {
Q(thisTimeValue(thisValue));
const t = Q(ToNumber(time));
const v = TimeClip(t);
thisValue.DateValue = v;
return v;
}
// 20.3.4.28 #sec-date.prototype.setutcdate
function DateProto_setUTCDate([date = Value.undefined], { thisValue }) {
const t = Q(thisTimeValue(thisValue));
const dt = Q(ToNumber(date));
const newDate = MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), dt), TimeWithinDay(t));
const v = TimeClip(newDate);
thisValue.DateValue = v;
return v;
}
// 20.3.4.29 #sec-date.prototype.setutcfullyear
function DateProto_setUTCFullYear([year = Value.undefined, month, date], { thisValue }) {
let t = Q(thisTimeValue(thisValue));
if (t.isNaN()) {
t = new Value(0);
}
const y = Q(ToNumber(year));
let m;
if (month !== undefined) {
m = Q(ToNumber(month));
} else {
m = MonthFromTime(t);
}
let dt;
if (date !== undefined) {
dt = Q(ToNumber(date));
} else {
dt = DateFromTime(t);
}
const newDate = MakeDate(MakeDay(y, m, dt), TimeWithinDay(t));
const v = TimeClip(newDate);
thisValue.DateValue = v;
return v;
}
// 20.3.4.30 #sec-date.prototype.setutchours
function DateProto_setUTCHours([hour = Value.undefined, min, sec, ms], { thisValue }) {
const t = Q(thisTimeValue(thisValue));
const h = Q(ToNumber(hour));
let m;
if (min !== undefined) {
m = Q(ToNumber(min));
} else {
m = MinFromTime(t);
}
let s;
if (sec !== undefined) {
s = Q(ToNumber(sec));
} else {
s = SecFromTime(t);
}
let milli;
if (ms !== undefined) {
milli = Q(ToNumber(ms));
} else {
milli = msFromTime(t);
}
const newDate = MakeDate(Day(t), MakeTime(h, m, s, milli));
const v = TimeClip(newDate);
thisValue.DateValue = v;
return v;
}
// 20.3.4.31 #sec-date.prototype.setutcmilliseconds
function DateProto_setUTCMilliseconds([ms = Value.undefined], { thisValue }) {
const t = Q(thisTimeValue(thisValue));
const milli = Q(ToNumber(ms));
const time = MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), milli);
const v = TimeClip(MakeDate(Day(t), time));
thisValue.DateValue = v;
return v;
}
// 20.3.4.32 #sec-date.prototype.setutcminutes
function DateProto_setUTCMinutes([min = Value.undefined, sec, ms], { thisValue }) {
const t = Q(thisTimeValue(thisValue));
const m = Q(ToNumber(min));
let s;
if (sec !== undefined) {
s = Q(ToNumber(sec));
} else {
s = SecFromTime(t);
}
let milli;
if (ms !== undefined) {
milli = Q(ToNumber(ms));
} else {
milli = msFromTime(t);
}
const date = MakeDate(Day(t), MakeTime(HourFromTime(t), m, s, milli));
const v = TimeClip(date);
thisValue.DateValue = v;
return v;
}
// 20.3.4.33 #sec-date.prototype.setutcmonth
function DateProto_setUTCMonth([month = Value.undefined, date], { thisValue }) {
const t = Q(thisTimeValue(thisValue));
const m = Q(ToNumber(month));
let dt;
if (date !== undefined) {
dt = Q(ToNumber(date));
} else {
dt = DateFromTime(t);
}
const newDate = MakeDate(MakeDay(YearFromTime(t), m, dt), TimeWithinDay(t));
const v = TimeClip(newDate);
thisValue.DateValue = v;
return v;
}
// 20.3.4.34 #sec-date.prototype.setutcseconds
function DateProto_setUTCSeconds([sec = Value.undefined, ms], { thisValue }) {
const t = Q(thisTimeValue(thisValue));
const s = Q(ToNumber(sec));
let milli;
if (ms !== undefined) {
milli = Q(ToNumber(ms));
} else {
milli = msFromTime(t);
}
const date = MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), s, milli));
const v = TimeClip(date);
thisValue.DateValue = v;
return v;
}
// 20.3.4.35 #sec-date.prototype.todatestring
function DateProto_toDateString(args, { thisValue }) {
const O = thisValue;
if (Type(O) !== 'Object') {
return surroundingAgent.Throw('TypeError', 'NotATypeObject', 'Date', O);
}
const tv = Q(thisTimeValue(O));
if (tv.isNaN()) {
return new Value('Invalid Date');
}
const t = LocalTime(tv);
return DateString(t);
}
// 20.3.4.36 #sec-date.prototype.toisostring
function DateProto_toISOString(args, { thisValue }) {
const t = Q(thisTimeValue(thisValue));
if (!Number.isFinite(t.numberValue())) {
return surroundingAgent.Throw('RangeError', 'DateInvalidTime');
}
const year = YearFromTime(t).numberValue();
const month = MonthFromTime(t).numberValue() + 1;
const date = DateFromTime(t).numberValue();
const hour = HourFromTime(t).numberValue();
const min = MinFromTime(t).numberValue();
const sec = SecFromTime(t).numberValue();
const ms = msFromTime(t).numberValue();
// TODO: figure out if there can be invalid years.
let YYYY = String(year);
if (year < 0 || year > 9999) {
YYYY = year < 0 ? `${String(year).padStart(6, '0')}` : `+${String(year).padStart(6, '0')}`;
}
const MM = String(month).padStart(2, '0');
const DD = String(date).padStart(2, '0');
const HH = String(hour).padStart(2, '0');
const mm = String(min).padStart(2, '0');
const ss = String(sec).padStart(2, '0');
const sss = String(ms).padStart(3, '0');
const format = `${YYYY}-${MM}-${DD}T${HH}:${mm}:${ss}.${sss}Z`;
return new Value(format);
}
// 20.3.4.37 #sec-date.prototype.tojson
function DateProto_toJSON(args, { thisValue }) {
const O = Q(ToObject(thisValue));
const tv = Q(ToPrimitive(O, 'Number'));
if (Type(tv) === 'Number' && !Number.isFinite(tv.numberValue())) {
return Value.null;
}
return Q(Invoke(O, new Value('toISOString')));
}
// 20.3.4.38 #sec-date.prototype.tolocaledatestring
function DateProto_toLocaleDateString() {
// TODO: implement this function.
return surroundingAgent.Throw('Error', 'Raw', 'Date.prototype.toLocaleDateString is not implemented');
}
// 20.3.4.39 #sec-date.prototype.tolocalestring
function DateProto_toLocaleString() {
// TODO: implement this function.
return surroundingAgent.Throw('Error', 'Raw', 'Date.prototype.toLocaleString is not implemented');
}
// 20.3.4.40 #sec-date.prototype.tolocaletimestring
function DateProto_toLocaleTimeString() {
// TODO: implement this function.
return surroundingAgent.Throw('Error', 'Raw', 'Date.prototype.toLocaleTimeString is not implemented');
}
// 20.3.4.41 #sec-date.prototype.tostring
function DateProto_toString(args, { thisValue }) {
const tv = Q(thisTimeValue(thisValue));
return ToDateString(tv);
}
// 20.3.4.41.1 #sec-timestring
function TimeString(tv) {
Assert(Type(tv) === 'Number');
Assert(!tv.isNaN());
const hour = String(HourFromTime(tv).numberValue()).padStart(2, '0');
const minute = String(MinFromTime(tv).numberValue()).padStart(2, '0');
const second = String(SecFromTime(tv).numberValue()).padStart(2, '0');
return new Value(`${hour}:${minute}:${second} GMT`);
}
// Table 46 #sec-todatestring-day-names
const daysOfTheWeek = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
// Table 47 #sec-todatestring-month-names
const monthsOfTheYear = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
// 20.3.4.41.2 #sec-datestring
function DateString(tv) {
Assert(Type(tv) === 'Number');
Assert(!tv.isNaN());
const weekday = daysOfTheWeek[WeekDay(tv).numberValue()];
const month = monthsOfTheYear[MonthFromTime(tv).numberValue()];
const day = String(DateFromTime(tv).numberValue()).padStart(2, '0');
const yv = YearFromTime(tv).numberValue();
const yearSign = yv >= 0 ? '' : '-';
const year = new Value(String(Math.abs(yv)));
const paddedYear = X(StringPad(year, new Value(4), new Value('0'), 'start')).stringValue();
return new Value(`${weekday} ${month} ${day} ${yearSign}${paddedYear}`);
}
// 20.3.4.41.3 #sec-timezoneestring
export function TimeZoneString(tv) {
Assert(Type(tv) === 'Number');
Assert(!tv.isNaN());
const offset = LocalTZA(tv, true);
const offsetSign = offset >= 0 ? '+' : '-';
const offsetMin = String(MinFromTime(new Value(Math.abs(offset))).numberValue()).padStart(2, '0');
const offsetHour = String(HourFromTime(new Value(Math.abs(offset))).numberValue()).padStart(2, '0');
const tzName = '';
return new Value(`${offsetSign}${offsetHour}${offsetMin}${tzName}`);
}
// 20.3.4.41.4 #sec-todatestring
export function ToDateString(tv) {
Assert(Type(tv) === 'Number');
if (tv.isNaN()) {
return new Value('Invalid Date');
}
const t = LocalTime(tv);
return new Value(`${DateString(t).stringValue()} ${TimeString(t).stringValue()}${TimeZoneString(t).stringValue()}`);
}
// 20.3.4.42 #sec-date.prototype.totimestring
function DateProto_toTimeString(args, { thisValue }) {
const O = thisValue;
if (Type(O) !== 'Object') {
return surroundingAgent.Throw('TypeError', 'NotATypeObject', 'Date', O);
}
const tv = Q(thisTimeValue(O));
if (tv.isNaN()) {
return new Value('Invalid Date');
}
const t = LocalTime(tv);
return new Value(`${TimeString(t).stringValue()}${TimeZoneString(tv).stringValue()}`);
}
// 20.3.4.43 #sec-date.prototype.toutcstring
function DateProto_toUTCString(args, { thisValue }) {
const O = thisValue;
if (Type(O) !== 'Object') {
return surroundingAgent.Throw('TypeError', 'NotATypeObject', 'Date', O);
}
const tv = Q(thisTimeValue(O));
if (tv.isNaN()) {
return new Value('Invalid Date');
}
const weekday = daysOfTheWeek[WeekDay(tv).numberValue()];
const month = monthsOfTheYear[MonthFromTime(tv).numberValue()];
const day = String(DateFromTime(tv).numberValue()).padStart(2, '0');
const yv = YearFromTime(tv).numberValue();
const yearSign = yv >= 0 ? '' : '-';
const year = new Value(String(Math.abs(yv)));
const paddedYear = X(StringPad(year, new Value(4), new Value('0'), 'start')).stringValue();
return new Value(`${weekday}, ${day} ${month} ${yearSign}${paddedYear} ${TimeString(tv).stringValue()}`);
}
// 20.3.4.44 #sec-date.prototype.valueof
function DateProto_valueOf(args, { thisValue }) {
return Q(thisTimeValue(thisValue));
}
// 20.3.4.45 #sec-date.prototype-@@toprimitive
function DateProto_toPrimitive([hint = Value.undefined], { thisValue }) {
const O = thisValue;
if (Type(O) !== 'Object') {
return surroundingAgent.Throw('TypeError', 'NotATypeObject', 'Date', O);
}
let tryFirst;
if (Type(hint) === 'String' && (hint.stringValue() === 'string' || hint.stringValue() === 'default')) {
tryFirst = new Value('string');
} else if (Type(hint) === 'String' && hint.stringValue() === 'number') {
tryFirst = new Value('number');
} else {
return surroundingAgent.Throw('TypeError', 'InvalidHint', hint);
}
return Q(OrdinaryToPrimitive(O, tryFirst));
}
export function BootstrapDatePrototype(realmRec) {
const proto = BootstrapPrototype(realmRec, [
['getDate', DateProto_getDate, 0],
['getDay', DateProto_getDay, 0],
['getFullYear', DateProto_getFullYear, 0],
['getHours', DateProto_getHours, 0],
['getMilliseconds', DateProto_getMilliseconds, 0],
['getMinutes', DateProto_getMinutes, 0],
['getMonth', DateProto_getMonth, 0],
['getSeconds', DateProto_getSeconds, 0],
['getTime', DateProto_getTime, 0],
['getTimezoneOffset', DateProto_getTimezoneOffset, 0],
['getUTCDate', DateProto_getUTCDate, 0],
['getUTCDay', DateProto_getUTCDay, 0],
['getUTCFullYear', DateProto_getUTCFullYear, 0],
['getUTCHours', DateProto_getUTCHours, 0],
['getUTCMilliseconds', DateProto_getUTCMilliseconds, 0],
['getUTCMinutes', DateProto_getUTCMinutes, 0],
['getUTCMonth', DateProto_getUTCMonth, 0],
['getUTCSeconds', DateProto_getUTCSeconds, 0],
['setDate', DateProto_setDate, 1],
['setFullYear', DateProto_setFullYear, 3],
['setHours', DateProto_setHours, 4],
['setMilliseconds', DateProto_setMilliseconds, 1],
['setMinutes', DateProto_setMinutes, 3],
['setMonth', DateProto_setMonth, 2],
['setSeconds', DateProto_setSeconds, 2],
['setTime', DateProto_setTime, 1],
['setUTCDate', DateProto_setUTCDate, 1],
['setUTCFullYear', DateProto_setUTCFullYear, 3],
['setUTCHours', DateProto_setUTCHours, 4],
['setUTCMilliseconds', DateProto_setUTCMilliseconds, 1],
['setUTCMinutes', DateProto_setUTCMinutes, 3],
['setUTCMonth', DateProto_setUTCMonth, 2],
['setUTCSeconds', DateProto_setUTCSeconds, 2],
['toDateString', DateProto_toDateString, 0],
['toISOString', DateProto_toISOString, 0],
['toJSON', DateProto_toJSON, 1],
['toLocaleDateString', DateProto_toLocaleDateString, 0],
['toLocaleString', DateProto_toLocaleString, 0],
['toLocaleTimeString', DateProto_toLocaleTimeString, 0],
['toString', DateProto_toString, 0],
['toTimeString', DateProto_toTimeString, 0],
['toUTCString', DateProto_toUTCString, 0],
['valueOf', DateProto_valueOf, 0],
[wellKnownSymbols.toPrimitive, DateProto_toPrimitive, 1, { Writable: Value.false, Enumerable: Value.false, Configurable: Value.true }],
], realmRec.Intrinsics['%Object.prototype%']);
realmRec.Intrinsics['%Date.prototype%'] = proto;
}
-51
View File
@@ -1,51 +0,0 @@
import {
DefinePropertyOrThrow,
OrdinaryCreateFromConstructor,
ToString,
} from '../abstract-ops/all.mjs';
import {
Descriptor,
Value,
} from '../value.mjs';
import { Q, X } from '../completion.mjs';
import { surroundingAgent } from '../engine.mjs';
import { captureStack } from '../helpers.mjs';
import { BootstrapConstructor } from './Bootstrap.mjs';
// #sec-error-constructor
function ErrorConstructor([message = Value.undefined], { NewTarget }) {
// 1. If NewTarget is undefined, let newTarget be the active function object; else let newTarget be NewTarget.
let newTarget;
if (NewTarget === Value.undefined) {
newTarget = surroundingAgent.activeFunctionObject;
} else {
newTarget = NewTarget;
}
// 2. Let O be ? OrdinaryCreateFromConstructor(newTarget, "%Error.prototype%", « [[ErrorData]] »).
const O = Q(OrdinaryCreateFromConstructor(newTarget, '%Error.prototype%', ['ErrorData']));
// 3. If message is not undefined, then
if (message !== Value.undefined) {
// a. Let msg be ? ToString(message).
const msg = Q(ToString(message));
// b. Let msgDesc be the PropertyDescriptor { [[Value]]: msg, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true }.
const msgDesc = Descriptor({
Value: msg,
Writable: Value.true,
Enumerable: Value.false,
Configurable: Value.true,
});
// c. Perform ! DefinePropertyOrThrow(O, "message", msgDesc).
X(DefinePropertyOrThrow(O, new Value('message'), msgDesc));
}
X(captureStack(O)); // NON-SPEC
// 4. Return O.
return O;
}
export function BootstrapError(realmRec) {
const error = BootstrapConstructor(realmRec, ErrorConstructor, 'Error', 1, realmRec.Intrinsics['%Error.prototype%'], []);
realmRec.Intrinsics['%Error%'] = error;
}
@@ -1,59 +0,0 @@
import {
surroundingAgent,
} from '../engine.mjs';
import {
Type,
Value,
} from '../value.mjs';
import {
Get,
ToString,
} from '../abstract-ops/all.mjs';
import { Q } from '../completion.mjs';
import { BootstrapPrototype } from './Bootstrap.mjs';
// #sec-error.prototype.tostring
function ErrorProto_toString(args, { thisValue }) {
// 1. Let O be this value.
const O = thisValue;
// 2. If Type(O) is not Object, throw a TypeError exception.
if (Type(O) !== 'Object') {
return surroundingAgent.Throw('TypeError', 'NotAnObject', O);
}
// 3. Let name be ? Get(O, "name").
let name = Q(Get(O, new Value('name')));
// 4. If name is undefined, set name to "Error"; otherwise set name to ? ToString(name).
if (name === Value.undefined) {
name = new Value('Error');
} else {
name = Q(ToString(name));
}
// 5. Let msg be ? Get(O, "message").
let msg = Q(Get(O, new Value('message')));
// 6. If msg is undefined, set msg to the empty String; otherwise set msg to ? ToString(msg).
if (msg === Value.undefined) {
msg = new Value('');
} else {
msg = Q(ToString(msg));
}
// 7. If name is the empty String, return msg.
if (name.stringValue() === '') {
return msg;
}
// 8. If msg is the empty String, return name.
if (msg.stringValue() === '') {
return name;
}
// 9. Return the string-concatenation of name, the code unit 0x003A (COLON), the code unit 0x0020 (SPACE), and msg.
return new Value(`${name.stringValue()}: ${msg.stringValue()}`);
}
export function BootstrapErrorPrototype(realmRec) {
const proto = BootstrapPrototype(realmRec, [
['toString', ErrorProto_toString, 0],
['message', new Value('')],
['name', new Value('Error')],
], realmRec.Intrinsics['%Object.prototype%']);
realmRec.Intrinsics['%Error.prototype%'] = proto;
}
@@ -1,42 +0,0 @@
import { surroundingAgent } from '../engine.mjs';
import { Value } from '../value.mjs';
import { IsCallable, OrdinaryCreateFromConstructor } from '../abstract-ops/all.mjs';
import { Q } from '../completion.mjs';
import { BootstrapConstructor } from './Bootstrap.mjs';
// #sec-finalization-registry-cleanup-callback
function FinalizationRegistryConstructor([cleanupCallback = Value.undefined], { NewTarget }) {
// 1. If NewTarget is undefined, throw a TypeError exception.
if (NewTarget === Value.undefined) {
return surroundingAgent.Throw('TypeError', 'NotAFunction', 'FinalizationRegistry');
}
// 2. If IsCallable(cleanupCallback) is false, throw a TypeError exception.
if (IsCallable(cleanupCallback) === Value.false) {
return surroundingAgent.Throw('TypeError', 'NotAFunction', cleanupCallback);
}
// 3. Let finalizationGroup be ? OrdinaryCreateFromConstructor(NewTarget, "%FinalizationRegistryPrototype%", « [[Realm]], [[CleanupCallback]], [[Cells]] »).
const finalizationGroup = Q(OrdinaryCreateFromConstructor(NewTarget, '%FinalizationRegistry.prototype%', [
'Realm',
'CleanupCallback',
'Cells',
]));
// 4. Let fn be the active function object.
const fn = surroundingAgent.activeFunctionObject;
// 5. Set finalizationGroup.[[Realm]] to fn.[[Realm]].
finalizationGroup.Realm = fn.Realm;
// 6. Set finalizationGroup.[[CleanupCallback]] to cleanupCallback.
finalizationGroup.CleanupCallback = cleanupCallback;
// 7. Set finalizationGroup.[[Cells]] to be an empty List.
finalizationGroup.Cells = [];
// 8. Return finalizationGroup.
return finalizationGroup;
}
export function BootstrapFinalizationRegistry(realmRec) {
const cons = BootstrapConstructor(
realmRec, FinalizationRegistryConstructor, 'FinalizationRegistry', 1,
realmRec.Intrinsics['%FinalizationRegistry.prototype%'], [],
);
realmRec.Intrinsics['%FinalizationRegistry%'] = cons;
}
@@ -1,101 +0,0 @@
import { surroundingAgent } from '../engine.mjs';
import { Value, Type } from '../value.mjs';
import {
CleanupFinalizationRegistry,
IsCallable,
RequireInternalSlot,
SameValue,
} from '../abstract-ops/all.mjs';
import { Q } from '../completion.mjs';
import { BootstrapPrototype } from './Bootstrap.mjs';
// #sec-finalization-registry.prototype.cleanupSome
function FinalizationRegistryProto_cleanupSome([callback = Value.undefined], { thisValue }) {
// 1. Let finalizationRegistry be the this value.
const finalizationRegistry = thisValue;
// 2. Perform ? RequireInternalSlot(finalizationRegistry, [[Cells]]).
Q(RequireInternalSlot(finalizationRegistry, 'Cells'));
// 3. If callback is present and IsCallable(callback) is false, throw a TypeError exception.
if (callback !== Value.undefined && IsCallable(callback) === Value.false) {
return surroundingAgent.Throw('TypeError', 'NotAFunction', callback);
}
// 4. Perform ? CleanupFinalizationRegistry(finalizationRegistry, callback).
Q(CleanupFinalizationRegistry(finalizationRegistry, callback));
// 5. Return *undefined*.
return Value.undefined;
}
// #sec-finalization-registry.prototype.register
function FinalizationRegistryProto_register([target = Value.undefined, heldValue = Value.undefined, unregisterToken = Value.undefined], { thisValue }) {
// 1. Let finalizationRegistry be the this value.
const finalizationRegistry = thisValue;
// 2. Perform ? RequireInternalSlot(finalizationRegistry, [[Cells]]).
Q(RequireInternalSlot(finalizationRegistry, 'Cells'));
// 3. If Type(target) is not Object, throw a TypeError exception.
if (Type(target) !== 'Object') {
return surroundingAgent.Throw('TypeError', 'NotAnObject', target);
}
// 4. If SameValue(target, heldValue), throw a TypeError exception.
if (SameValue(target, heldValue) === Value.true) {
return surroundingAgent.Throw('TypeError', 'TargetMatchesHeldValue', heldValue);
}
// 5. If Type(unregisterToken) is not Object,
if (Type(unregisterToken) !== 'Object') {
// a. If unregisterToken is not undefined, throw a TypeError exception.
if (unregisterToken !== Value.undefined) {
return surroundingAgent.Throw('TypeError', 'NotAnObject', unregisterToken);
}
// b. Set unregisterToken to empty.
unregisterToken = undefined;
}
// 6. Let cell be the Record { [[WeakRefTarget]] : target, [[HeldValue]]: heldValue, [[UnregisterToken]]: unregisterToken }.
const cell = {
WeakRefTarget: target,
HeldValue: heldValue,
UnregisterToken: unregisterToken,
};
// 7. Append cell to finalizationRegistry.[[Cells]].
finalizationRegistry.Cells.push(cell);
// 8. Return undefined.
return Value.undefined;
}
// #sec-finalization-registry.prototype.unregister
function FinalizationRegistryProto_unregister([unregisterToken = Value.undefined], { thisValue }) {
// 1. Let finalizationRegistry be the this value.
const finalizationRegistry = thisValue;
// 2. Perform ? RequireInternalSlot(finalizationRegistry, [[Cells]]).
Q(RequireInternalSlot(finalizationRegistry, 'Cells'));
// 3. If Type(unregisterToken) is not Object, throw a TypeError exception.
if (Type(unregisterToken) !== 'Object') {
return surroundingAgent.Throw('TypeError', 'NotAnObject', unregisterToken);
}
// 4. Let removed be false.
let removed = Value.false;
// 5. For each Record { [[WeakRefTarget]], [[HeldValue]], [[UnregisterToken]] } cell that is an element of finalizationRegistry.[[Cells]], do
finalizationRegistry.Cells = finalizationRegistry.Cells.filter((cell) => {
let r = true;
// a. If cell.[[UnregisterToken]] is not empty and SameValue(cell.[[UnregisterToken]], unregisterToken) is true, then
if (cell.UnregisterToken !== undefined && SameValue(cell.UnregisterToken, unregisterToken) === Value.true) {
// i. Remove cell from finalizationRegistry.Cells.
r = false;
// ii. Set removed to true.
removed = Value.true;
}
return r;
});
// 6. Return removed.
return removed;
}
export function BootstrapFinalizationRegistryPrototype(realmRec) {
const proto = BootstrapPrototype(realmRec, [
surroundingAgent.feature('cleanup-some')
? ['cleanupSome', FinalizationRegistryProto_cleanupSome, 0]
: undefined,
['register', FinalizationRegistryProto_register, 2],
['unregister', FinalizationRegistryProto_unregister, 1],
], realmRec.Intrinsics['%Object.prototype%'], 'FinalizationRegistry');
realmRec.Intrinsics['%FinalizationRegistry.prototype%'] = proto;
}
@@ -1,104 +0,0 @@
import { Value, Type } from '../value.mjs';
import { surroundingAgent } from '../engine.mjs';
import {
Assert,
SameValue,
OrdinaryObjectCreate,
CreateIterResultObject,
} from '../abstract-ops/all.mjs';
import { Q } from '../completion.mjs';
import { BootstrapPrototype } from './Bootstrap.mjs';
// #sec-createforiniterator
export function CreateForInIterator(object) {
// 1. Assert: Type(object) is Object.
Assert(Type(object) === 'Object');
// 2. Let iterator be ObjectCreate(%ForInIteratorPrototype%, « [[Object]], [[ObjectWasVisited]], [[VisitedKeys]], [[RemainingKeys]] »).
const iterator = OrdinaryObjectCreate(surroundingAgent.intrinsic('%ForInIteratorPrototype%'), [
'Object',
'ObjectWasVisited',
'VisitedKeys',
'RemainingKeys',
]);
// 3. Set iterator.[[Object]] to object.
iterator.Object = object;
// 4. Set iterator.[[ObjectWasVisited]] to false.
iterator.ObjectWasVisited = Value.false;
// 5. Set iterator.[[VisitedKeys]] to a new empty List.
iterator.VisitedKeys = [];
// 6. Set iterator.[[RemainingKeys]] to a new empty List.
iterator.RemainingKeys = [];
// 7. Return iterator.
return iterator;
}
// #sec-%foriniteratorprototype%.next
function ForInIteratorPrototype_next(args, { thisValue }) {
// 1. Let O be this value.
const O = thisValue;
// 2. Assert: Type(O) is Object.
Assert(Type(O) === 'Object');
// 3. Assert: O has all the internal slot sof a For-In Iterator Instance.
Assert('Object' in O && 'ObjectWasVisited' in O && 'VisitedKeys' in O && 'RemainingKeys in O');
// 4. Let object be O.[[Object]].
let object = O.Object;
// 5. Let visited be O.[[VisitedKeys]].
const visited = O.VisitedKeys;
// 6. Let remaining be O.[[RemainingKeys]].
const remaining = O.RemainingKeys;
// 7. Repeat,
while (true) {
// a. If O.[[ObjectWasVisited]] is false, then
if (O.ObjectWasVisited === Value.false) {
// i. Let keys be ? object.[[OwnPropertyKeys]]().
const keys = Q(object.OwnPropertyKeys());
// ii. for each key of keys in List order, do
for (const key of keys) {
// 1. If Type(key) is String, then
if (Type(key) === 'String') {
// a. Append key to remaining.
remaining.push(key);
}
}
// iii. Set O.ObjectWasVisited to true.
O.ObjectWasVisited = Value.true;
}
// b. Repeat, while remaining is not empty,
while (remaining.length > 0) {
// i. Remove the first element from remaining and let r be the value of the element.
const r = remaining.shift();
// ii. If there does not exist an element v of visisted such that SameValue(r, v) is true, then
if (!visited.find((v) => SameValue(r, v) === Value.true)) {
// 1. Let desc be ? object.[[GetOwnProperty]](r).
const desc = Q(object.GetOwnProperty(r));
// 2. If desc is not undefined, then,
if (desc !== Value.undefined) {
// a. Append r to visited.
visited.push(r);
// b. If desc.[[Enumerable]] is true, return CreateIterResultObject(r, false).
if (desc.Enumerable === Value.true) {
return CreateIterResultObject(r, Value.false);
}
}
}
}
// c. Set object to ? object.[[GetPrototypeOf]]().
object = Q(object.GetPrototypeOf());
// d. Set O.Object to object.
O.Object = object;
// e. Set O.ObjectWasVisited to false.
O.ObjectWasVisited = Value.false;
// f. If object is null, return CreateIterResultObject(undefined, true).
if (object === Value.null) {
return CreateIterResultObject(Value.undefined, Value.true);
}
}
}
export function BootstrapForInIteratorPrototype(realmRec) {
const proto = BootstrapPrototype(realmRec, [
['next', ForInIteratorPrototype_next, 0],
], realmRec.Intrinsics['%IteratorPrototype%']);
realmRec.Intrinsics['%ForInIteratorPrototype%'] = proto;
}
-18
View File
@@ -1,18 +0,0 @@
import { surroundingAgent } from '../engine.mjs';
import { Q } from '../completion.mjs';
import { CreateDynamicFunction } from '../runtime-semantics/all.mjs';
import { BootstrapConstructor } from './Bootstrap.mjs';
// #sec-function-p1-p2-pn-body
function FunctionConstructor(args, { NewTarget }) {
// 1. Let C be the active function object.
const C = surroundingAgent.activeFunctionObject;
// 2. Let args be the argumentsList that was passed to this function by [[Call]] or [[Construct]].
// 3. Return ? CreateDynamicFunction(C, NewTarget, normal, args).
return Q(CreateDynamicFunction(C, NewTarget, 'normal', args));
}
export function BootstrapFunction(realmRec) {
const cons = BootstrapConstructor(realmRec, FunctionConstructor, 'Function', 1, realmRec.Intrinsics['%Function.prototype%'], []);
realmRec.Intrinsics['%Function%'] = cons;
}
@@ -1,240 +0,0 @@
import {
surroundingAgent,
HostHasSourceTextAvailable,
} from '../engine.mjs';
import {
Assert,
Call,
Construct,
CreateListFromArrayLike,
Get,
HasOwnProperty,
IsCallable,
IsConstructor,
OrdinaryHasInstance,
PrepareForTailCall,
SameValue,
SetFunctionLength,
SetFunctionName,
ToInteger,
CreateBuiltinFunction,
MakeBasicObject,
} from '../abstract-ops/all.mjs';
import {
Type,
Value,
wellKnownSymbols,
} from '../value.mjs';
import { Q, X } from '../completion.mjs';
import { assignProps } from './Bootstrap.mjs';
// #sec-properties-of-the-function-prototype-object
function FunctionProto(_args, _meta) {
// * accepts any arguments and returns undefined when invoked.
return Value.undefined;
}
// #sec-function.prototype.apply
function FunctionProto_apply([thisArg = Value.undefined, argArray = Value.undefined], { thisValue }) {
// 1. Let func be the this value.
const func = thisValue;
// 2. If IsCallable(func) is false, throw a TypeError exception.
if (IsCallable(func) === Value.false) {
return surroundingAgent.Throw('TypeError', 'NotAFunction', func);
}
// 3. If argArray is undefined or null, then
if (argArray === Value.undefined || argArray === Value.null) {
// a. Perform PrepareForTailCall().
PrepareForTailCall();
// b. Return ? Call(func, thisArg).
return Q(Call(func, thisArg));
}
// 4. Let argList be ? CreateListFromArrayLike(argArray).
const argList = Q(CreateListFromArrayLike(argArray));
// 5. Perform PrepareForTailCall().
PrepareForTailCall();
// 6. Return ? Call(func, thisArg, argList).
return Q(Call(func, thisArg, argList));
}
function BoundFunctionExoticObjectCall(thisArgument, argumentsList) {
const F = this;
const target = F.BoundTargetFunction;
const boundThis = F.BoundThis;
const boundArgs = F.BoundArguments;
const args = [...boundArgs, ...argumentsList];
return Q(Call(target, boundThis, args));
}
function BoundFunctionExoticObjectConstruct(argumentsList, newTarget) {
const F = this;
const target = F.BoundTargetFunction;
Assert(IsConstructor(target) === Value.true);
const boundArgs = F.BoundArguments;
const args = [...boundArgs, ...argumentsList];
if (SameValue(F, newTarget) === Value.true) {
newTarget = target;
}
return Q(Construct(target, args, newTarget));
}
// #sec-boundfunctioncreate
function BoundFunctionCreate(targetFunction, boundThis, boundArgs) {
// 1. Assert: Type(targetFunction) is Object.
Assert(Type(targetFunction) === 'Object');
// 2. Let proto be ? targetFunction.[[GetPrototypeOf]]().
const proto = Q(targetFunction.GetPrototypeOf());
// 3. Let internalSlotsList be the internal slots listed in Table 30, plus [[Prototype]] and [[Extensible]].
const internalSlotsList = [
'BoundTargetFunction',
'BoundThis',
'BoundArguments',
'Prototype',
'Extensible',
];
// 4. Let obj be ! MakeBasicObject(internalSlotsList).
const obj = X(MakeBasicObject(internalSlotsList));
// 5. Set obj.[[Prototype]] to proto.
obj.Prototype = proto;
// 6. Set obj.[[Call]] as described in 9.4.1.1.
obj.Call = BoundFunctionExoticObjectCall;
// 7. If IsConstructor(targetFunction) is true, then
if (IsConstructor(targetFunction) === Value.true) {
// a. Set obj.[[Construct]] as described in 9.4.1.2.
obj.Construct = BoundFunctionExoticObjectConstruct;
}
// 8. Set obj.[[BoundTargetFunction]] to targetFunction.
obj.BoundTargetFunction = targetFunction;
// 9. Set obj.[[BoundThis]] to boundThis.
obj.BoundThis = boundThis;
// 10. Set obj.[[BoundArguments]] to boundArguments.
obj.BoundArguments = boundArgs;
// 11. Return obj.
return obj;
}
// #sec-function.prototype.bind
function FunctionProto_bind([thisArg = Value.undefined, ...args], { thisValue }) {
// 1. Let Target be the this value.
const Target = thisValue;
// 2. If IsCallable(Target) is false, throw a TypeError exception.
if (IsCallable(Target) === Value.false) {
return surroundingAgent.Throw('TypeError', 'NotAFunction', Target);
}
// 3. Let args be a new (possibly empty) List consisting of all of the argument values provided after thisArg in order.
// 4. Let F be ? BoundFunctionCreate(Target, thisArg, args).
const F = Q(BoundFunctionCreate(Target, thisArg, args));
// 5. Let targetHasLength be ? HasOwnProperty(Target, "length").
const targetHasLength = Q(HasOwnProperty(Target, new Value('length')));
// 6. If targetHasLength is true, then
let L;
if (targetHasLength === Value.true) {
// a. Let targetLen be ? Get(Target, "length").
let targetLen = Q(Get(Target, new Value('length')));
// b. If Type(targetLen) is not Number, let L be 0.
if (Type(targetLen) !== 'Number') {
L = 0;
} else { // c. Else,
// i. Set targetLen to ! ToInteger(targetLen).
targetLen = Q(ToInteger(targetLen)).numberValue();
// ii. Let L be the larger of 0 and the result of targetLen minus the number of elements of args.
L = Math.max(0, targetLen - args.length);
}
} else {
// 7. ELse, let L be 0.
L = 0;
}
// 8. Perform ! SetFunctionLength(F, L).
X(SetFunctionLength(F, new Value(L)));
// 9. Let targetName be ? Get(Target, "name").
let targetName = Q(Get(Target, new Value('name')));
// 10. If Type(targetName) is not String, set targetName to the empty String.
if (Type(targetName) !== 'String') {
targetName = new Value('');
}
// 11. Perform SetFunctionName(F, targetName, "bound").
SetFunctionName(F, targetName, new Value('bound'));
// 12. Return F.
return F;
}
// #sec-function.prototype.call
function FunctionProto_call([thisArg = Value.undefined, ...args], { thisValue }) {
// 1. Let func be the this value.
const func = thisValue;
// 2. If IsCallable(func) is false, throw a TypeError exception.
if (IsCallable(func) === Value.false) {
return surroundingAgent.Throw('TypeError', 'NotAFunction', func);
}
// 3. Let argList be a new empty List.
const argList = [];
// 4. If this method was called with more than one argument, then in left to right order, starting with the second argument, append each argument as the last element of argList.
for (const arg of args) {
argList.push(arg);
}
// 5. Perform PrepareForTailCall().
PrepareForTailCall();
// 6. Return ? Call(func, thisArg, argList).
return Q(Call(func, thisArg, argList));
}
// #sec-function.prototype.tostring
function FunctionProto_toString(args, { thisValue }) {
// 1. Let func be the this value.
const func = thisValue;
// 2. If func is a built-in function object, then return an implementation-defined
// String source code representation of func. The representation must have the
// syntax of a NativeFunction. Additionally, if func has an [[InitialName]] internal
// slot and func.[[InitialName]] is a String, the portion of the returned String
// that would be matched by `NativeFunctionAccessor? PropertyName` must be the
// value of func.[[InitialName]].
if ('nativeFunction' in func) {
if (func.InitialName !== Value.null) {
return new Value(`function ${func.InitialName.stringValue()}() { [native code] }`);
}
return new Value('function() { [native code] }');
}
// 3. If Type(func) is Object and func has a [[SourceText]] internal slot and func.[[SourceText]]
// is a sequence of Unicode code points and ! HostHasSourceTextAvailable(func) is true, then
if (Type(func) === 'Object'
&& 'SourceText' in func
&& X(HostHasSourceTextAvailable(func)) === Value.true) {
// Return ! UTF16Encode(func.[[SourceText]]).
return new Value(func.SourceText);
}
// 4. If Type(func) is Object and IsCallable(func) is true, then return an implementation
// dependent String source code representation of func. The representation must have
// the syntax of a NativeFunction.
if (Type(func) === 'Object' && IsCallable(func) === Value.true) {
return new Value('function() { [native code] }');
}
// 5. Throw a TypeError exception.
return surroundingAgent.Throw('TypeError', 'NotAFunction', func);
}
// #sec-function.prototype-@@hasinstance
function FunctionProto_hasInstance([V = Value.undefined], { thisValue }) {
// 1. Let F be this value.
const F = thisValue;
// 2. Return ? OrdinaryHasInstance(F, V).
return Q(OrdinaryHasInstance(F, V));
}
export function BootstrapFunctionPrototype(realmRec) {
const proto = CreateBuiltinFunction(FunctionProto, [], realmRec, realmRec.Intrinsics['%Object.prototype%']);
realmRec.Intrinsics['%Function.prototype%'] = proto;
SetFunctionLength(proto, new Value(0));
SetFunctionName(proto, new Value(''));
const readonly = { Writable: Value.false, Configurable: Value.false };
assignProps(realmRec, proto, [
['apply', FunctionProto_apply, 2],
['bind', FunctionProto_bind, 1],
['call', FunctionProto_call, 1],
['toString', FunctionProto_toString, 0],
[wellKnownSymbols.hasInstance, FunctionProto_hasInstance, 1, readonly],
]);
}
-21
View File
@@ -1,21 +0,0 @@
import { Descriptor, Value } from '../value.mjs';
import { DefinePropertyOrThrow } from '../abstract-ops/all.mjs';
import { X } from '../completion.mjs';
import { BootstrapPrototype } from './Bootstrap.mjs';
export function BootstrapGenerator(realmRec) {
const generatorPrototype = realmRec.Intrinsics['%Generator.prototype%'];
const generator = BootstrapPrototype(realmRec, [
['prototype', generatorPrototype, undefined, { Writable: Value.false }],
], realmRec.Intrinsics['%Function.prototype%'], 'GeneratorFunction');
X(DefinePropertyOrThrow(generatorPrototype, new Value('constructor'), Descriptor({
Value: generator,
Writable: Value.false,
Enumerable: Value.false,
Configurable: Value.true,
})));
realmRec.Intrinsics['%Generator%'] = generator;
}
@@ -1,30 +0,0 @@
import { surroundingAgent } from '../engine.mjs';
import { Descriptor, Value } from '../value.mjs';
import { DefinePropertyOrThrow } from '../abstract-ops/all.mjs';
import { Q, X } from '../completion.mjs';
import { CreateDynamicFunction } from '../runtime-semantics/all.mjs';
import { BootstrapConstructor } from './Bootstrap.mjs';
// #sec-generatorfunction
function GeneratorFunctionConstructor(args, { NewTarget }) {
// 1. Let C be the active function object.
const C = surroundingAgent.activeFunctionObject;
// 2. Let args be the argumentsList that was passed to this function by [[Call]] or [[Construct]].
// 3. Return ? CreateDynamicFunction(C, NewTarget, generator, args).
return Q(CreateDynamicFunction(C, NewTarget, 'generator', args));
}
export function BootstrapGeneratorFunction(realmRec) {
const generator = realmRec.Intrinsics['%Generator%'];
const cons = BootstrapConstructor(realmRec, GeneratorFunctionConstructor, 'GeneratorFunction', 1, generator, []);
X(DefinePropertyOrThrow(cons, new Value('prototype'), Descriptor({
Writable: Value.false,
Configurable: Value.false,
})));
X(DefinePropertyOrThrow(generator, new Value('constructor'), Descriptor({
Writable: Value.false,
})));
realmRec.Intrinsics['%GeneratorFunction%'] = cons;
}
@@ -1,49 +0,0 @@
import {
GeneratorResume,
GeneratorResumeAbrupt,
} from '../abstract-ops/all.mjs';
import {
Completion,
ThrowCompletion,
Q,
} from '../completion.mjs';
import { Value } from '../value.mjs';
import { BootstrapPrototype } from './Bootstrap.mjs';
// #sec-generator.prototype.next
function GeneratorProto_next([value = Value.undefined], { thisValue }) {
// 1. Let g be the this value.
const g = thisValue;
// 2. Return ? GeneratorResume(g, value).
return Q(GeneratorResume(g, value));
}
// #sec-generator.prototype.return
function GeneratorProto_return([value = Value.undefined], { thisValue }) {
// 1. Let g be the this value.
const g = thisValue;
// 2. Let C be Completion { [[Type]]: return, [[Value]]: value, [[Target]]: empty }.
const C = new Completion({ Type: 'return', Value: value, Target: undefined });
// 3. Return ? GeneratorResumeAbrupt(g, C).
return Q(GeneratorResumeAbrupt(g, C));
}
// #sec-generator.prototype.throw
function GeneratorProto_throw([exception = Value.undefined], { thisValue }) {
// 1. Let g be the this value.
const g = thisValue;
// 2. Let C be ThrowCompletion(exception).
const C = ThrowCompletion(exception);
// 3. Return ? GeneratorResumeAbrupt(g, C).
return Q(GeneratorResumeAbrupt(g, C));
}
export function BootstrapGeneratorPrototype(realmRec) {
const generatorPrototype = BootstrapPrototype(realmRec, [
['next', GeneratorProto_next, 1],
['return', GeneratorProto_return, 1],
['throw', GeneratorProto_throw, 1],
], realmRec.Intrinsics['%IteratorPrototype%'], 'Generator');
realmRec.Intrinsics['%Generator.prototype%'] = generatorPrototype;
}
@@ -1,16 +0,0 @@
import { wellKnownSymbols } from '../value.mjs';
import { BootstrapPrototype } from './Bootstrap.mjs';
// #sec-%iteratorprototype%-@@iterator
function IteratorPrototype_iterator(args, { thisValue }) {
// 1. Return this value.
return thisValue;
}
export function BootstrapIteratorPrototype(realmRec) {
const proto = BootstrapPrototype(realmRec, [
[wellKnownSymbols.iterator, IteratorPrototype_iterator, 0],
], realmRec.Intrinsics['%Object.prototype%']);
realmRec.Intrinsics['%IteratorPrototype%'] = proto;
}
-548
View File
@@ -1,548 +0,0 @@
import { surroundingAgent } from '../engine.mjs';
import {
BooleanValue,
NullValue,
NumberValue,
ObjectValue,
JSStringValue,
Type,
Value,
} from '../value.mjs';
import {
Assert,
Call,
CreateDataProperty,
CreateDataPropertyOrThrow,
EnumerableOwnPropertyNames,
Get,
GetV,
IsArray,
IsCallable,
OrdinaryObjectCreate,
LengthOfArrayLike,
ToInteger,
ToNumber,
ToString,
} from '../abstract-ops/all.mjs';
import {
isLeadingSurrogate,
isTrailingSurrogate,
} from '../parser/Lexer.mjs';
import {
CodePointToUTF16CodeUnits,
} from '../static-semantics/all.mjs';
import {
NormalCompletion,
Q, X,
} from '../completion.mjs';
import { ValueSet } from '../helpers.mjs';
import { evaluateScript } from '../api.mjs';
import { BootstrapPrototype } from './Bootstrap.mjs';
const WHITESPACE = [' ', '\t', '\r', '\n'];
const NUMERIC = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
const VALID_HEX = [...NUMERIC, 'A', 'B', 'C', 'D', 'E', 'F', 'a', 'b', 'c', 'd', 'e', 'f'];
const ESCAPABLE = ['"', '\\', '/', 'b', 'f', 'n', 'r', 't'];
class JSONValidator {
constructor(input) {
this.input = input;
this.pos = 0;
this.char = input.charAt(0);
}
validate() {
X(this.eatWhitespace());
Q(this.parseValue());
if (this.pos < this.input.length) {
return surroundingAgent.Throw('SyntaxError', 'JSONUnexpectedToken');
}
return NormalCompletion(undefined);
}
advance() {
this.pos += 1;
if (this.pos === this.input.length) {
this.char = null;
} else if (this.pos > this.input.length) {
return surroundingAgent.Throw('SyntaxError', 'JSONUnexpectedToken');
} else {
this.char = this.input.charAt(this.pos);
}
return this.char;
}
eatWhitespace() {
while (this.eat(WHITESPACE)) {
// nothing
}
}
eat(c) {
if (Array.isArray(c) && c.includes(this.char)) {
X(this.advance());
return true;
} else if (this.char === c) {
X(this.advance());
return true;
}
return false;
}
expect(c) {
const { char } = this;
if (!this.eat(c)) {
return surroundingAgent.Throw('SyntaxError', 'JSONExpected', c, this.char);
}
return char;
}
parseValue() {
switch (this.char) {
case '"':
return Q(this.parseString());
case '{':
return Q(this.parseObject());
case '[':
return Q(this.parseArray());
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '-':
return Q(this.parseNumber());
case 'f':
X(this.expect('f'));
Q(this.expect('a'));
Q(this.expect('l'));
Q(this.expect('s'));
Q(this.expect('e'));
return X(this.eatWhitespace());
case 't':
X(this.expect('t'));
Q(this.expect('r'));
Q(this.expect('u'));
Q(this.expect('e'));
return X(this.eatWhitespace());
case 'n':
X(this.expect('n'));
Q(this.expect('u'));
Q(this.expect('l'));
Q(this.expect('l'));
return X(this.eatWhitespace());
default:
return surroundingAgent.Throw('SyntaxError', 'JSONUnexpectedChar', this.char);
}
}
parseString() {
Q(this.expect('"'));
while (!this.eat('"')) {
if (this.eat('\\')) {
if (!this.eat(ESCAPABLE)) {
Q(this.expect('u'));
Q(this.expect(VALID_HEX));
Q(this.expect(VALID_HEX));
Q(this.expect(VALID_HEX));
Q(this.expect(VALID_HEX));
}
} else {
if (this.char < ' ') {
return surroundingAgent.Throw('SyntaxError', 'JSONUnexpectedChar', this.char);
}
Q(this.advance());
}
}
return X(this.eatWhitespace());
}
parseNumber() {
this.eat('-');
if (!this.eat('0')) {
Q(this.expect(NUMERIC));
while (this.eat(NUMERIC)) {
// nothing
}
}
if (this.eat('.')) {
Q(this.expect(NUMERIC));
while (this.eat(NUMERIC)) {
// nothing
}
}
if (this.eat(['e', 'E'])) {
this.eat(['-', '+']);
Q(this.expect(NUMERIC));
while (this.eat(NUMERIC)) {
// nothing
}
}
X(this.eatWhitespace());
}
parseObject() {
Q(this.expect('{'));
X(this.eatWhitespace());
let first = true;
while (!this.eat('}')) {
if (first) {
first = false;
} else {
Q(this.expect(','));
X(this.eatWhitespace());
}
Q(this.parseString());
X(this.eatWhitespace());
Q(this.expect(':'));
X(this.eatWhitespace());
Q(this.parseValue());
X(this.eatWhitespace());
}
X(this.eatWhitespace());
}
parseArray() {
Q(this.expect('['));
X(this.eatWhitespace());
let first = true;
while (!this.eat(']')) {
if (first) {
first = false;
} else {
Q(this.expect(','));
X(this.eatWhitespace());
}
Q(this.parseValue());
X(this.eatWhitespace());
}
X(this.eatWhitespace());
}
static validate(input) {
const v = new JSONValidator(input);
return v.validate();
}
}
function InternalizeJSONProperty(holder, name, reviver) {
const val = Q(Get(holder, name));
if (Type(val) === 'Object') {
const isArray = Q(IsArray(val));
if (isArray === Value.true) {
let I = 0;
const len = Q(LengthOfArrayLike(val)).numberValue();
while (I < len) {
const Istr = X(ToString(new Value(I)));
const newElement = Q(InternalizeJSONProperty(val, Istr, reviver));
if (Type(newElement) === 'Undefined') {
Q(val.Delete(Istr));
} else {
Q(CreateDataProperty(val, Istr, newElement));
}
I += 1;
}
} else {
const keys = Q(EnumerableOwnPropertyNames(val, 'key'));
for (const P of keys) {
const newElement = Q(InternalizeJSONProperty(val, P, reviver));
if (Type(newElement) === 'Undefined') {
Q(val.Delete(P));
} else {
Q(CreateDataProperty(val, P, newElement));
}
}
}
}
return Q(Call(reviver, holder, [name, val]));
}
// #sec-json.parse
function JSON_parse([text = Value.undefined, reviver = Value.undefined]) {
// 1. Let jsonString be ? ToString(text).
const jsonString = Q(ToString(text));
// 2. Parse ! UTF16DecodeString(jsonString) as a JSON text as specified in ECMA-404.
// Throw a SyntaxError exception if it is not a valid JSON text as defined in that specification.
Q(JSONValidator.validate(jsonString.stringValue()));
// 3. Let scriptString be the string-concatenation of "(", jsonString, and ");".
const scriptString = `(${jsonString.stringValue()});`;
// 4. Let completion be the result of parsing and evaluating
// ! UTF16DecodeString(scriptString) as if it was the source text of an ECMAScript Script. The
// extended PropertyDefinitionEvaluation semantics defined in B.3.1 must not be used during the evaluation.
const completion = evaluateScript(scriptString, surroundingAgent.currentRealmRecord);
// 5. Let unfiltered be completion.[[Value]].
const unfiltered = completion.Value;
// 6. Assert: unfiltered is either a String, Number, Boolean, Null, or an Object that is defined by either an ArrayLiteral or an ObjectLiteral.
Assert(unfiltered instanceof JSStringValue
|| unfiltered instanceof NumberValue
|| unfiltered instanceof BooleanValue
|| unfiltered instanceof NullValue
|| unfiltered instanceof ObjectValue);
// 7. If IsCallable(reviver) is true, then
if (IsCallable(reviver) === Value.true) {
// a. Let root be OrdinaryObjectCreate(%Object.prototype%).
const root = OrdinaryObjectCreate(surroundingAgent.intrinsic('%Object.prototype%'));
// b. Let rootName be the empty String.
const rootName = new Value('');
// c. Perform ! CreateDataPropertyOrThrow(root, rootName, unfiltered).
X(CreateDataPropertyOrThrow(root, rootName, unfiltered));
// d. Return ? InternalizeJSONProperty(root, rootName, reviver).
return Q(InternalizeJSONProperty(root, rootName, reviver));
} else {
// a. Return unfiltered.
return unfiltered;
}
}
const codeUnitTable = new Map([
[0x0008, '\\b'],
[0x0009, '\\t'],
[0x000A, '\\n'],
[0x000C, '\\f'],
[0x000D, '\\r'],
[0x0022, '\\"'],
[0x005C, '\\\\'],
]);
// #sec-serializejsonproperty
function SerializeJSONProperty(state, key, holder) {
let value = Q(Get(holder, key)); // eslint-disable-line no-shadow
if (Type(value) === 'Object' || Type(value) === 'BigInt') {
const toJSON = Q(GetV(value, new Value('toJSON')));
if (IsCallable(toJSON) === Value.true) {
value = Q(Call(toJSON, value, [key]));
}
}
if (state.ReplacerFunction !== Value.undefined) {
value = Q(Call(state.ReplacerFunction, holder, [key, value]));
}
if (Type(value) === 'Object') {
if ('NumberData' in value) {
value = Q(ToNumber(value));
} else if ('StringData' in value) {
value = Q(ToString(value));
} else if ('BooleanData' in value) {
value = value.BooleanData;
} else if ('BigIntData' in value) {
value = value.BigIntData;
}
}
if (value === Value.null) {
return new Value('null');
}
if (value === Value.true) {
return new Value('true');
}
if (value === Value.false) {
return new Value('false');
}
if (Type(value) === 'String') {
return QuoteJSONString(value);
}
if (Type(value) === 'Number') {
if (value.isFinite()) {
return X(ToString(value));
}
return new Value('null');
}
if (Type(value) === 'BigInt') {
return surroundingAgent.Throw('TypeError', 'CannotJSONSerializeBigInt');
}
if (Type(value) === 'Object' && IsCallable(value) === Value.false) {
const isArray = Q(IsArray(value));
if (isArray === Value.true) {
return Q(SerializeJSONArray(state, value));
}
return Q(SerializeJSONObject(state, value));
}
return Value.undefined;
}
function UnicodeEscape(C) {
const n = C.charCodeAt(0);
Assert(n < 0xFFFF);
return `\u005Cu${n.toString(16).padStart(4, '0')}`;
}
function QuoteJSONString(value) { // eslint-disable-line no-shadow
let product = '\u0022';
const cpList = [...value.stringValue()].map((c) => c.codePointAt(0));
for (const C of cpList) {
if (codeUnitTable.has(C)) {
product = `${product}${codeUnitTable.get(C)}`;
} else if (C < 0x0020 || isLeadingSurrogate(C) || isTrailingSurrogate(C)) {
const unit = String.fromCodePoint(C);
product = `${product}${UnicodeEscape(unit)}`;
} else {
product = `${product}${String.fromCodePoint(...CodePointToUTF16CodeUnits(C))}`;
}
}
product = `${product}\u0022`;
return new Value(product);
}
// #sec-serializejsonobject
function SerializeJSONObject(state, value) {
if (state.Stack.includes(value)) {
return surroundingAgent.Throw('TypeError', 'JSONCircular');
}
state.Stack.push(value);
const stepback = state.Indent;
state.Indent = `${state.Indent}${state.Gap}`;
let K;
if (state.PropertyList !== Value.undefined) {
K = state.PropertyList;
} else {
K = Q(EnumerableOwnPropertyNames(value, 'key'));
}
const partial = [];
for (const P of K) {
const strP = Q(SerializeJSONProperty(state, P, value));
if (strP !== Value.undefined) {
let member = QuoteJSONString(P).stringValue();
member = `${member}:`;
if (state.Gap !== '') {
member = `${member} `;
}
member = `${member}${strP.stringValue()}`;
partial.push(member);
}
}
let final;
if (partial.length === 0) {
final = new Value('{}');
} else {
if (state.Gap === '') {
const properties = partial.join(',');
final = new Value(`{${properties}}`);
} else {
const separator = `,\u000A${state.Indent}`;
const properties = partial.join(separator);
final = new Value(`{\u000A${state.Indent}${properties}\u000A${stepback}}`);
}
}
state.Stack.pop();
state.Indent = stepback;
return final;
}
// #sec-serializejsonarray
function SerializeJSONArray(state, value) {
if (state.Stack.includes(value)) {
return surroundingAgent.Throw('TypeError', 'JSONCircular');
}
state.Stack.push(value);
const stepback = state.Indent;
state.Indent = `${state.Indent}${state.Gap}`;
const partial = [];
const len = Q(LengthOfArrayLike(value)).numberValue();
let index = 0;
while (index < len) {
const indexStr = X(ToString(new Value(index)));
const strP = Q(SerializeJSONProperty(state, indexStr, value));
if (strP === Value.undefined) {
partial.push('null');
} else {
partial.push(strP.stringValue());
}
index += 1;
}
let final;
if (partial.length === 0) {
final = new Value('[]');
} else {
if (state.Gap === '') {
const properties = partial.join(',');
final = new Value(`[${properties}]`);
} else {
const separator = `,\u000A${state.Indent}`;
const properties = partial.join(separator);
final = new Value(`[\u000A${state.Indent}${properties}\u000A${stepback}]`);
}
}
state.Stack.pop();
state.Indent = stepback;
return final;
}
// #sec-json.stringify
function JSON_stringify([value = Value.undefined, replacer = Value.undefined, space = Value.undefined]) {
const stack = [];
const indent = '';
let PropertyList = Value.undefined;
let ReplacerFunction = Value.undefined;
if (Type(replacer) === 'Object') {
if (IsCallable(replacer) === Value.true) {
ReplacerFunction = replacer;
} else {
const isArray = Q(IsArray(replacer));
if (isArray === Value.true) {
PropertyList = new ValueSet();
const len = Q(LengthOfArrayLike(replacer)).numberValue();
let k = 0;
while (k < len) {
const vStr = X(ToString(new Value(k)));
const v = Q(Get(replacer, vStr));
let item = Value.undefined;
if (Type(v) === 'String') {
item = v;
} else if (Type(v) === 'Number') {
item = X(ToString(v));
} else if (Type(v) === 'Object') {
if ('StringData' in v || 'NumberData' in v) {
item = Q(ToString(v));
}
}
if (item !== Value.undefined && !PropertyList.has(item)) {
PropertyList.add(item);
}
k += 1;
}
}
}
}
if (Type(space) === 'Object') {
if ('NumberData' in space) {
space = Q(ToNumber(space));
} else if ('StringData' in space) {
space = Q(ToString(space));
}
}
let gap;
if (Type(space) === 'Number') {
space = Math.min(10, X(ToInteger(space)).numberValue());
if (space < 1) {
gap = '';
} else {
gap = ' '.repeat(space);
}
} else if (Type(space) === 'String') {
if (space.stringValue().length <= 10) {
gap = space.stringValue();
} else {
gap = space.stringValue().slice(0, 10);
}
} else {
gap = '';
}
const wrapper = OrdinaryObjectCreate(surroundingAgent.intrinsic('%Object.prototype%'));
X(CreateDataPropertyOrThrow(wrapper, new Value(''), value));
const state = {
ReplacerFunction, Stack: stack, Indent: indent, Gap: gap, PropertyList,
};
return Q(SerializeJSONProperty(state, new Value(''), wrapper));
}
export function BootstrapJSON(realmRec) {
const json = BootstrapPrototype(realmRec, [
['parse', JSON_parse, 2],
['stringify', JSON_stringify, 3],
], realmRec.Intrinsics['%Object.prototype%'], 'JSON');
realmRec.Intrinsics['%JSON%'] = json;
}
-87
View File
@@ -1,87 +0,0 @@
import { surroundingAgent } from '../engine.mjs';
import {
Assert,
Call,
Get,
GetIterator,
IsCallable,
IteratorClose,
IteratorStep,
IteratorValue,
OrdinaryCreateFromConstructor,
} from '../abstract-ops/all.mjs';
import {
Type,
Value,
wellKnownSymbols,
} from '../value.mjs';
import {
AbruptCompletion,
Q,
} from '../completion.mjs';
import { BootstrapConstructor } from './Bootstrap.mjs';
export function AddEntriesFromIterable(target, iterable, adder) {
if (IsCallable(adder) === Value.false) {
return surroundingAgent.Throw('TypeError', 'NotAFunction', adder);
}
Assert(iterable !== undefined && iterable !== Value.undefined && iterable !== Value.null);
const iteratorRecord = Q(GetIterator(iterable));
while (true) {
const next = Q(IteratorStep(iteratorRecord));
if (next === Value.false) {
return target;
}
const nextItem = Q(IteratorValue(next));
if (Type(nextItem) !== 'Object') {
const error = surroundingAgent.Throw('TypeError', 'NotAnObject', nextItem);
return Q(IteratorClose(iteratorRecord, error));
}
const k = Get(nextItem, new Value('0'));
if (k instanceof AbruptCompletion) {
return Q(IteratorClose(iteratorRecord, k));
}
const v = Get(nextItem, new Value('1'));
if (v instanceof AbruptCompletion) {
return Q(IteratorClose(iteratorRecord, v));
}
const status = Call(adder, target, [k.Value, v.Value]);
if (status instanceof AbruptCompletion) {
return Q(IteratorClose(iteratorRecord, status));
}
}
}
// #sec-map-iterable
function MapConstructor([iterable = Value.undefined], { NewTarget }) {
// 1. If NewTarget is undefined, throw a TypeError exception.
if (NewTarget === Value.undefined) {
return surroundingAgent.Throw('TypeError', 'ConstructorNonCallable', this);
}
// 2. Let map be ? OrdinaryCreateFromConstructor(NewTarget, "%Map.prototype%", « [[MapData]] »).
const map = Q(OrdinaryCreateFromConstructor(NewTarget, '%Map.prototype%', ['MapData']));
// 3. Set map.[[MapData]] to a new empty List.
map.MapData = [];
// 4. If iterable is either undefined or null, return map.
if (iterable === Value.undefined || iterable === Value.null) {
return map;
}
// 5. Let adder be ? Get(map, "set").
const adder = Q(Get(map, new Value('set')));
// 6. Return ? AddEntriesFromIterable(map, iterable, adder).
return Q(AddEntriesFromIterable(map, iterable, adder));
}
// #sec-get-map-@@species
function Map_speciesGetter(args, { thisValue }) {
// 1. Return the this value.
return thisValue;
}
export function BootstrapMap(realmRec) {
const mapConstructor = BootstrapConstructor(realmRec, MapConstructor, 'Map', 0, realmRec.Intrinsics['%Map.prototype%'], [
[wellKnownSymbols.species, [Map_speciesGetter]],
]);
realmRec.Intrinsics['%Map%'] = mapConstructor;
}

Some files were not shown because too many files have changed in this diff Show More