mirror of
https://github.com/bendtherules/completion-viz.git
synced 2026-08-18 13:42:51 +00:00
Install old engine262 in src/engine262
New version was not returning completion records easily
This commit is contained in:
@@ -0,0 +1,4 @@
|
|||||||
|
!.eslintrc.js
|
||||||
|
*-gen.mjs
|
||||||
|
test/test262/test262
|
||||||
|
test/json/JSONTestSuite
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const Module = require('module');
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-underscore-dangle
|
||||||
|
const ModuleFindPath = Module._findPath;
|
||||||
|
const hacks = [
|
||||||
|
'eslint-plugin-engine262',
|
||||||
|
];
|
||||||
|
// eslint-disable-next-line no-underscore-dangle
|
||||||
|
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 = {
|
||||||
|
extends: 'airbnb-base',
|
||||||
|
plugins: ['engine262'],
|
||||||
|
parser: 'babel-eslint',
|
||||||
|
parserOptions: {
|
||||||
|
ecmaVersion: 2019,
|
||||||
|
},
|
||||||
|
overrides: [
|
||||||
|
{
|
||||||
|
files: ['*.js'],
|
||||||
|
parserOptions: { sourceType: 'script' },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
globals: {
|
||||||
|
BigInt: false,
|
||||||
|
Atomics: false,
|
||||||
|
SharedArrayBuffer: false,
|
||||||
|
WeakRef: false,
|
||||||
|
globalThis: false,
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
'arrow-parens': ['error', 'always'],
|
||||||
|
'brace-style': ['error', '1tbs', { allowSingleLine: false }],
|
||||||
|
'curly': ['error', 'all'],
|
||||||
|
'engine262/no-use-in-def': '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', 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-constant-condition': 'off',
|
||||||
|
'no-continue': 'off',
|
||||||
|
'no-else-return': 'off',
|
||||||
|
'no-lonely-if': 'off',
|
||||||
|
'no-param-reassign': 'off',
|
||||||
|
'no-restricted-syntax': 'off',
|
||||||
|
'no-use-before-define': 'off',
|
||||||
|
'prefer-destructuring': 'off',
|
||||||
|
},
|
||||||
|
};
|
||||||
Vendored
+12
@@ -0,0 +1,12 @@
|
|||||||
|
# These are supported funding model platforms
|
||||||
|
|
||||||
|
github: [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
@@ -0,0 +1,55 @@
|
|||||||
|
name: Publish Package
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
|
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}}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
test.mjs
|
||||||
|
.eslintcache
|
||||||
|
coverage
|
||||||
|
.nyc_output
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
[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
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
test
|
||||||
|
src
|
||||||
|
scripts
|
||||||
|
coverage
|
||||||
|
rollup.config.js
|
||||||
|
.eslintcache
|
||||||
|
.eslintrc.js
|
||||||
|
.eslintignore
|
||||||
|
.travis.yml
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
package-lock=false
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
# 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/
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
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.
|
||||||
@@ -0,0 +1,149 @@
|
|||||||
|
# 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
|
||||||
|
@@ -416,6 +416,9 @@ function* Inner_Evaluate_Expression(Expression) {
|
||||||
|
case isExpressionWithComma(Expression):
|
||||||
|
return yield* Evaluate_ExpressionWithComma(Expression);
|
||||||
|
|
||||||
|
+ case Expression.type === 'DoExpression':
|
||||||
|
+ return yield* Evaluate_BlockStatement(Expression.body);
|
||||||
|
+
|
||||||
|
default:
|
||||||
|
throw new OutOfRange('Evaluate_Expression', Expression);
|
||||||
|
}
|
||||||
|
--- a/src/parse.mjs
|
||||||
|
+++ b/src/parse.mjs
|
||||||
|
@@ -11,6 +11,17 @@ const Parser = acorn.Parser.extend((P) => class Parse262 extends P {
|
||||||
|
node.source = () => this.input.slice(node.start, node.end);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
+
|
||||||
|
+ parseExprAtom(refDestructuringErrors) {
|
||||||
|
+ if (this.value === 'do') {
|
||||||
|
+ // DoExpression : `do` Block
|
||||||
|
+ this.next();
|
||||||
|
+ const node = this.startNode();
|
||||||
|
+ node.body = this.parseBlock();
|
||||||
|
+ return this.finishNode(node, 'DoExpression');
|
||||||
|
+ }
|
||||||
|
+ return super.parseExprAtom(refDestructuringErrors);
|
||||||
|
+ }
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
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, Realm } = require('engine262');
|
||||||
|
|
||||||
|
const agent = new Agent({
|
||||||
|
// onDebugger() {},
|
||||||
|
// ensureCanCompileStrings() {},
|
||||||
|
// hasSourceTextAvailable() {},
|
||||||
|
// onNodeEvaluation() {},
|
||||||
|
// features: [],
|
||||||
|
});
|
||||||
|
agent.enter();
|
||||||
|
|
||||||
|
const realm = new Realm({
|
||||||
|
// promiseRejectionTracker() {},
|
||||||
|
// resolveImportedModule() {},
|
||||||
|
// getImportMetaProperties() {},
|
||||||
|
// finalizeImportMeta() {},
|
||||||
|
});
|
||||||
|
|
||||||
|
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
|
||||||
|
[nodejs/node#25221]: https://github.com/nodejs/node/issues/25221
|
||||||
|
[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
|
||||||
Executable
+202
@@ -0,0 +1,202 @@
|
|||||||
|
#!/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 {
|
||||||
|
inspect,
|
||||||
|
Agent,
|
||||||
|
Completion,
|
||||||
|
AbruptCompletion,
|
||||||
|
Value,
|
||||||
|
Object: APIObject,
|
||||||
|
Abstract,
|
||||||
|
Throw,
|
||||||
|
FEATURES,
|
||||||
|
} = 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) {
|
||||||
|
FEATURES.forEach(({ name, url }) => {
|
||||||
|
process.stdout.write(`${name} - ${url}\n`);
|
||||||
|
});
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
let features;
|
||||||
|
if (argv.features === 'all') {
|
||||||
|
features = FEATURES.map((f) => f.name);
|
||||||
|
} else if (argv.features) {
|
||||||
|
features = argv.features.split(',');
|
||||||
|
} else {
|
||||||
|
features = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const agent = new Agent({ features });
|
||||||
|
agent.enter();
|
||||||
|
|
||||||
|
const { realm, resolverCache } = createRealm({ printCompatMode: true });
|
||||||
|
{
|
||||||
|
const console = new APIObject(realm);
|
||||||
|
Abstract.CreateDataProperty(realm.global, new Value(realm, 'console'), console);
|
||||||
|
|
||||||
|
const format = (args) => args.map((a, i) => {
|
||||||
|
if (i === 0 && Abstract.Type(a) === 'String') {
|
||||||
|
return a.stringValue();
|
||||||
|
}
|
||||||
|
return inspect(a, realm);
|
||||||
|
}).join(' ');
|
||||||
|
|
||||||
|
const log = new Value(realm, (args) => {
|
||||||
|
process.stdout.write(`${format(args)}\n`);
|
||||||
|
return Value.undefined;
|
||||||
|
});
|
||||||
|
|
||||||
|
Abstract.CreateDataProperty(console, new Value(realm, 'log'), log);
|
||||||
|
|
||||||
|
const error = new Value(realm, (args) => {
|
||||||
|
process.stderr.write(`${format(args)}\n`);
|
||||||
|
return Value.undefined;
|
||||||
|
});
|
||||||
|
|
||||||
|
Abstract.CreateDataProperty(console, new Value(realm, 'error'), error);
|
||||||
|
|
||||||
|
const debug = new Value(realm, (args) => {
|
||||||
|
process.stderr.write(`${util.format(...args)}\n`);
|
||||||
|
return Value.undefined;
|
||||||
|
});
|
||||||
|
|
||||||
|
Abstract.CreateDataProperty(console, new Value(realm, 'debug'), debug);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argv.inspector) {
|
||||||
|
const inspector = require('../inspector');
|
||||||
|
inspector.attachRealm(realm);
|
||||||
|
}
|
||||||
|
|
||||||
|
function oneShotEval(source, filename) {
|
||||||
|
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(realm, result.PromiseResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result = realm.evaluateScript(source, { specifier: filename });
|
||||||
|
}
|
||||||
|
if (result instanceof AbruptCompletion) {
|
||||||
|
let inspected;
|
||||||
|
if (Abstract.Type(result.Value) === 'Object') {
|
||||||
|
const errorToString = realm.realm.Intrinsics['%Error.prototype%'].properties.get(new Value(realm, 'toString')).Value;
|
||||||
|
inspected = Abstract.Call(errorToString, result.Value).stringValue();
|
||||||
|
} else {
|
||||||
|
inspected = inspect(result, realm);
|
||||||
|
}
|
||||||
|
process.stderr.write(`${inspected}\n`);
|
||||||
|
process.exit(1);
|
||||||
|
} else {
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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: process.cwd() });
|
||||||
|
callback(null, result);
|
||||||
|
} catch (e) {
|
||||||
|
callback(e, null);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
preview: false,
|
||||||
|
completer: () => [],
|
||||||
|
writer: (o) => {
|
||||||
|
if (o instanceof Value || o instanceof Completion) {
|
||||||
|
return inspect(o, realm);
|
||||||
|
}
|
||||||
|
return util.inspect(o);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
'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;
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const path = require('path');
|
||||||
|
const fs = require('fs');
|
||||||
|
const {
|
||||||
|
Realm, Throw,
|
||||||
|
Abstract, Value,
|
||||||
|
Object: APIObject,
|
||||||
|
ToString,
|
||||||
|
AbruptCompletion,
|
||||||
|
inspect,
|
||||||
|
} = require('..');
|
||||||
|
|
||||||
|
const createRealm = ({ printCompatMode = false } = {}) => {
|
||||||
|
const resolverCache = new Map();
|
||||||
|
const trackedPromises = new Set();
|
||||||
|
|
||||||
|
const realm = new Realm({
|
||||||
|
promiseRejectionTracker(promise, operation) {
|
||||||
|
switch (operation) {
|
||||||
|
case 'reject':
|
||||||
|
trackedPromises.add(promise);
|
||||||
|
break;
|
||||||
|
case 'handle':
|
||||||
|
trackedPromises.delete(promise);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new RangeError('promiseRejectionTracker', operation);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
resolveImportedModule(referencingScriptOrModule, specifier) {
|
||||||
|
try {
|
||||||
|
const base = path.dirname(referencingScriptOrModule.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(realm, e.name, e.message);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const $262 = new APIObject(realm);
|
||||||
|
|
||||||
|
let printHandle;
|
||||||
|
const setPrintHandle = (f) => {
|
||||||
|
printHandle = f;
|
||||||
|
};
|
||||||
|
Abstract.CreateDataProperty(realm.global, new Value(realm, 'print'), new Value(realm, (args) => {
|
||||||
|
if (printHandle !== undefined) {
|
||||||
|
printHandle(...args);
|
||||||
|
} else {
|
||||||
|
if (printCompatMode) {
|
||||||
|
for (let i = 0; i < args.length; i += 1) {
|
||||||
|
const arg = args[i];
|
||||||
|
const s = ToString(realm, arg);
|
||||||
|
if (s instanceof AbruptCompletion) {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
process.stdout.write(s);
|
||||||
|
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 && Abstract.Type(a) === 'String') {
|
||||||
|
return a.stringValue();
|
||||||
|
}
|
||||||
|
return inspect(a, realm);
|
||||||
|
}).join(' ');
|
||||||
|
console.log(formatted); // eslint-disable-line no-console
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Value.undefined;
|
||||||
|
}));
|
||||||
|
|
||||||
|
[
|
||||||
|
['global', realm.global],
|
||||||
|
['createRealm', () => {
|
||||||
|
const info = createRealm();
|
||||||
|
return info.$262;
|
||||||
|
}],
|
||||||
|
['evalScript', ([sourceText]) => realm.evaluateScript(sourceText.stringValue())],
|
||||||
|
['detachArrayBuffer', ([arrayBuffer]) => Abstract.DetachArrayBuffer(arrayBuffer)],
|
||||||
|
['gc', () => Value.undefined],
|
||||||
|
['spec', ([v]) => {
|
||||||
|
if (v.nativeFunction && v.nativeFunction.section) {
|
||||||
|
return new Value(realm, v.nativeFunction.section);
|
||||||
|
}
|
||||||
|
return Value.undefined;
|
||||||
|
}],
|
||||||
|
].forEach(([name, value]) => {
|
||||||
|
const v = value instanceof Value ? value : new Value(realm, value);
|
||||||
|
Abstract.CreateDataProperty($262, new Value(realm, name), v);
|
||||||
|
});
|
||||||
|
|
||||||
|
Abstract.CreateDataProperty(realm.global, new Value(realm, '$262'), $262);
|
||||||
|
Abstract.CreateDataProperty(realm.global, new Value(realm, '$'), $262);
|
||||||
|
|
||||||
|
return {
|
||||||
|
realm,
|
||||||
|
$262,
|
||||||
|
resolverCache,
|
||||||
|
trackedPromises,
|
||||||
|
setPrintHandle,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = { createRealm };
|
||||||
@@ -0,0 +1,187 @@
|
|||||||
|
'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.Abstract.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.Abstract.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);
|
||||||
|
result.preview = {
|
||||||
|
type: result.type,
|
||||||
|
subtype: result.subtype,
|
||||||
|
overflow: false,
|
||||||
|
properties: this.getProperties(object, options),
|
||||||
|
};
|
||||||
|
this.previewStack.pop();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
getProperties(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()
|
||||||
|
: 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: true,
|
||||||
|
symbol: key.stringValue ? undefined : wrap(key),
|
||||||
|
};
|
||||||
|
properties.push(descriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 };
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
require('./server');
|
||||||
|
const { attachRealm } = require('./context');
|
||||||
|
|
||||||
|
module.exports = { attachRealm };
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,102 @@
|
|||||||
|
'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.Abstract.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 };
|
||||||
|
},
|
||||||
|
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() {},
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
'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(); // eslint-disable-line no-underscore-dangle
|
||||||
|
|
||||||
|
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();
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
{
|
||||||
|
"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": {
|
||||||
|
"test:test262": "node test/test262/test262.js",
|
||||||
|
"test:supplemental": "node test/supplemental.js",
|
||||||
|
"test:json": "node test/json/json.js",
|
||||||
|
"build:grammar": "nearleyc src/grammar/StrNumericLiteral.ne -o src/grammar/StrNumericLiteral-gen.mjs",
|
||||||
|
"build:engine": "rollup -c",
|
||||||
|
"lint": "eslint rollup.config.js test/ src/ bin/ inspector/ scripts/ --cache --ext=js,mjs",
|
||||||
|
"build": "npm run build:engine",
|
||||||
|
"test": "npm run test:test262 && npm run test:supplemental",
|
||||||
|
"coverage": "nyc --reporter=lcov bash test/coverage_root.sh",
|
||||||
|
"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.8.7",
|
||||||
|
"@babel/plugin-syntax-bigint": "^7.8.3",
|
||||||
|
"@snek/source-map-support": "^1.0.4",
|
||||||
|
"acorn": "^7.1.1",
|
||||||
|
"babel-eslint": "^10.1.0",
|
||||||
|
"eslint": "^6.8.0",
|
||||||
|
"eslint-config-airbnb-base": "^14.1.0",
|
||||||
|
"eslint-plugin-import": "^2.20.2",
|
||||||
|
"glob": "^7.1.6",
|
||||||
|
"minimatch": "^3.0.4",
|
||||||
|
"nearley": "2.16.0",
|
||||||
|
"nyc": "^15.0.0",
|
||||||
|
"rollup": "^1.32.1",
|
||||||
|
"rollup-plugin-babel": "^4.4.0",
|
||||||
|
"rollup-plugin-commonjs": "^10.1.0",
|
||||||
|
"rollup-plugin-node-resolve": "^5.2.0",
|
||||||
|
"test262-stream": "^1.3.0",
|
||||||
|
"unicode-13.0.0": "^0.8.0",
|
||||||
|
"ws": "^7.2.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
'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({
|
||||||
|
exclude: 'node_modules/**',
|
||||||
|
plugins: [
|
||||||
|
'@babel/plugin-syntax-bigint',
|
||||||
|
'./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;
|
||||||
|
}
|
||||||
|
warn(warning);
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
'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');
|
||||||
@@ -0,0 +1,252 @@
|
|||||||
|
'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}";
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
if (path.node.leadingComments) {
|
||||||
|
outer: // eslint-disable-line no-labels
|
||||||
|
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.name}.section = '${url}';`));
|
||||||
|
break outer; // eslint-disable-line no-labels
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
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 './reference-operations.mjs';
|
||||||
|
export * from './regexp-objects.mjs';
|
||||||
|
export * from './source-code.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';
|
||||||
@@ -0,0 +1,248 @@
|
|||||||
|
import { surroundingAgent } from '../engine.mjs';
|
||||||
|
import {
|
||||||
|
Descriptor,
|
||||||
|
Value,
|
||||||
|
wellKnownSymbols,
|
||||||
|
} from '../value.mjs';
|
||||||
|
import { BoundNames_FormalParameters } 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_FormalParameters(formals).map(Value);
|
||||||
|
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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,250 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,213 @@
|
|||||||
|
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.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.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);
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import { isExpressionBody } from '../ast.mjs';
|
||||||
|
import { EnsureCompletion, X } from '../completion.mjs';
|
||||||
|
import { surroundingAgent } from '../engine.mjs';
|
||||||
|
import {
|
||||||
|
Evaluate_ExpressionBody,
|
||||||
|
Evaluate_FunctionBody,
|
||||||
|
} from '../runtime-semantics/all.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 evaluator = isExpressionBody(asyncBody) ? Evaluate_ExpressionBody : Evaluate_FunctionBody;
|
||||||
|
const result = EnsureCompletion(yield* evaluator(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));
|
||||||
|
}
|
||||||
@@ -0,0 +1,193 @@
|
|||||||
|
import { surroundingAgent } from '../engine.mjs';
|
||||||
|
import { Evaluate_FunctionBody } from '../runtime-semantics/all.mjs';
|
||||||
|
import {
|
||||||
|
Q, X,
|
||||||
|
Await,
|
||||||
|
Completion,
|
||||||
|
EnsureCompletion,
|
||||||
|
NormalCompletion,
|
||||||
|
AbruptCompletion,
|
||||||
|
} from '../completion.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_FunctionBody(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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 25.5.3.7 #sec-asyncgeneratoryield
|
||||||
|
export function* AsyncGeneratorYield(value) {
|
||||||
|
const genContext = surroundingAgent.runningExecutionContext;
|
||||||
|
Assert(genContext.Generator !== Value.undefined);
|
||||||
|
const generator = genContext.Generator;
|
||||||
|
Assert(GetGeneratorKind() === 'async');
|
||||||
|
value = Q(yield* Await(value));
|
||||||
|
generator.AsyncGeneratorState = 'suspendedYield';
|
||||||
|
surroundingAgent.executionContextStack.pop(genContext);
|
||||||
|
const resumptionValue = EnsureCompletion(yield handleInResume(AsyncGeneratorResolve, generator, value, Value.false));
|
||||||
|
if (resumptionValue.Type !== 'return') {
|
||||||
|
return Completion(resumptionValue);
|
||||||
|
}
|
||||||
|
const awaited = EnsureCompletion(yield* Await(resumptionValue.Value));
|
||||||
|
if (awaited.Type === 'Throw') {
|
||||||
|
return Completion(awaited);
|
||||||
|
}
|
||||||
|
Assert(awaited.Type === 'normal');
|
||||||
|
return new Completion('return', awaited.Value, undefined);
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
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.4 #leading-surrogate
|
||||||
|
export function isLeadingSurrogate(cp) {
|
||||||
|
return cp >= 0xD800 && cp <= 0xDBFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6.1.4 #trailing-surrogate
|
||||||
|
export function isTrailingSurrogate(cp) {
|
||||||
|
return cp >= 0xDC00 && cp <= 0xDFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
@@ -0,0 +1,233 @@
|
|||||||
|
// 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));
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
import { Q } from '../completion.mjs';
|
||||||
|
import { surroundingAgent } from '../engine.mjs';
|
||||||
|
import {
|
||||||
|
GetIdentifierReference,
|
||||||
|
LexicalEnvironment,
|
||||||
|
} from '../environment.mjs';
|
||||||
|
import {
|
||||||
|
Type,
|
||||||
|
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) {
|
||||||
|
if (!env || Type(env) === 'Undefined') {
|
||||||
|
env = surroundingAgent.runningExecutionContext.LexicalEnvironment;
|
||||||
|
}
|
||||||
|
Assert(env instanceof LexicalEnvironment);
|
||||||
|
return GetIdentifierReference(env, name, strict ? Value.true : Value.false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 8.3.3 #sec-getthisenvironment
|
||||||
|
export function GetThisEnvironment() {
|
||||||
|
let lex = surroundingAgent.runningExecutionContext.LexicalEnvironment;
|
||||||
|
while (true) { // eslint-disable-line no-constant-condition
|
||||||
|
const envRec = lex.EnvironmentRecord;
|
||||||
|
const exists = envRec.HasThisBinding();
|
||||||
|
if (exists === Value.true) {
|
||||||
|
return envRec;
|
||||||
|
}
|
||||||
|
const outer = lex.outerEnvironmentReference;
|
||||||
|
Assert(Type(outer) !== 'Null');
|
||||||
|
lex = 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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,410 @@
|
|||||||
|
import {
|
||||||
|
surroundingAgent,
|
||||||
|
ExecutionContext,
|
||||||
|
} from '../engine.mjs';
|
||||||
|
import { Realm } from '../realm.mjs';
|
||||||
|
import {
|
||||||
|
Descriptor,
|
||||||
|
Type,
|
||||||
|
Value,
|
||||||
|
} from '../value.mjs';
|
||||||
|
import {
|
||||||
|
EnsureCompletion, NormalCompletion, Q,
|
||||||
|
ReturnIfAbrupt,
|
||||||
|
X,
|
||||||
|
} from '../completion.mjs';
|
||||||
|
import { ExpectedArgumentCount } from '../static-semantics/all.mjs';
|
||||||
|
import {
|
||||||
|
EvaluateBody_AsyncConciseBody_ExpressionBody,
|
||||||
|
EvaluateBody_AsyncFunctionBody,
|
||||||
|
EvaluateBody_ConciseBody_ExpressionBody,
|
||||||
|
EvaluateBody_FunctionBody,
|
||||||
|
EvaluateBody_GeneratorBody,
|
||||||
|
EvaluateBody_AsyncGeneratorBody,
|
||||||
|
getFunctionBodyType,
|
||||||
|
} from '../runtime-semantics/all.mjs';
|
||||||
|
import {
|
||||||
|
FunctionEnvironmentRecord,
|
||||||
|
GlobalEnvironmentRecord,
|
||||||
|
NewFunctionEnvironment,
|
||||||
|
} from '../environment.mjs';
|
||||||
|
import { unwind, OutOfRange } from '../helpers.mjs';
|
||||||
|
import {
|
||||||
|
Assert,
|
||||||
|
DefinePropertyOrThrow,
|
||||||
|
GetActiveScriptOrModule,
|
||||||
|
HasOwnProperty,
|
||||||
|
IsConstructor,
|
||||||
|
IsExtensible,
|
||||||
|
IsInteger,
|
||||||
|
MakeBasicObject,
|
||||||
|
OrdinaryObjectCreate,
|
||||||
|
OrdinaryCreateFromConstructor,
|
||||||
|
ToObject,
|
||||||
|
isStrictModeCode,
|
||||||
|
} 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 9.2.1.1 #sec-prepareforordinarycall
|
||||||
|
function PrepareForOrdinaryCall(F, newTarget) {
|
||||||
|
Assert(Type(newTarget) === 'Undefined' || Type(newTarget) === 'Object');
|
||||||
|
// const callerContext = surroundingAgent.runningExecutionContext;
|
||||||
|
const calleeContext = new ExecutionContext();
|
||||||
|
calleeContext.Function = F;
|
||||||
|
const calleeRealm = F.Realm;
|
||||||
|
calleeContext.Realm = calleeRealm;
|
||||||
|
calleeContext.ScriptOrModule = F.ScriptOrModule;
|
||||||
|
const localEnv = NewFunctionEnvironment(F, newTarget);
|
||||||
|
calleeContext.LexicalEnvironment = localEnv;
|
||||||
|
calleeContext.VariableEnvironment = localEnv;
|
||||||
|
// Suspend(callerContext);
|
||||||
|
surroundingAgent.executionContextStack.push(calleeContext);
|
||||||
|
return calleeContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 9.2.1.2 #sec-ordinarycallbindthis
|
||||||
|
function OrdinaryCallBindThis(F, calleeContext, thisArgument) {
|
||||||
|
const thisMode = F.ThisMode;
|
||||||
|
if (thisMode === 'lexical') {
|
||||||
|
return new NormalCompletion(Value.undefined);
|
||||||
|
}
|
||||||
|
const calleeRealm = F.Realm;
|
||||||
|
const localEnv = calleeContext.LexicalEnvironment;
|
||||||
|
let thisValue;
|
||||||
|
if (thisMode === 'strict') {
|
||||||
|
thisValue = thisArgument;
|
||||||
|
} else {
|
||||||
|
if (thisArgument === Value.undefined || thisArgument === Value.null) {
|
||||||
|
const globalEnv = calleeRealm.GlobalEnv;
|
||||||
|
const globalEnvRec = globalEnv.EnvironmentRecord;
|
||||||
|
Assert(globalEnvRec instanceof GlobalEnvironmentRecord);
|
||||||
|
thisValue = globalEnvRec.GlobalThisValue;
|
||||||
|
} else {
|
||||||
|
thisValue = X(ToObject(thisArgument));
|
||||||
|
// NOTE: ToObject produces wrapper objects using calleeRealm.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const envRec = localEnv.EnvironmentRecord;
|
||||||
|
Assert(envRec instanceof FunctionEnvironmentRecord);
|
||||||
|
Assert(envRec.ThisBindingStatus !== 'initialized');
|
||||||
|
return envRec.BindThisValue(thisValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 9.2.1.3 #sec-ordinarycallevaluatebody
|
||||||
|
export function* OrdinaryCallEvaluateBody(F, argumentsList) {
|
||||||
|
switch (getFunctionBodyType(F.ECMAScriptCode)) {
|
||||||
|
// FunctionBody : FunctionStatementList
|
||||||
|
// ConciseBody : `{` FunctionBody `}`
|
||||||
|
case 'FunctionBody':
|
||||||
|
case 'ConciseBody_FunctionBody':
|
||||||
|
return yield* EvaluateBody_FunctionBody(F.ECMAScriptCode.body.body, F, argumentsList);
|
||||||
|
|
||||||
|
// ConciseBody : ExpressionBody
|
||||||
|
case 'ConciseBody_ExpressionBody':
|
||||||
|
return yield* EvaluateBody_ConciseBody_ExpressionBody(F.ECMAScriptCode.body, F, argumentsList);
|
||||||
|
|
||||||
|
case 'GeneratorBody':
|
||||||
|
return yield* EvaluateBody_GeneratorBody(F.ECMAScriptCode.body.body, F, argumentsList);
|
||||||
|
|
||||||
|
case 'AsyncFunctionBody':
|
||||||
|
case 'AsyncConciseBody_AsyncFunctionBody':
|
||||||
|
return yield* EvaluateBody_AsyncFunctionBody(F.ECMAScriptCode.body.body, F, argumentsList);
|
||||||
|
|
||||||
|
case 'AsyncConciseBody_ExpressionBody':
|
||||||
|
return yield* EvaluateBody_AsyncConciseBody_ExpressionBody(F.ECMAScriptCode.body, F, argumentsList);
|
||||||
|
|
||||||
|
case 'AsyncGeneratorBody':
|
||||||
|
return yield* EvaluateBody_AsyncGeneratorBody(F.ECMAScriptCode.body.body, F, argumentsList);
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new OutOfRange('OrdinaryCallEvaluateBody', F.ECMAScriptCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 9.2.1 #sec-ecmascript-function-objects-call-thisargument-argumentslist
|
||||||
|
function FunctionCallSlot(thisArgument, argumentsList) {
|
||||||
|
const F = this;
|
||||||
|
|
||||||
|
Assert(isECMAScriptFunctionObject(F));
|
||||||
|
if (F.IsClassConstructor === Value.true) {
|
||||||
|
return surroundingAgent.Throw('TypeError', 'ConstructorNonCallable', F);
|
||||||
|
}
|
||||||
|
// const callerContext = surroundingAgent.runningExecutionContext;
|
||||||
|
const calleeContext = PrepareForOrdinaryCall(F, Value.undefined);
|
||||||
|
Assert(surroundingAgent.runningExecutionContext === calleeContext);
|
||||||
|
OrdinaryCallBindThis(F, calleeContext, thisArgument);
|
||||||
|
const result = EnsureCompletion(unwind(OrdinaryCallEvaluateBody(F, argumentsList)));
|
||||||
|
// Remove calleeContext from the execution context stack and
|
||||||
|
// restore callerContext as the running execution context.
|
||||||
|
surroundingAgent.executionContextStack.pop(calleeContext);
|
||||||
|
if (result.Type === 'return') {
|
||||||
|
return new NormalCompletion(result.Value);
|
||||||
|
}
|
||||||
|
ReturnIfAbrupt(result);
|
||||||
|
return new NormalCompletion(Value.undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 9.2.2 #sec-ecmascript-function-objects-construct-argumentslist-newtarget
|
||||||
|
function FunctionConstructSlot(argumentsList, newTarget) {
|
||||||
|
const F = this;
|
||||||
|
|
||||||
|
Assert(isECMAScriptFunctionObject(F));
|
||||||
|
Assert(Type(newTarget) === 'Object');
|
||||||
|
// const callerContext = surroundingAgent.runningExecutionContext;
|
||||||
|
const kind = F.ConstructorKind;
|
||||||
|
let thisArgument;
|
||||||
|
if (kind === 'base') {
|
||||||
|
thisArgument = Q(OrdinaryCreateFromConstructor(newTarget, '%Object.prototype%'));
|
||||||
|
}
|
||||||
|
const calleeContext = PrepareForOrdinaryCall(F, newTarget);
|
||||||
|
Assert(surroundingAgent.runningExecutionContext === calleeContext);
|
||||||
|
surroundingAgent.runningExecutionContext.callSite.constructCall = true;
|
||||||
|
if (kind === 'base') {
|
||||||
|
OrdinaryCallBindThis(F, calleeContext, thisArgument);
|
||||||
|
}
|
||||||
|
const constructorEnv = calleeContext.LexicalEnvironment;
|
||||||
|
const envRec = constructorEnv.EnvironmentRecord;
|
||||||
|
const result = EnsureCompletion(unwind(OrdinaryCallEvaluateBody(F, argumentsList)));
|
||||||
|
// Remove calleeContext from the execution context stack and
|
||||||
|
// restore callerContext as the running execution context.
|
||||||
|
surroundingAgent.executionContextStack.pop(calleeContext);
|
||||||
|
if (result.Type === 'return') {
|
||||||
|
if (Type(result.Value) === 'Object') {
|
||||||
|
return new NormalCompletion(result.Value);
|
||||||
|
}
|
||||||
|
if (kind === 'base') {
|
||||||
|
return new NormalCompletion(thisArgument);
|
||||||
|
}
|
||||||
|
if (Type(result.Value) !== 'Undefined') {
|
||||||
|
return surroundingAgent.Throw('TypeError', 'DerivedConstructorReturnedNonObject');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ReturnIfAbrupt(result);
|
||||||
|
}
|
||||||
|
return Q(envRec.GetThisBinding());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 9.2.3 #sec-functionallocate
|
||||||
|
export function OrdinaryFunctionCreate(functionPrototype, 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 = FunctionCallSlot;
|
||||||
|
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 = FunctionConstructSlot;
|
||||||
|
F.ConstructorKind = 'base';
|
||||||
|
if (writablePrototype === undefined) {
|
||||||
|
writablePrototype = true;
|
||||||
|
}
|
||||||
|
if (prototype === undefined) {
|
||||||
|
prototype = OrdinaryObjectCreate(surroundingAgent.intrinsic('%Object.prototype%'));
|
||||||
|
X(DefinePropertyOrThrow(prototype, new Value('constructor'), Descriptor({
|
||||||
|
Value: F,
|
||||||
|
Writable: writablePrototype ? Value.true : Value.false,
|
||||||
|
Enumerable: Value.false,
|
||||||
|
Configurable: Value.true,
|
||||||
|
})));
|
||||||
|
}
|
||||||
|
X(DefinePropertyOrThrow(F, new Value('prototype'), Descriptor({
|
||||||
|
Value: prototype,
|
||||||
|
Writable: writablePrototype ? Value.true : Value.false,
|
||||||
|
Enumerable: Value.false,
|
||||||
|
Configurable: Value.false,
|
||||||
|
})));
|
||||||
|
return new 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 new NormalCompletion(Value.undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 9.2.12 #sec-makemethod
|
||||||
|
export function MakeMethod(F, homeObject) {
|
||||||
|
Assert(isECMAScriptFunctionObject(F));
|
||||||
|
Assert(Type(homeObject) === 'Object');
|
||||||
|
F.HomeObject = homeObject;
|
||||||
|
return new NormalCompletion(Value.undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 9.2.13 #sec-setfunctionname
|
||||||
|
export function SetFunctionName(F, name, prefix) {
|
||||||
|
Assert(IsExtensible(F) === Value.true && HasOwnProperty(F, new Value('name')) === Value.false);
|
||||||
|
Assert(Type(name) === 'Symbol' || Type(name) === 'String');
|
||||||
|
Assert(!prefix || Type(prefix) === 'String');
|
||||||
|
if (Type(name) === 'Symbol') {
|
||||||
|
const description = name.Description;
|
||||||
|
if (Type(description) === 'Undefined') {
|
||||||
|
name = new Value('');
|
||||||
|
} else {
|
||||||
|
name = new Value(`[${description.stringValue()}]`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (prefix !== undefined) {
|
||||||
|
name = new Value(`${prefix.stringValue()} ${name.stringValue()}`);
|
||||||
|
}
|
||||||
|
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(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.[[Extensible]] to true.
|
||||||
|
func.ScriptOrModule = Value.null;
|
||||||
|
// 10. 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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,200 @@
|
|||||||
|
import {
|
||||||
|
Completion,
|
||||||
|
NormalCompletion,
|
||||||
|
Q, X,
|
||||||
|
EnsureCompletion,
|
||||||
|
} from '../completion.mjs';
|
||||||
|
import { surroundingAgent } from '../engine.mjs';
|
||||||
|
import { Evaluate_FunctionBody } from '../runtime-semantics/all.mjs';
|
||||||
|
import { Value } from '../value.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_FunctionBody(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 new 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 = resume(genContext, new 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 = 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 new 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.
|
||||||
|
}
|
||||||
@@ -0,0 +1,243 @@
|
|||||||
|
import { ExecutionContext, HostEnsureCanCompileStrings, surroundingAgent } from '../engine.mjs';
|
||||||
|
import { InstantiateFunctionObject } from '../runtime-semantics/all.mjs';
|
||||||
|
import { Type, Value } from '../value.mjs';
|
||||||
|
import {
|
||||||
|
AbruptCompletion, Completion,
|
||||||
|
NormalCompletion,
|
||||||
|
Q,
|
||||||
|
X,
|
||||||
|
} from '../completion.mjs';
|
||||||
|
import { ParseScript } from '../parse.mjs';
|
||||||
|
import {
|
||||||
|
BoundNames_BindingIdentifier,
|
||||||
|
BoundNames_Declaration,
|
||||||
|
BoundNames_ForBinding,
|
||||||
|
BoundNames_FunctionDeclaration,
|
||||||
|
BoundNames_VariableDeclaration,
|
||||||
|
IsConstantDeclaration,
|
||||||
|
IsStrict,
|
||||||
|
LexicallyScopedDeclarations_ScriptBody,
|
||||||
|
VarDeclaredNames_ScriptBody,
|
||||||
|
VarScopedDeclarations_ScriptBody,
|
||||||
|
} from '../static-semantics/all.mjs';
|
||||||
|
import {
|
||||||
|
isAsyncFunctionDeclaration,
|
||||||
|
isAsyncGeneratorDeclaration,
|
||||||
|
isBindingIdentifier,
|
||||||
|
isForBinding,
|
||||||
|
isFunctionDeclaration,
|
||||||
|
isGeneratorDeclaration,
|
||||||
|
isVariableDeclaration,
|
||||||
|
} from '../ast.mjs';
|
||||||
|
import { Evaluate_Script } from '../evaluator.mjs';
|
||||||
|
import {
|
||||||
|
NewDeclarativeEnvironment,
|
||||||
|
// FunctionEnvironmentRecord,
|
||||||
|
GlobalEnvironmentRecord,
|
||||||
|
ObjectEnvironmentRecord,
|
||||||
|
} from '../environment.mjs';
|
||||||
|
import {
|
||||||
|
Assert,
|
||||||
|
// GetThisEnvironment,
|
||||||
|
} from './all.mjs';
|
||||||
|
|
||||||
|
// This file covers abstract operations defined in
|
||||||
|
// 18 #sec-global-object
|
||||||
|
|
||||||
|
// 18.2.1.1 #sec-performeval
|
||||||
|
export function PerformEval(x, callerRealm, strictCaller, direct) {
|
||||||
|
if (direct === false) {
|
||||||
|
Assert(strictCaller === false);
|
||||||
|
}
|
||||||
|
if (Type(x) !== 'String') {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
const evalRealm = surroundingAgent.currentRealmRecord;
|
||||||
|
Q(HostEnsureCanCompileStrings(callerRealm, evalRealm));
|
||||||
|
/*
|
||||||
|
const thisEnvRec = X(GetThisEnvironment());
|
||||||
|
let inFunction;
|
||||||
|
let inMethod;
|
||||||
|
let inDerivedConstructor;
|
||||||
|
if (thisEnvRec instanceof FunctionEnvironmentRecord) {
|
||||||
|
const F = thisEnvRec.FunctionObject;
|
||||||
|
inFunction = true;
|
||||||
|
inMethod = thisEnvRec.HasSuperBinding() === Value.true;
|
||||||
|
if (F.ConstructorKind === 'derived') {
|
||||||
|
inDerivedConstructor = true;
|
||||||
|
} else {
|
||||||
|
inDerivedConstructor = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
inFunction = false;
|
||||||
|
inMethod = false;
|
||||||
|
inDerivedConstructor = false;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
const r = ParseScript(x.stringValue(), evalRealm, undefined, strictCaller);
|
||||||
|
if (Array.isArray(r)) {
|
||||||
|
return surroundingAgent.Throw(r[0]);
|
||||||
|
}
|
||||||
|
const script = r.ECMAScriptCode;
|
||||||
|
// If script Contains ScriptBody is false, return undefined.
|
||||||
|
const body = script.body;
|
||||||
|
let strictEval;
|
||||||
|
if (strictCaller === true) {
|
||||||
|
strictEval = true;
|
||||||
|
} else {
|
||||||
|
strictEval = IsStrict(script);
|
||||||
|
}
|
||||||
|
const runningContext = surroundingAgent.runningExecutionContext;
|
||||||
|
let lexEnv;
|
||||||
|
let varEnv;
|
||||||
|
if (direct === true) {
|
||||||
|
lexEnv = NewDeclarativeEnvironment(runningContext.LexicalEnvironment);
|
||||||
|
varEnv = runningContext.VariableEnvironment;
|
||||||
|
} else {
|
||||||
|
lexEnv = NewDeclarativeEnvironment(evalRealm.GlobalEnv);
|
||||||
|
varEnv = evalRealm.GlobalEnv;
|
||||||
|
}
|
||||||
|
if (strictEval === true) {
|
||||||
|
varEnv = lexEnv;
|
||||||
|
}
|
||||||
|
// If runningContext is not already suspended, suspend runningContext.
|
||||||
|
const evalContext = new ExecutionContext();
|
||||||
|
evalContext.Function = Value.null;
|
||||||
|
evalContext.Realm = evalRealm;
|
||||||
|
evalContext.ScriptOrModule = runningContext.ScriptOrModule;
|
||||||
|
evalContext.VariableEnvironment = varEnv;
|
||||||
|
evalContext.LexicalEnvironment = lexEnv;
|
||||||
|
surroundingAgent.executionContextStack.push(evalContext);
|
||||||
|
let result = EvalDeclarationInstantiation(body, varEnv, lexEnv, strictEval);
|
||||||
|
if (result.Type === 'normal') {
|
||||||
|
result = Evaluate_Script(body);
|
||||||
|
}
|
||||||
|
if (result.Type === 'normal' && result.Value === undefined) {
|
||||||
|
result = new NormalCompletion(Value.undefined);
|
||||||
|
}
|
||||||
|
surroundingAgent.executionContextStack.pop(evalContext);
|
||||||
|
// Resume the context that is now on the top of the execution context stack as the running execution context.
|
||||||
|
return Completion(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 18.2.1.3 #sec-evaldeclarationinstantiation
|
||||||
|
function EvalDeclarationInstantiation(body, varEnv, lexEnv, strict) {
|
||||||
|
const varNames = VarDeclaredNames_ScriptBody(body).map(Value);
|
||||||
|
const varDeclarations = VarScopedDeclarations_ScriptBody(body);
|
||||||
|
const lexEnvRec = lexEnv.EnvironmentRecord;
|
||||||
|
const varEnvRec = varEnv.EnvironmentRecord;
|
||||||
|
if (strict === false) {
|
||||||
|
if (varEnvRec instanceof GlobalEnvironmentRecord) {
|
||||||
|
for (const name of varNames) {
|
||||||
|
if (varEnvRec.HasLexicalDeclaration(name) === Value.true) {
|
||||||
|
return surroundingAgent.Throw('SyntaxError', 'AlreadyDeclared', name);
|
||||||
|
}
|
||||||
|
// NOTE: eval will not create a global var declaration that would be shadowed by a global lexical declaration.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let thisLex = lexEnv;
|
||||||
|
// Assert: The following loop will terminate.
|
||||||
|
while (thisLex !== varEnv) {
|
||||||
|
const thisEnvRec = thisLex.EnvironmentRecord;
|
||||||
|
if (!(thisEnvRec instanceof ObjectEnvironmentRecord)) {
|
||||||
|
for (const name of varNames) {
|
||||||
|
if (thisEnvRec.HasBinding(name) === Value.true) {
|
||||||
|
return surroundingAgent.Throw('SyntaxError', 'AlreadyDeclared', name);
|
||||||
|
// NOTE: Annex B.3.5 defines alternate semantics for the above step.
|
||||||
|
}
|
||||||
|
// NOTE: A direct eval will not hoist var declaration over a like-named lexical declaration
|
||||||
|
}
|
||||||
|
}
|
||||||
|
thisLex = thisLex.outerEnvironmentReference;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const functionsToInitialize = [];
|
||||||
|
const declaredFunctionNames = [];
|
||||||
|
for (const d of [...varDeclarations].reverse()) {
|
||||||
|
if (!isVariableDeclaration(d) && !isForBinding(d) && !isBindingIdentifier(d)) {
|
||||||
|
Assert(isFunctionDeclaration(d) || isGeneratorDeclaration(d)
|
||||||
|
|| isAsyncFunctionDeclaration(d) || isAsyncGeneratorDeclaration(d));
|
||||||
|
const fn = new Value(BoundNames_FunctionDeclaration(d)[0]);
|
||||||
|
if (!declaredFunctionNames.includes(fn)) {
|
||||||
|
if (varEnvRec instanceof GlobalEnvironmentRecord) {
|
||||||
|
const fnDefinable = Q(varEnvRec.CanDeclareGlobalFunction(fn));
|
||||||
|
if (fnDefinable === Value.false) {
|
||||||
|
return surroundingAgent.Throw('TypeError', 'AlreadyDeclared', fn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
declaredFunctionNames.push(fn);
|
||||||
|
functionsToInitialize.unshift(d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// NOTE: Annex B.3.3.3 adds additional steps at this point.
|
||||||
|
const declaredVarNames = [];
|
||||||
|
for (const d of varDeclarations) {
|
||||||
|
let boundNames;
|
||||||
|
if (isVariableDeclaration(d)) {
|
||||||
|
boundNames = BoundNames_VariableDeclaration(d);
|
||||||
|
} else if (isForBinding(d)) {
|
||||||
|
boundNames = BoundNames_ForBinding(d);
|
||||||
|
} else if (isBindingIdentifier(d)) {
|
||||||
|
boundNames = BoundNames_BindingIdentifier(d);
|
||||||
|
}
|
||||||
|
if (boundNames !== undefined) {
|
||||||
|
for (const vn of boundNames.map(Value)) {
|
||||||
|
if (!declaredFunctionNames.includes(vn)) {
|
||||||
|
if (varEnvRec instanceof GlobalEnvironmentRecord) {
|
||||||
|
const vnDefinable = Q(varEnvRec.CanDeclareGlobalVar(vn));
|
||||||
|
if (vnDefinable === Value.false) {
|
||||||
|
return surroundingAgent.Throw('TypeError', 'AlreadyDeclared', vn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!declaredVarNames.includes(vn)) {
|
||||||
|
declaredVarNames.push(vn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// NOTE: No abnormal terminations occur after this algorithm step unless
|
||||||
|
// varEnvRec is a global Environment Record and the global object is a Proxy exotic object.
|
||||||
|
const lexDeclarations = LexicallyScopedDeclarations_ScriptBody(body);
|
||||||
|
for (const d of lexDeclarations) {
|
||||||
|
for (const dn of BoundNames_Declaration(d).map(Value)) {
|
||||||
|
if (IsConstantDeclaration(d)) {
|
||||||
|
Q(lexEnvRec.CreateImmutableBinding(dn, Value.true));
|
||||||
|
} else {
|
||||||
|
Q(lexEnvRec.CreateMutableBinding(dn, Value.false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const f of functionsToInitialize) {
|
||||||
|
const fn = new Value(BoundNames_FunctionDeclaration(f)[0]);
|
||||||
|
const fo = InstantiateFunctionObject(f, lexEnv);
|
||||||
|
if (varEnvRec instanceof GlobalEnvironmentRecord) {
|
||||||
|
Q(varEnvRec.CreateGlobalFunctionBinding(fn, fo, Value.true));
|
||||||
|
} else {
|
||||||
|
const bindingExists = varEnvRec.HasBinding(fn);
|
||||||
|
if (bindingExists === Value.false) {
|
||||||
|
const status = X(varEnvRec.CreateMutableBinding(fn, Value.true));
|
||||||
|
Assert(!(status instanceof AbruptCompletion));
|
||||||
|
X(varEnvRec.InitializeBinding(fn, fo));
|
||||||
|
} else {
|
||||||
|
X(varEnvRec.SetMutableBinding(fn, fo, Value.false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const vn of declaredVarNames) {
|
||||||
|
if (!declaredFunctionNames.includes(vn)) {
|
||||||
|
if (varEnvRec instanceof GlobalEnvironmentRecord) {
|
||||||
|
Q(varEnvRec.CreateGlobalVarBinding(vn, Value.true));
|
||||||
|
} else {
|
||||||
|
const bindingExists = varEnvRec.HasBinding(vn);
|
||||||
|
if (bindingExists === Value.false) {
|
||||||
|
const status = X(varEnvRec.CreateMutableBinding(vn, Value.true));
|
||||||
|
Assert(!(status instanceof AbruptCompletion));
|
||||||
|
X(varEnvRec.InitializeBinding(vn, Value.undefined));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new NormalCompletion(undefined);
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,314 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,224 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7.4.6 #sec-iteratorclose
|
||||||
|
export function IteratorClose(iteratorRecord, completion) {
|
||||||
|
// TODO: completion should be a Completion Record so this should not be necessary
|
||||||
|
completion = EnsureCompletion(completion);
|
||||||
|
Assert(Type(iteratorRecord.Iterator) === 'Object');
|
||||||
|
Assert(completion instanceof Completion);
|
||||||
|
const iterator = iteratorRecord.Iterator;
|
||||||
|
const ret = Q(GetMethod(iterator, new Value('return')));
|
||||||
|
if (ret === Value.undefined) {
|
||||||
|
return Completion(completion);
|
||||||
|
}
|
||||||
|
const innerResult = EnsureCompletion(Call(ret, iterator));
|
||||||
|
if (completion.Type === 'throw') {
|
||||||
|
return Completion(completion);
|
||||||
|
}
|
||||||
|
if (innerResult.Type === 'throw') {
|
||||||
|
return Completion(innerResult);
|
||||||
|
}
|
||||||
|
if (Type(innerResult.Value) !== 'Object') {
|
||||||
|
return surroundingAgent.Throw('TypeError', 'NotAnObject', innerResult.Value);
|
||||||
|
}
|
||||||
|
return Completion(completion);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7.4.7 #sec-asynciteratorclose
|
||||||
|
export function* AsyncIteratorClose(iteratorRecord, completion) {
|
||||||
|
Assert(Type(iteratorRecord.Iterator) === 'Object');
|
||||||
|
Assert(completion instanceof Completion);
|
||||||
|
const iterator = iteratorRecord.Iterator;
|
||||||
|
const ret = Q(GetMethod(iterator, new Value('return')));
|
||||||
|
if (ret === Value.undefined) {
|
||||||
|
return Completion(completion);
|
||||||
|
}
|
||||||
|
let innerResult = EnsureCompletion(Call(ret, iterator));
|
||||||
|
if (innerResult.Type === 'normal') {
|
||||||
|
innerResult = EnsureCompletion(yield* Await(innerResult.Value));
|
||||||
|
}
|
||||||
|
if (completion.Type === 'throw') {
|
||||||
|
return Completion(completion);
|
||||||
|
}
|
||||||
|
if (innerResult.Type === 'throw') {
|
||||||
|
return Completion(innerResult);
|
||||||
|
}
|
||||||
|
if (Type(innerResult.Value) !== 'Object') {
|
||||||
|
return surroundingAgent.Throw('TypeError', 'NotAnObject', innerResult.Value);
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,199 @@
|
|||||||
|
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,
|
||||||
|
} 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ModuleNamespaceGet(P, Receiver) {
|
||||||
|
const O = this;
|
||||||
|
|
||||||
|
Assert(IsPropertyKey(P));
|
||||||
|
if (Type(P) === 'Symbol') {
|
||||||
|
return OrdinaryGet(O, P, Receiver);
|
||||||
|
}
|
||||||
|
const exports = O.Exports;
|
||||||
|
if (!exports.has(P)) {
|
||||||
|
return Value.undefined;
|
||||||
|
}
|
||||||
|
const m = O.Module;
|
||||||
|
const binding = m.ResolveExport(P);
|
||||||
|
Assert(binding instanceof ResolvedBindingRecord);
|
||||||
|
const targetModule = binding.Module;
|
||||||
|
Assert(targetModule !== Value.undefined);
|
||||||
|
const targetEnv = targetModule.Environment;
|
||||||
|
if (targetEnv === Value.undefined) {
|
||||||
|
return surroundingAgent.Throw('ReferenceError', 'NotDefined', P);
|
||||||
|
}
|
||||||
|
const targetEnvRec = targetEnv.EnvironmentRecord;
|
||||||
|
return Q(targetEnvRec.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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,263 @@
|
|||||||
|
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 = new 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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
import { surroundingAgent } from '../engine.mjs';
|
||||||
|
import { Value, Type } from '../value.mjs';
|
||||||
|
|
||||||
|
export function Assert(invariant, source) {
|
||||||
|
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 new Value(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) {
|
||||||
|
if (node.strict === true) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.type === 'FunctionDeclaration' || node.type === 'FunctionExpression') {
|
||||||
|
return isStrictModeCode(node.body);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
@@ -0,0 +1,434 @@
|
|||||||
|
import {
|
||||||
|
Descriptor,
|
||||||
|
Type,
|
||||||
|
Value,
|
||||||
|
ObjectValue,
|
||||||
|
wellKnownSymbols,
|
||||||
|
} from '../value.mjs';
|
||||||
|
import {
|
||||||
|
surroundingAgent,
|
||||||
|
} from '../engine.mjs';
|
||||||
|
import {
|
||||||
|
InstanceofOperator,
|
||||||
|
} from '../runtime-semantics/all.mjs';
|
||||||
|
import {
|
||||||
|
NormalCompletion,
|
||||||
|
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 new 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 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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,405 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,392 @@
|
|||||||
|
import {
|
||||||
|
HostEnqueuePromiseJob,
|
||||||
|
HostPromiseRejectionTracker,
|
||||||
|
surroundingAgent,
|
||||||
|
} from '../engine.mjs';
|
||||||
|
import { Type, Value } from '../value.mjs';
|
||||||
|
import {
|
||||||
|
Completion,
|
||||||
|
AbruptCompletion,
|
||||||
|
NormalCompletion,
|
||||||
|
Q,
|
||||||
|
X,
|
||||||
|
ThrowCompletion,
|
||||||
|
EnsureCompletion,
|
||||||
|
} 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 === Value.undefined
|
||||||
|
|| isFunctionObject(O.Handler));
|
||||||
|
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 Call(then, thenable, « resolvingFunctions.[[Resolve]], resolvingFunctions.[[Reject]] »).
|
||||||
|
const thenCallResult = Call(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).
|
||||||
|
const getThenRealmResult = GetFunctionRealm(then);
|
||||||
|
// 3. If getThenRealmResult is a normal completion, then let thenRealm be getThenRealmResult.[[Value]].
|
||||||
|
// 4. Otherwise, let thenRealm be null.
|
||||||
|
let thenRealm;
|
||||||
|
if (getThenRealmResult instanceof NormalCompletion) {
|
||||||
|
thenRealm = getThenRealmResult.Value;
|
||||||
|
} else {
|
||||||
|
thenRealm = Value.null;
|
||||||
|
}
|
||||||
|
// 5. Return { [[Job]]: job, [[Realm]]: thenRealm }.
|
||||||
|
return { Job: job, Realm: thenRealm };
|
||||||
|
}
|
||||||
|
|
||||||
|
// 25.6.1.3.2 #sec-promise-resolve-functions
|
||||||
|
function PromiseResolveFunctions([resolution = 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;
|
||||||
|
if (SameValue(resolution, promise) === Value.true) {
|
||||||
|
const selfResolutionError = surroundingAgent.Throw('TypeError', 'CannotResolvePromiseWithItself').Value;
|
||||||
|
return RejectPromise(promise, selfResolutionError);
|
||||||
|
}
|
||||||
|
if (Type(resolution) !== 'Object') {
|
||||||
|
return FulfillPromise(promise, resolution);
|
||||||
|
}
|
||||||
|
|
||||||
|
const then = Get(resolution, new Value('then'));
|
||||||
|
if (then instanceof AbruptCompletion) {
|
||||||
|
return RejectPromise(promise, then.Value);
|
||||||
|
}
|
||||||
|
const thenAction = then.Value;
|
||||||
|
if (IsCallable(thenAction) === Value.false) {
|
||||||
|
return FulfillPromise(promise, resolution);
|
||||||
|
}
|
||||||
|
const job = NewPromiseResolveThenableJob(promise, resolution, thenAction);
|
||||||
|
HostEnqueuePromiseJob(job.Job, job.Realm);
|
||||||
|
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 undefined, then
|
||||||
|
if (handler === Value.undefined) {
|
||||||
|
// i. If type is Fulfill, let handlerResult be NormalCompletion(argument).
|
||||||
|
if (type === 'Fulfill') {
|
||||||
|
handlerResult = new NormalCompletion(argument);
|
||||||
|
} else {
|
||||||
|
// 1. Assert: type is Reject.
|
||||||
|
Assert(type === 'Reject');
|
||||||
|
// 2. Let handlerResult be ThrowCompletion(argument).
|
||||||
|
handlerResult = new ThrowCompletion(argument);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// f. let handlerResult be Call(handler, undefined, « argument »).
|
||||||
|
handlerResult = Call(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 new 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, [EnsureCompletion(handlerResult).Value]);
|
||||||
|
}
|
||||||
|
// j. Return Completion(status).
|
||||||
|
return Completion(status);
|
||||||
|
};
|
||||||
|
// 2. Let handlerRealm be null.
|
||||||
|
let handlerRealm = Value.null;
|
||||||
|
// 3. If reaction.[[Handler]] is not undefined, then
|
||||||
|
if (reaction.Handler !== Value.undefined) {
|
||||||
|
// a. Let getHandlerRealmResult be GetFunctionRealm(handler).
|
||||||
|
const getHandlerRealmResult = GetFunctionRealm(reaction.Handler);
|
||||||
|
// b. If getHandlerRealmResult is a normal completion, then set handlerRealm to getHandlerRealmResult.[[Value]].
|
||||||
|
if (getHandlerRealmResult instanceof NormalCompletion) {
|
||||||
|
handlerRealm = getHandlerRealmResult.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 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 present, then
|
||||||
|
if (resultCapability) {
|
||||||
|
// a. Assert: resultCapability is a PromiseCapability Record.
|
||||||
|
Assert(resultCapability instanceof PromiseCapabilityRecord);
|
||||||
|
} else {
|
||||||
|
// a. Set resultCapability to undefined.
|
||||||
|
resultCapability = Value.undefined;
|
||||||
|
}
|
||||||
|
// 4. If IsCallable(onFulfilled) is false, then
|
||||||
|
if (IsCallable(onFulfilled) === Value.false) {
|
||||||
|
// a. Set onFulfilled to undefined.
|
||||||
|
onFulfilled = Value.undefined;
|
||||||
|
}
|
||||||
|
// 5. If IsCallable(onRejected) is false, then
|
||||||
|
if (IsCallable(onRejected) === Value.false) {
|
||||||
|
// a. Set onRejected to undefined.
|
||||||
|
onRejected = Value.undefined;
|
||||||
|
}
|
||||||
|
// 6. Let fulfillReaction be the PromiseReaction { [[Capability]]: resultCapability, [[Type]]: Fulfill, [[Handler]]: onFulfilled }.
|
||||||
|
const fulfillReaction = new PromiseReactionRecord({
|
||||||
|
Capability: resultCapability,
|
||||||
|
Type: 'Fulfill',
|
||||||
|
Handler: onFulfilled,
|
||||||
|
});
|
||||||
|
// 7. Let rejectReaction be the PromiseReaction { [[Capability]]: resultCapability, [[Type]]: Reject, [[Handler]]: onRejected }.
|
||||||
|
const rejectReaction = new PromiseReactionRecord({
|
||||||
|
Capability: resultCapability,
|
||||||
|
Type: 'Reject',
|
||||||
|
Handler: onRejected,
|
||||||
|
});
|
||||||
|
// 8. 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);
|
||||||
|
}
|
||||||
|
// 11. Set promise.[[PromiseIsHandled]] to true.
|
||||||
|
promise.PromiseIsHandled = Value.true;
|
||||||
|
// 12. If resultCapability is undefined, then
|
||||||
|
if (resultCapability === Value.undefined) {
|
||||||
|
// a. Return undefined.
|
||||||
|
return Value.undefined;
|
||||||
|
} else {
|
||||||
|
// return resultCapability.[[Promise]].
|
||||||
|
return resultCapability.Promise;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,579 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,142 @@
|
|||||||
|
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 new 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);
|
||||||
|
}
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
import {
|
||||||
|
surroundingAgent,
|
||||||
|
} from '../engine.mjs';
|
||||||
|
import {
|
||||||
|
ParseRegExp,
|
||||||
|
} from '../parse.mjs';
|
||||||
|
import {
|
||||||
|
Descriptor,
|
||||||
|
Value,
|
||||||
|
} from '../value.mjs';
|
||||||
|
import { Q, X } from '../completion.mjs';
|
||||||
|
import {
|
||||||
|
getMatcher,
|
||||||
|
} from '../runtime-semantics/all.mjs';
|
||||||
|
import {
|
||||||
|
Assert,
|
||||||
|
DefinePropertyOrThrow,
|
||||||
|
OrdinaryCreateFromConstructor,
|
||||||
|
Set,
|
||||||
|
ToString,
|
||||||
|
} from './all.mjs';
|
||||||
|
|
||||||
|
|
||||||
|
// https://tc39.es/proposal-regexp-match-indices/#sec-match-records
|
||||||
|
export class MatchRecord {
|
||||||
|
constructor(StartIndex, EndIndex) {
|
||||||
|
Assert(Number.isInteger(StartIndex) && StartIndex >= 0);
|
||||||
|
Assert(Number.isInteger(EndIndex) && EndIndex >= StartIndex);
|
||||||
|
this.StartIndex = StartIndex;
|
||||||
|
this.EndIndex = EndIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 21.2.3.2.1 #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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 21.2.3.2.2 #sec-regexpinitialize
|
||||||
|
export function RegExpInitialize(obj, pattern, flags) {
|
||||||
|
let P;
|
||||||
|
if (pattern === Value.undefined) {
|
||||||
|
P = new Value('');
|
||||||
|
} else {
|
||||||
|
P = Q(ToString(pattern));
|
||||||
|
}
|
||||||
|
|
||||||
|
let F;
|
||||||
|
if (flags === Value.undefined) {
|
||||||
|
F = new Value('');
|
||||||
|
} else {
|
||||||
|
F = Q(ToString(flags));
|
||||||
|
}
|
||||||
|
|
||||||
|
const f = F.stringValue();
|
||||||
|
if (/^[gimsuy]*$/.test(f) === false || (new globalThis.Set(f).size !== f.length)) {
|
||||||
|
return surroundingAgent.Throw('SyntaxError', 'InvalidRegExpFlags', f);
|
||||||
|
}
|
||||||
|
|
||||||
|
let parsed;
|
||||||
|
try {
|
||||||
|
parsed = ParseRegExp(P.stringValue(), F.stringValue());
|
||||||
|
} catch (e) {
|
||||||
|
return surroundingAgent.Throw('SyntaxError', 'Raw', e.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
obj.OriginalSource = P;
|
||||||
|
obj.OriginalFlags = F;
|
||||||
|
obj.RegExpMatcher = getMatcher(parsed, F.stringValue());
|
||||||
|
obj.parsedRegExp = parsed;
|
||||||
|
|
||||||
|
Q(Set(obj, new Value('lastIndex'), new Value(0), Value.true));
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 21.2.3.2.4 #sec-escaperegexppattern
|
||||||
|
export function EscapeRegExpPattern(P, F) {
|
||||||
|
// TODO: implement this without host
|
||||||
|
const re = new RegExp(P.stringValue(), F.stringValue());
|
||||||
|
return new Value(re.source);
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
import { X } from '../completion.mjs';
|
||||||
|
import { Value } from '../value.mjs';
|
||||||
|
import {
|
||||||
|
Assert,
|
||||||
|
isLeadingSurrogate,
|
||||||
|
isTrailingSurrogate,
|
||||||
|
} from './all.mjs';
|
||||||
|
|
||||||
|
// This file covers abstract operations defined in
|
||||||
|
// 10 #sec-ecmascript-language-source-code
|
||||||
|
|
||||||
|
// 10.1.1 #sec-utf16encoding
|
||||||
|
export function UTF16Encoding(cp) {
|
||||||
|
Assert(cp >= 0 && cp <= 0x10FFFF);
|
||||||
|
if (cp <= 0xFFFF) {
|
||||||
|
return [cp];
|
||||||
|
}
|
||||||
|
const cu1 = Math.floor((cp - 0x10000) / 0x400) + 0xD800;
|
||||||
|
const cu2 = ((cp - 0x10000) % 0x400) + 0xDC00;
|
||||||
|
return [cu1, cu2];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 10.1.2 #sec-utf16encode
|
||||||
|
export function UTF16Encode(text) {
|
||||||
|
return new Value(text.map(UTF16Encoding).join(''));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 10.1.3 #sec-utf16decodesurrogatepair
|
||||||
|
export function UTF16DecodeSurrogatePair(lead, trail) {
|
||||||
|
Assert(isLeadingSurrogate(lead) && isTrailingSurrogate(trail));
|
||||||
|
const cp = (lead - 0xD800) * 0x400 + (trail - 0xDC00) + 0x10000;
|
||||||
|
return cp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 10.1.4 #sec-codepointat
|
||||||
|
export function CodePointAt(string, position) {
|
||||||
|
const size = string.stringValue().length;
|
||||||
|
Assert(position >= 0 && position < size);
|
||||||
|
const first = string.stringValue().charCodeAt(position);
|
||||||
|
let cp = first;
|
||||||
|
if (!isLeadingSurrogate(first) && !isTrailingSurrogate(first)) {
|
||||||
|
return {
|
||||||
|
CodePoint: new Value(cp),
|
||||||
|
CodeUnitCount: new Value(1),
|
||||||
|
IsUnpairedSurrogate: Value.false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (isTrailingSurrogate(first) || position + 1 === size) {
|
||||||
|
return {
|
||||||
|
CodePoint: new Value(cp),
|
||||||
|
CodeUnitCount: new Value(1),
|
||||||
|
IsUnpairedSurrogate: Value.true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const second = string.stringValue().charCodeAt(position + 1);
|
||||||
|
if (!isTrailingSurrogate(second)) {
|
||||||
|
return {
|
||||||
|
CodePoint: new Value(cp),
|
||||||
|
CodeUnitCount: new Value(1),
|
||||||
|
IsUnpairedSurrogate: Value.true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
cp = X(UTF16DecodeSurrogatePair(first, second));
|
||||||
|
return {
|
||||||
|
CodePoint: new Value(cp),
|
||||||
|
CodeUnitCount: new Value(2),
|
||||||
|
IsUnpairedSurrogate: Value.false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 10.1.5 #sec-utf16decodestring
|
||||||
|
export function UTF16DecodeString(string) {
|
||||||
|
const codePoints = [];
|
||||||
|
const size = string.stringValue().length;
|
||||||
|
let position = 0;
|
||||||
|
while (position < size) {
|
||||||
|
const cp = X(CodePointAt(string, position));
|
||||||
|
codePoints.push(cp.CodePoint);
|
||||||
|
position += cp.CodeUnitCount;
|
||||||
|
}
|
||||||
|
return codePoints;
|
||||||
|
}
|
||||||
@@ -0,0 +1,210 @@
|
|||||||
|
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 new NormalCompletion(undefined);
|
||||||
|
}
|
||||||
@@ -0,0 +1,145 @@
|
|||||||
|
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,
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
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()})`);
|
||||||
|
}
|
||||||
@@ -0,0 +1,412 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,469 @@
|
|||||||
|
import {
|
||||||
|
Type,
|
||||||
|
Value,
|
||||||
|
NumberValue,
|
||||||
|
BigIntValue,
|
||||||
|
wellKnownSymbols,
|
||||||
|
} from '../value.mjs';
|
||||||
|
import { MV_StringNumericLiteral } from '../runtime-semantics/all.mjs';
|
||||||
|
import {
|
||||||
|
surroundingAgent,
|
||||||
|
} from '../engine.mjs';
|
||||||
|
import { Q, X } from '../completion.mjs';
|
||||||
|
import { OutOfRange } from '../helpers.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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,240 @@
|
|||||||
|
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-allocatedtypedarray
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// #sec-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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
import { surroundingAgent } from '../engine.mjs';
|
||||||
|
import { Value } from '../value.mjs';
|
||||||
|
import { NormalCompletion, Q } from '../completion.mjs';
|
||||||
|
import { Assert, Call } from './all.mjs';
|
||||||
|
|
||||||
|
// https://tc39.es/proposal-weakrefs/#sec-clear-kept-objects
|
||||||
|
export function ClearKeptObjects() {
|
||||||
|
// 1. Let agent be the surrounding agent.
|
||||||
|
const agent = surroundingAgent;
|
||||||
|
// 2. Set agent.[[KeptAlive]] to a new empty List.
|
||||||
|
agent.KeptAlive = new Set();
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://tc39.es/proposal-weakrefs/#sec-clear-kept-objects
|
||||||
|
export function AddToKeptObjects(object) {
|
||||||
|
// 1. Let agent be the surrounding agent.
|
||||||
|
const agent = surroundingAgent;
|
||||||
|
// 2. Append object to agent.[[KeptAlive]].
|
||||||
|
agent.KeptAlive.add(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://tc39.es/proposal-weakrefs/#sec-cleanup-finalization-registry
|
||||||
|
export function CleanupFinalizationRegistry(finalizationRegistry, callback) {
|
||||||
|
// 1. Assert: finalizationRegistry has [[Cells]] and [[CleanupCallback]] internal slots.
|
||||||
|
Assert('Cells' in finalizationRegistry);
|
||||||
|
// 2. If callback is not present or undefined, 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, do
|
||||||
|
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);
|
||||||
|
}
|
||||||
@@ -0,0 +1,388 @@
|
|||||||
|
import {
|
||||||
|
CreateRealm,
|
||||||
|
SetDefaultGlobalBindings,
|
||||||
|
SetRealmGlobalObject,
|
||||||
|
} from './realm.mjs';
|
||||||
|
import {
|
||||||
|
ExecutionContext,
|
||||||
|
surroundingAgent,
|
||||||
|
setSurroundingAgent,
|
||||||
|
evaluateScript,
|
||||||
|
Agent,
|
||||||
|
HostCleanupFinalizationRegistry,
|
||||||
|
FEATURES,
|
||||||
|
} from './engine.mjs';
|
||||||
|
import {
|
||||||
|
Descriptor,
|
||||||
|
Type,
|
||||||
|
Value,
|
||||||
|
} from './value.mjs';
|
||||||
|
import { ParseModule } from './parse.mjs';
|
||||||
|
import {
|
||||||
|
AbruptCompletion,
|
||||||
|
Completion,
|
||||||
|
NormalCompletion,
|
||||||
|
Q, X,
|
||||||
|
ThrowCompletion,
|
||||||
|
} from './completion.mjs';
|
||||||
|
import * as AbstractOps from './abstract-ops/all.mjs';
|
||||||
|
|
||||||
|
export const Abstract = { ...AbstractOps, Type };
|
||||||
|
const {
|
||||||
|
OrdinaryObjectCreate,
|
||||||
|
CreateBuiltinFunction,
|
||||||
|
GetModuleNamespace,
|
||||||
|
ToPrimitive,
|
||||||
|
} = Abstract;
|
||||||
|
export {
|
||||||
|
AbruptCompletion,
|
||||||
|
NormalCompletion,
|
||||||
|
Completion,
|
||||||
|
Descriptor,
|
||||||
|
FEATURES,
|
||||||
|
};
|
||||||
|
|
||||||
|
export { inspect } from './inspect.mjs';
|
||||||
|
|
||||||
|
function mark() {
|
||||||
|
// https://tc39.es/proposal-weakrefs/#sec-weakref-execution
|
||||||
|
// At any time, if a set of objects S is not live, an ECMAScript implementation may perform the following steps automically:
|
||||||
|
// 1. For each obj os 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, such that cell.[[WeakRefTarget]] is obj,
|
||||||
|
// i. Set cell.[[WeakRefTarget]] to empty.
|
||||||
|
// ii. Optionally, perform ! HostCleanupFinalizationRegistry(fg).
|
||||||
|
// c. For each WeakMap map such that map.WeakMapData contains a record r such that r.Key is obj,
|
||||||
|
// i. Remove r from map.WeakMapData.
|
||||||
|
// d. For each WeakSet set such that set.WeakSetData contains obj,
|
||||||
|
// i. Remove obj from WeakSetData.
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (surroundingAgent.feature('WeakRefs')) {
|
||||||
|
AbstractOps.ClearKeptObjects();
|
||||||
|
}
|
||||||
|
|
||||||
|
markCb(surroundingAgent);
|
||||||
|
|
||||||
|
while (ephemeronQueue.length > 0) {
|
||||||
|
const item = ephemeronQueue.shift();
|
||||||
|
if (marked.has(item.Key)) {
|
||||||
|
markCb(item.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (surroundingAgent.feature('WeakRefs')) {
|
||||||
|
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(HostCleanupFinalizationRegistry(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
|
||||||
|
function runJobQueue() {
|
||||||
|
if (surroundingAgent.executionContextStack.length !== 0) {
|
||||||
|
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 (true) { // eslint-disable-line no-constant-condition
|
||||||
|
if (surroundingAgent.jobQueue.length === 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const {
|
||||||
|
job: abstractClosure,
|
||||||
|
callerRealm,
|
||||||
|
callerScriptOrModule,
|
||||||
|
} = surroundingAgent.jobQueue.shift();
|
||||||
|
|
||||||
|
// 1. Push an execution context onto the execution context stack.
|
||||||
|
const newContext = new ExecutionContext();
|
||||||
|
surroundingAgent.executionContextStack.push(newContext);
|
||||||
|
// 2. Perform any implementation-defined preparation steps.
|
||||||
|
newContext.Function = Value.null;
|
||||||
|
newContext.Realm = callerRealm;
|
||||||
|
newContext.ScriptOrModule = callerScriptOrModule;
|
||||||
|
// 3. Call the abstract closure.
|
||||||
|
X(abstractClosure());
|
||||||
|
// 4. Perform any implementation-defined cleanup steps.
|
||||||
|
mark();
|
||||||
|
// 5. Pop the previously-pushed execution context from the execution context stack.
|
||||||
|
surroundingAgent.executionContextStack.pop(newContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class APIAgent {
|
||||||
|
constructor(options = {}) {
|
||||||
|
this.agent = new Agent(options);
|
||||||
|
this.active = false;
|
||||||
|
this.outerAgent = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
scope(cb) {
|
||||||
|
this.enter();
|
||||||
|
try {
|
||||||
|
return cb();
|
||||||
|
} finally {
|
||||||
|
this.exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enter() {
|
||||||
|
if (this.active) {
|
||||||
|
throw new Error('Agent is already entered');
|
||||||
|
}
|
||||||
|
this.active = true;
|
||||||
|
this.outerAgent = surroundingAgent;
|
||||||
|
setSurroundingAgent(this.agent);
|
||||||
|
}
|
||||||
|
|
||||||
|
exit() {
|
||||||
|
if (!this.active) {
|
||||||
|
throw new Error('Agent is not entered');
|
||||||
|
}
|
||||||
|
setSurroundingAgent(this.outerAgent);
|
||||||
|
this.outerAgent = undefined;
|
||||||
|
this.active = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class APIRealm {
|
||||||
|
constructor(options = {}) {
|
||||||
|
const realm = CreateRealm();
|
||||||
|
|
||||||
|
realm.HostDefined = options;
|
||||||
|
|
||||||
|
const newContext = new ExecutionContext();
|
||||||
|
newContext.Function = Value.null;
|
||||||
|
newContext.Realm = realm;
|
||||||
|
newContext.ScriptOrModule = Value.null;
|
||||||
|
surroundingAgent.executionContextStack.push(newContext);
|
||||||
|
const global = Value.undefined;
|
||||||
|
const thisValue = Value.undefined;
|
||||||
|
SetRealmGlobalObject(realm, global, thisValue);
|
||||||
|
const globalObj = SetDefaultGlobalBindings(realm);
|
||||||
|
|
||||||
|
// Create any implementation-defined global object properties on globalObj.
|
||||||
|
|
||||||
|
surroundingAgent.executionContextStack.pop(newContext);
|
||||||
|
|
||||||
|
this.global = globalObj;
|
||||||
|
this.realm = realm;
|
||||||
|
this.context = newContext;
|
||||||
|
this.agent = surroundingAgent;
|
||||||
|
|
||||||
|
this.active = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
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.realm, {
|
||||||
|
specifier,
|
||||||
|
public: {
|
||||||
|
specifier,
|
||||||
|
Link: () => this.scope(() => module.Link()),
|
||||||
|
GetNamespace: () => this.scope(() => GetModuleNamespace(module)),
|
||||||
|
Evaluate: () => {
|
||||||
|
const res = this.scope(() => module.Evaluate());
|
||||||
|
if (!(res instanceof AbruptCompletion)) {
|
||||||
|
runJobQueue();
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
if (Array.isArray(module)) {
|
||||||
|
return new ThrowCompletion(module[0]);
|
||||||
|
}
|
||||||
|
module.HostDefined.public.module = module;
|
||||||
|
return module.HostDefined.public;
|
||||||
|
}
|
||||||
|
|
||||||
|
scope(cb) {
|
||||||
|
if (this.active) {
|
||||||
|
return cb();
|
||||||
|
}
|
||||||
|
this.active = true;
|
||||||
|
surroundingAgent.executionContextStack.push(this.context);
|
||||||
|
const r = cb();
|
||||||
|
surroundingAgent.executionContextStack.pop(this.context);
|
||||||
|
this.active = false;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function APIObject(realm, intrinsic = '%Object.prototype%') {
|
||||||
|
return OrdinaryObjectCreate(realm.realm.Intrinsics[intrinsic]);
|
||||||
|
}
|
||||||
|
|
||||||
|
class APIValue extends Value {
|
||||||
|
constructor(realm, value) {
|
||||||
|
if (typeof value === 'function') {
|
||||||
|
return CreateBuiltinFunction(value, [], realm.realm);
|
||||||
|
}
|
||||||
|
if (value === undefined) {
|
||||||
|
return Value.undefined;
|
||||||
|
}
|
||||||
|
if (value === null) {
|
||||||
|
return Value.null;
|
||||||
|
}
|
||||||
|
if (value === true) {
|
||||||
|
return Value.true;
|
||||||
|
}
|
||||||
|
if (value === false) {
|
||||||
|
return Value.false;
|
||||||
|
}
|
||||||
|
return new Value(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static [Symbol.hasInstance](v) {
|
||||||
|
return v instanceof Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
APIAgent as Agent,
|
||||||
|
APIRealm as Realm,
|
||||||
|
APIValue as Value,
|
||||||
|
APIObject as Object,
|
||||||
|
};
|
||||||
|
|
||||||
|
export function Throw(realm, V, ...args) {
|
||||||
|
return realm.scope(() => {
|
||||||
|
if (typeof V === 'string') {
|
||||||
|
// eslint-disable-next-line engine262/valid-throw
|
||||||
|
return surroundingAgent.Throw(V, 'Raw', args[0]);
|
||||||
|
}
|
||||||
|
return new ThrowCompletion(V);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ToString(realm, value) {
|
||||||
|
return realm.scope(() => {
|
||||||
|
while (true) {
|
||||||
|
const type = Type(value);
|
||||||
|
switch (type) {
|
||||||
|
case 'String':
|
||||||
|
return value.stringValue();
|
||||||
|
case 'Number':
|
||||||
|
return value.numberValue().toString();
|
||||||
|
case 'Boolean':
|
||||||
|
return value === Value.true ? 'true' : 'false';
|
||||||
|
case 'Undefined':
|
||||||
|
return 'undefined';
|
||||||
|
case 'Null':
|
||||||
|
return 'null';
|
||||||
|
case 'Symbol':
|
||||||
|
return surroundingAgent.Throw('TypeError', 'CannotConvertSymbol', 'string');
|
||||||
|
default:
|
||||||
|
value = Q(ToPrimitive(value, 'String'));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,176 @@
|
|||||||
|
import { surroundingAgent } from './engine.mjs';
|
||||||
|
import {
|
||||||
|
Assert,
|
||||||
|
CreateBuiltinFunction,
|
||||||
|
PerformPromiseThen,
|
||||||
|
PromiseResolve,
|
||||||
|
SetFunctionLength,
|
||||||
|
} from './abstract-ops/all.mjs';
|
||||||
|
import { Reference, Value } from './value.mjs';
|
||||||
|
import { resume } from './helpers.mjs';
|
||||||
|
|
||||||
|
// 6.2.3 #sec-completion-record-specification-type
|
||||||
|
export function Completion(type, value, target) {
|
||||||
|
if (new.target === Completion) {
|
||||||
|
if (typeof type !== 'string') {
|
||||||
|
throw new TypeError('Completion type is not a string');
|
||||||
|
}
|
||||||
|
this.Type = type;
|
||||||
|
this.Value = value;
|
||||||
|
this.Target = target;
|
||||||
|
}
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NON-SPEC
|
||||||
|
Completion.prototype.mark = function mark(m) {
|
||||||
|
m(this.Value);
|
||||||
|
};
|
||||||
|
|
||||||
|
// #sec-normalcompletion
|
||||||
|
export function NormalCompletion(value) {
|
||||||
|
return new Completion('normal', value);
|
||||||
|
}
|
||||||
|
|
||||||
|
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';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class BreakCompletion {
|
||||||
|
constructor(target) {
|
||||||
|
return new Completion('break', undefined, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
static [Symbol.hasInstance](v) {
|
||||||
|
return v instanceof Completion && v.Type === 'break';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ContinueCompletion {
|
||||||
|
constructor(target) {
|
||||||
|
return new Completion('continue', undefined, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
static [Symbol.hasInstance](v) {
|
||||||
|
return v instanceof Completion && v.Type === 'continue';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6.2.3.2 #sec-normalcompletion
|
||||||
|
export class ReturnCompletion {
|
||||||
|
constructor(value) {
|
||||||
|
return new Completion('return', value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static [Symbol.hasInstance](v) {
|
||||||
|
return v instanceof Completion && v.Type === 'return';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6.2.3.3 #sec-throwcompletion
|
||||||
|
export function ThrowCompletion(value) {
|
||||||
|
return new Completion('throw', value);
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.defineProperty(ThrowCompletion, Symbol.hasInstance, {
|
||||||
|
value: function hasInstance(v) {
|
||||||
|
return v instanceof Completion && v.Type === 'throw';
|
||||||
|
},
|
||||||
|
writable: true,
|
||||||
|
enumerable: false,
|
||||||
|
configurable: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 6.2.3.4 #sec-updateempty
|
||||||
|
export function UpdateEmpty(completionRecord, value) {
|
||||||
|
Assert(completionRecord instanceof Completion);
|
||||||
|
if (completionRecord.Type === 'return' || completionRecord.Type === 'throw') {
|
||||||
|
Assert(completionRecord.Value !== undefined);
|
||||||
|
}
|
||||||
|
if (completionRecord.Value !== undefined) {
|
||||||
|
return completionRecord;
|
||||||
|
}
|
||||||
|
return new Completion(completionRecord.Type, value, completionRecord.Target);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5.2.3.3 #sec-returnifabrupt
|
||||||
|
export function ReturnIfAbrupt() {
|
||||||
|
throw new TypeError('ReturnIfAbrupt requires build');
|
||||||
|
}
|
||||||
|
|
||||||
|
// #sec-returnifabrupt-shorthands ? OperationName()
|
||||||
|
export const Q = ReturnIfAbrupt;
|
||||||
|
|
||||||
|
// #sec-returnifabrupt-shorthands ! OperationName()
|
||||||
|
export function X(val) {
|
||||||
|
Assert(!(val instanceof AbruptCompletion));
|
||||||
|
if (val instanceof Completion) {
|
||||||
|
return val.Value;
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 25.6.1.1.1 #sec-ifabruptrejectpromise
|
||||||
|
export function IfAbruptRejectPromise() {
|
||||||
|
throw new TypeError('IfAbruptRejectPromise requires build');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function EnsureCompletion(val) {
|
||||||
|
if (val instanceof Completion) {
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
if (val instanceof Reference) {
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
return new 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, new 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, new 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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,403 @@
|
|||||||
|
import { Value } from './value.mjs';
|
||||||
|
import {
|
||||||
|
AbruptCompletion,
|
||||||
|
EnsureCompletion,
|
||||||
|
NormalCompletion,
|
||||||
|
ThrowCompletion,
|
||||||
|
Q, X,
|
||||||
|
} from './completion.mjs';
|
||||||
|
import {
|
||||||
|
CreateRealm,
|
||||||
|
SetDefaultGlobalBindings,
|
||||||
|
SetRealmGlobalObject,
|
||||||
|
} from './realm.mjs';
|
||||||
|
import {
|
||||||
|
Call, Construct, Assert, GetModuleNamespace,
|
||||||
|
PerformPromiseThen, CreateBuiltinFunction,
|
||||||
|
GetActiveScriptOrModule,
|
||||||
|
CleanupFinalizationRegistry,
|
||||||
|
} from './abstract-ops/all.mjs';
|
||||||
|
import { ParseScript } from './parse.mjs';
|
||||||
|
import { GlobalDeclarationInstantiation } from './runtime-semantics/all.mjs';
|
||||||
|
import { Evaluate_Script } from './evaluator.mjs';
|
||||||
|
import { CyclicModuleRecord } from './modules.mjs';
|
||||||
|
import { CallSite } from './helpers.mjs';
|
||||||
|
import * as messages from './messages.mjs';
|
||||||
|
|
||||||
|
export const FEATURES = Object.freeze([
|
||||||
|
{
|
||||||
|
name: 'TopLevelAwait',
|
||||||
|
url: 'https://github.com/tc39/proposal-top-level-await',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'WeakRefs',
|
||||||
|
url: 'https://github.com/tc39/proposal-weakrefs',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'LogicalAssignment',
|
||||||
|
url: 'https://github.com/tc39/proposal-logical-assignment',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Promise.any',
|
||||||
|
url: 'https://github.com/tc39/proposal-promise-any',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'RegExpMatchIndices',
|
||||||
|
url: 'https://github.com/tc39/proposal-regexp-match-Indices',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'String.prototype.replaceAll',
|
||||||
|
url: 'https://github.com/tc39/proposal-string-replaceall',
|
||||||
|
},
|
||||||
|
].map(Object.freeze));
|
||||||
|
|
||||||
|
// #sec-agents
|
||||||
|
export class Agent {
|
||||||
|
constructor(options = {}) {
|
||||||
|
this.LittleEndian = Value.true;
|
||||||
|
this.CanBlock = true;
|
||||||
|
this.Signifier = Agent.Increment;
|
||||||
|
Agent.Increment += 1;
|
||||||
|
this.IsLockFree1 = true;
|
||||||
|
this.IsLockFree2 = true;
|
||||||
|
this.CandidateExecution = undefined;
|
||||||
|
|
||||||
|
this.executionContextStack = [];
|
||||||
|
const stackPop = this.executionContextStack.pop;
|
||||||
|
this.executionContextStack.pop = function pop(ctx) {
|
||||||
|
if (!ctx.poppedForTailCall) {
|
||||||
|
const popped = stackPop.call(this);
|
||||||
|
Assert(popped === ctx);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.jobQueue = [];
|
||||||
|
|
||||||
|
this.hostDefinedOptions = {
|
||||||
|
...options,
|
||||||
|
features: FEATURES.reduce((acc, { name }) => {
|
||||||
|
if (options.features) {
|
||||||
|
acc[name] = options.features.includes(name);
|
||||||
|
} else {
|
||||||
|
acc[name] = false;
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, {}),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.feature('WeakRefs')) {
|
||||||
|
this.KeptAlive = new Set();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// #sec-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 new ThrowCompletion(type);
|
||||||
|
}
|
||||||
|
const message = messages[template](...templateArgs);
|
||||||
|
const cons = this.currentRealmRecord.Intrinsics[`%${type}%`];
|
||||||
|
let error;
|
||||||
|
if (type === 'AggregateError') {
|
||||||
|
error = X(Construct(cons, [
|
||||||
|
Symbol.for('engine262.placeholder'),
|
||||||
|
new Value(message),
|
||||||
|
]));
|
||||||
|
} else {
|
||||||
|
error = X(Construct(cons, [new Value(message)]));
|
||||||
|
}
|
||||||
|
return new 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.executionContextStack.forEach((e) => {
|
||||||
|
m(e);
|
||||||
|
});
|
||||||
|
this.jobQueue.forEach((j) => {
|
||||||
|
m(j.callerRealm);
|
||||||
|
m(j.callerScriptOrModule);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Agent.Increment = 0;
|
||||||
|
|
||||||
|
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) {
|
||||||
|
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.body;
|
||||||
|
let result = EnsureCompletion(GlobalDeclarationInstantiation(scriptBody, globalEnv));
|
||||||
|
|
||||||
|
if (result.Type === 'normal') {
|
||||||
|
result = Evaluate_Script(scriptBody, globalEnv);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.Type === 'normal' && !result.Value) {
|
||||||
|
result = new NormalCompletion(Value.undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Suspend scriptCtx
|
||||||
|
surroundingAgent.executionContextStack.pop(scriptContext);
|
||||||
|
// Resume(surroundingAgent.runningExecutionContext);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function evaluateScript(sourceText, realm, hostDefined) {
|
||||||
|
const s = ParseScript(sourceText, realm, hostDefined);
|
||||||
|
if (Array.isArray(s)) {
|
||||||
|
return new ThrowCompletion(s[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return EnsureCompletion(ScriptEvaluation(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
// #sec-hostenqueuepromisejob
|
||||||
|
export function HostEnqueuePromiseJob(job, _realm) {
|
||||||
|
surroundingAgent.queueJob('PromiseJobs', job);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 8.5 #sec-initializehostdefinedrealm
|
||||||
|
export function InitializeHostDefinedRealm() {
|
||||||
|
const realm = CreateRealm();
|
||||||
|
const newContext = new ExecutionContext();
|
||||||
|
newContext.Function = Value.null;
|
||||||
|
newContext.Realm = realm;
|
||||||
|
newContext.ScriptOrModule = Value.null;
|
||||||
|
surroundingAgent.executionContextStack.push(newContext);
|
||||||
|
const global = Value.undefined;
|
||||||
|
const thisValue = Value.undefined;
|
||||||
|
SetRealmGlobalObject(realm, global, thisValue);
|
||||||
|
SetDefaultGlobalBindings(realm);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 8.7.1 #sec-agentsignifier
|
||||||
|
export function AgentSignifier() {
|
||||||
|
const AR = surroundingAgent;
|
||||||
|
return AR.Signifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function HostEnsureCanCompileStrings(callerRealm, calleeRealm) {
|
||||||
|
if (surroundingAgent.hostDefinedOptions.ensureCanCompileStrings !== undefined) {
|
||||||
|
Q(surroundingAgent.hostDefinedOptions.ensureCanCompileStrings(callerRealm, calleeRealm));
|
||||||
|
}
|
||||||
|
return new 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 publicModule = referencingScriptOrModule.HostDefined ? referencingScriptOrModule.HostDefined.public : null;
|
||||||
|
const apiModule = Q(realm.HostDefined.resolveImportedModule(publicModule, specifier));
|
||||||
|
if (referencingScriptOrModule !== Value.null) {
|
||||||
|
referencingScriptOrModule.HostDefined.moduleMap.set(specifier, apiModule.module);
|
||||||
|
}
|
||||||
|
return apiModule.module;
|
||||||
|
}
|
||||||
|
return surroundingAgent.Throw('Error', 'CouldNotResolveModule', specifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
function FinishDynamicImport(referencingScriptOrModule, specifier, promiseCapability, completion) {
|
||||||
|
if (completion instanceof AbruptCompletion) {
|
||||||
|
X(Call(promiseCapability.Reject, Value.undefined, [completion.Value]));
|
||||||
|
} else {
|
||||||
|
Assert(completion instanceof NormalCompletion);
|
||||||
|
const onFulfilled = X(CreateBuiltinFunction(([v = Value.undefined]) => {
|
||||||
|
Assert(v === Value.undefined);
|
||||||
|
const moduleRecord = X(HostResolveImportedModule(referencingScriptOrModule, specifier));
|
||||||
|
// Assert: Evaluate has already been invoked on moduleRecord and successfully completed.
|
||||||
|
const namespace = EnsureCompletion(GetModuleNamespace(moduleRecord));
|
||||||
|
if (namespace instanceof AbruptCompletion) {
|
||||||
|
X(Call(promiseCapability.Reject, Value.undefined, [namespace.Value]));
|
||||||
|
} else {
|
||||||
|
X(Call(promiseCapability.Resolve, Value.undefined, [namespace.Value]));
|
||||||
|
}
|
||||||
|
return Value.undefined;
|
||||||
|
}, []));
|
||||||
|
const onRejected = X(CreateBuiltinFunction(([r = Value.undefined]) => {
|
||||||
|
X(Call(promiseCapability.Reject, Value.undefined, [r]));
|
||||||
|
return Value.undefined;
|
||||||
|
}, []));
|
||||||
|
X(PerformPromiseThen(completion.Value, onFulfilled, onRejected));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function HostImportModuleDynamically(referencingScriptOrModule, specifier, promiseCapability) {
|
||||||
|
surroundingAgent.queueJob('ImportModuleDynamicallyJobs', () => {
|
||||||
|
let completion = EnsureCompletion(HostResolveImportedModule(referencingScriptOrModule, specifier));
|
||||||
|
if (!(completion instanceof AbruptCompletion)) {
|
||||||
|
const module = completion.Value;
|
||||||
|
if (module instanceof CyclicModuleRecord) {
|
||||||
|
if (module.HostDefined.cachedCompletion) {
|
||||||
|
completion = module.HostDefined.cachedCompletion;
|
||||||
|
} else {
|
||||||
|
if (module.Status !== 'linking' && module.Status !== 'evaluating') {
|
||||||
|
completion = EnsureCompletion(module.Link());
|
||||||
|
}
|
||||||
|
if (!(completion instanceof AbruptCompletion)) {
|
||||||
|
completion = EnsureCompletion(module.Evaluate());
|
||||||
|
module.HostDefined.cachedCompletion = completion;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
completion = EnsureCompletion(module.Link());
|
||||||
|
if (!(completion instanceof AbruptCompletion)) {
|
||||||
|
completion = EnsureCompletion(module.Evaluate());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FinishDynamicImport(referencingScriptOrModule, specifier, promiseCapability, completion);
|
||||||
|
});
|
||||||
|
return new 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://tc39.es/proposal-weakrefs/#sec-host-cleanup-finalization-registry
|
||||||
|
const scheduledForCleanup = new Set();
|
||||||
|
export function HostCleanupFinalizationRegistry(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 new NormalCompletion(undefined);
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,516 @@
|
|||||||
|
import {
|
||||||
|
Completion,
|
||||||
|
EnsureCompletion,
|
||||||
|
NormalCompletion,
|
||||||
|
ReturnIfAbrupt,
|
||||||
|
UpdateEmpty,
|
||||||
|
} from './completion.mjs';
|
||||||
|
import {
|
||||||
|
isActualAdditiveExpression,
|
||||||
|
isActualAssignmentExpression,
|
||||||
|
isActualBitwiseANDExpression,
|
||||||
|
isActualBitwiseORExpression,
|
||||||
|
isActualBitwiseXORExpression,
|
||||||
|
isActualCallExpression,
|
||||||
|
isActualCoalesceExpression,
|
||||||
|
isActualConditionalExpression,
|
||||||
|
isActualEqualityExpression,
|
||||||
|
isActualExponentiationExpression,
|
||||||
|
isActualLogicalANDExpression,
|
||||||
|
isActualLogicalORExpression,
|
||||||
|
isActualMemberExpression,
|
||||||
|
isActualMultiplicativeExpression,
|
||||||
|
isActualNewExpression,
|
||||||
|
isActualRelationalExpression,
|
||||||
|
isActualShiftExpression,
|
||||||
|
isActualUnaryExpression,
|
||||||
|
isActualUpdateExpression,
|
||||||
|
isArrayLiteral,
|
||||||
|
isArrowFunction,
|
||||||
|
isAsyncArrowFunction,
|
||||||
|
isAsyncFunctionExpression,
|
||||||
|
isAsyncGeneratorExpression,
|
||||||
|
isAwaitExpression,
|
||||||
|
isBlockStatement,
|
||||||
|
isBreakStatement,
|
||||||
|
isBreakableStatement,
|
||||||
|
isClassDeclaration,
|
||||||
|
isClassExpression,
|
||||||
|
isContinueStatement,
|
||||||
|
isDebuggerStatement,
|
||||||
|
isDeclaration,
|
||||||
|
isEmptyStatement,
|
||||||
|
isExportDeclaration,
|
||||||
|
isExpression,
|
||||||
|
isExpressionStatement,
|
||||||
|
isExpressionWithComma,
|
||||||
|
isFunctionExpression,
|
||||||
|
isImportCall,
|
||||||
|
isGeneratorExpression,
|
||||||
|
isHoistableDeclaration,
|
||||||
|
isIdentifierReference,
|
||||||
|
isIfStatement,
|
||||||
|
isImportDeclaration,
|
||||||
|
isLabelledStatement,
|
||||||
|
isLexicalDeclaration,
|
||||||
|
isLiteral,
|
||||||
|
isMetaProperty,
|
||||||
|
isObjectLiteral,
|
||||||
|
isOptionalExpression,
|
||||||
|
isParenthesizedExpression,
|
||||||
|
isRegularExpressionLiteral,
|
||||||
|
isReturnStatement,
|
||||||
|
isStatement,
|
||||||
|
isSuperCall,
|
||||||
|
isSuperProperty,
|
||||||
|
isTaggedTemplate,
|
||||||
|
isTemplateLiteral,
|
||||||
|
isThis,
|
||||||
|
isThrowStatement,
|
||||||
|
isTryStatement,
|
||||||
|
isVariableStatement,
|
||||||
|
isWithStatement,
|
||||||
|
isYieldExpression,
|
||||||
|
} from './ast.mjs';
|
||||||
|
import {
|
||||||
|
EvaluateBinopValues_AdditiveExpression_Minus,
|
||||||
|
EvaluateBinopValues_AdditiveExpression_Plus,
|
||||||
|
EvaluateBinopValues_BitwiseANDExpression,
|
||||||
|
EvaluateBinopValues_BitwiseORExpression,
|
||||||
|
EvaluateBinopValues_BitwiseXORExpression,
|
||||||
|
EvaluateBinopValues_ExponentiationExpression,
|
||||||
|
EvaluateBinopValues_MultiplicativeExpression,
|
||||||
|
EvaluateBinopValues_ShiftExpression,
|
||||||
|
Evaluate_AdditiveExpression,
|
||||||
|
Evaluate_ArrayLiteral,
|
||||||
|
Evaluate_ArrowFunction,
|
||||||
|
Evaluate_AssignmentExpression,
|
||||||
|
Evaluate_AsyncArrowFunction,
|
||||||
|
Evaluate_AsyncFunctionExpression,
|
||||||
|
Evaluate_AsyncGeneratorExpression,
|
||||||
|
Evaluate_AwaitExpression,
|
||||||
|
Evaluate_BinaryBitwiseExpression,
|
||||||
|
Evaluate_BlockStatement,
|
||||||
|
Evaluate_BreakStatement,
|
||||||
|
Evaluate_BreakableStatement,
|
||||||
|
Evaluate_CallExpression,
|
||||||
|
Evaluate_ClassDeclaration,
|
||||||
|
Evaluate_ClassExpression,
|
||||||
|
Evaluate_CoalesceExpression,
|
||||||
|
Evaluate_ConditionalExpression,
|
||||||
|
Evaluate_ContinueStatement,
|
||||||
|
Evaluate_DebuggerStatement,
|
||||||
|
Evaluate_EmptyStatement,
|
||||||
|
Evaluate_EqualityExpression,
|
||||||
|
Evaluate_ExponentiationExpression,
|
||||||
|
Evaluate_ExpressionWithComma,
|
||||||
|
Evaluate_ExportDeclaration,
|
||||||
|
Evaluate_FunctionExpression,
|
||||||
|
Evaluate_GeneratorExpression,
|
||||||
|
Evaluate_HoistableDeclaration,
|
||||||
|
Evaluate_Identifier,
|
||||||
|
Evaluate_IfStatement,
|
||||||
|
Evaluate_ImportCall,
|
||||||
|
Evaluate_LabelledStatement,
|
||||||
|
Evaluate_LexicalDeclaration,
|
||||||
|
Evaluate_Literal,
|
||||||
|
Evaluate_LogicalANDExpression,
|
||||||
|
Evaluate_LogicalORExpression,
|
||||||
|
Evaluate_MemberExpression,
|
||||||
|
Evaluate_MetaProperty,
|
||||||
|
Evaluate_MultiplicativeExpression,
|
||||||
|
Evaluate_NewExpression,
|
||||||
|
Evaluate_ObjectLiteral,
|
||||||
|
Evaluate_OptionalExpression,
|
||||||
|
Evaluate_RegularExpressionLiteral,
|
||||||
|
Evaluate_RelationalExpression,
|
||||||
|
Evaluate_ReturnStatement,
|
||||||
|
Evaluate_ShiftExpression,
|
||||||
|
Evaluate_SuperCall,
|
||||||
|
Evaluate_SuperProperty,
|
||||||
|
Evaluate_TaggedTemplate,
|
||||||
|
Evaluate_TemplateLiteral,
|
||||||
|
Evaluate_ThisExpression,
|
||||||
|
Evaluate_ThrowStatement,
|
||||||
|
Evaluate_TryStatement,
|
||||||
|
Evaluate_UnaryExpression,
|
||||||
|
Evaluate_UpdateExpression,
|
||||||
|
Evaluate_VariableStatement,
|
||||||
|
Evaluate_WithStatement,
|
||||||
|
Evaluate_YieldExpression,
|
||||||
|
} from './runtime-semantics/all.mjs';
|
||||||
|
import { Value } from './value.mjs';
|
||||||
|
import { GetValue } from './abstract-ops/all.mjs';
|
||||||
|
import { surroundingAgent } from './engine.mjs';
|
||||||
|
import { unwind, OutOfRange } from './helpers.mjs';
|
||||||
|
|
||||||
|
// 13.2.13 #sec-block-runtime-semantics-evaluation
|
||||||
|
// StatementList : StatementList StatementListItem
|
||||||
|
//
|
||||||
|
// (implicit)
|
||||||
|
// StatementList : StatementListItem
|
||||||
|
export function* Evaluate_StatementList(StatementList) {
|
||||||
|
if (StatementList.length === 0) {
|
||||||
|
return new NormalCompletion(undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
let sl = yield* Evaluate(StatementList[0]);
|
||||||
|
if (StatementList.length === 1) {
|
||||||
|
return sl;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const StatementListItem of StatementList.slice(1)) {
|
||||||
|
ReturnIfAbrupt(sl);
|
||||||
|
let s = yield* Evaluate(StatementListItem);
|
||||||
|
// We don't always return a Completion value, but here we actually need it
|
||||||
|
// to be a Completion.
|
||||||
|
s = EnsureCompletion(s);
|
||||||
|
sl = UpdateEmpty(s, sl);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 15.2.1.23 #sec-module-semantics-runtime-semantics-evaluation
|
||||||
|
// ModuleItemList :
|
||||||
|
// ModuleItem
|
||||||
|
// ModuleItemList ModuleItem
|
||||||
|
export function* Evaluate_ModuleItemList(ModuleItemList) {
|
||||||
|
let sl = yield* Evaluate(ModuleItemList[0]);
|
||||||
|
if (ModuleItemList.length === 1) {
|
||||||
|
return sl;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const ModuleItemListItem of ModuleItemList.slice(1)) {
|
||||||
|
ReturnIfAbrupt(sl);
|
||||||
|
let s = yield* Evaluate(ModuleItemListItem);
|
||||||
|
// We don't always return a Completion value, but here we actually need it
|
||||||
|
// to be a Completion.
|
||||||
|
s = EnsureCompletion(s);
|
||||||
|
sl = UpdateEmpty(s, sl);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// (implicit)
|
||||||
|
// StatementListItem :
|
||||||
|
// Statement
|
||||||
|
// Declaration
|
||||||
|
//
|
||||||
|
// Statement :
|
||||||
|
// BlockStatement
|
||||||
|
// ExpressionStatement
|
||||||
|
// VariableStatement
|
||||||
|
// EmptyStatement
|
||||||
|
// ExpressionStatement
|
||||||
|
// IfStatement
|
||||||
|
// BreakableStatement
|
||||||
|
// ContinueStatement
|
||||||
|
// BreakStatement
|
||||||
|
// ReturnStatement
|
||||||
|
// WithStatement
|
||||||
|
// LabelledStatement
|
||||||
|
// ThrowStatement
|
||||||
|
// TryStatement
|
||||||
|
// DebuggerStatement
|
||||||
|
//
|
||||||
|
// Declaration :
|
||||||
|
// HoistableDeclaration
|
||||||
|
// ClassDeclaration
|
||||||
|
// LexicalDeclaration
|
||||||
|
function* Evaluate_StatementListItem(StatementListItem) {
|
||||||
|
switch (true) {
|
||||||
|
case isBlockStatement(StatementListItem):
|
||||||
|
return yield* Evaluate_BlockStatement(StatementListItem);
|
||||||
|
|
||||||
|
case isVariableStatement(StatementListItem):
|
||||||
|
return yield* Evaluate_VariableStatement(StatementListItem);
|
||||||
|
|
||||||
|
case isEmptyStatement(StatementListItem):
|
||||||
|
return Evaluate_EmptyStatement(StatementListItem);
|
||||||
|
|
||||||
|
case isExpressionStatement(StatementListItem):
|
||||||
|
return yield* Evaluate_ExpressionStatement(StatementListItem);
|
||||||
|
|
||||||
|
case isIfStatement(StatementListItem):
|
||||||
|
return yield* Evaluate_IfStatement(StatementListItem);
|
||||||
|
|
||||||
|
case isBreakableStatement(StatementListItem):
|
||||||
|
return yield* Evaluate_BreakableStatement(StatementListItem);
|
||||||
|
|
||||||
|
case isContinueStatement(StatementListItem):
|
||||||
|
return Evaluate_ContinueStatement(StatementListItem);
|
||||||
|
|
||||||
|
case isBreakStatement(StatementListItem):
|
||||||
|
return Evaluate_BreakStatement(StatementListItem);
|
||||||
|
|
||||||
|
case isReturnStatement(StatementListItem):
|
||||||
|
return yield* Evaluate_ReturnStatement(StatementListItem);
|
||||||
|
|
||||||
|
case isWithStatement(StatementListItem):
|
||||||
|
return yield* Evaluate_WithStatement(StatementListItem);
|
||||||
|
|
||||||
|
case isLabelledStatement(StatementListItem):
|
||||||
|
return yield* Evaluate_LabelledStatement(StatementListItem);
|
||||||
|
|
||||||
|
case isThrowStatement(StatementListItem):
|
||||||
|
return yield* Evaluate_ThrowStatement(StatementListItem.argument);
|
||||||
|
|
||||||
|
case isTryStatement(StatementListItem):
|
||||||
|
return yield* Evaluate_TryStatement(StatementListItem);
|
||||||
|
|
||||||
|
case isDebuggerStatement(StatementListItem):
|
||||||
|
return Evaluate_DebuggerStatement(StatementListItem);
|
||||||
|
|
||||||
|
case isHoistableDeclaration(StatementListItem):
|
||||||
|
return Evaluate_HoistableDeclaration(StatementListItem);
|
||||||
|
|
||||||
|
case isClassDeclaration(StatementListItem):
|
||||||
|
return yield* Evaluate_ClassDeclaration(StatementListItem);
|
||||||
|
|
||||||
|
case isLexicalDeclaration(StatementListItem):
|
||||||
|
return yield* Evaluate_LexicalDeclaration(StatementListItem);
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new OutOfRange('Evaluate_StatementListItem', StatementListItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Evaluate_Statement = Evaluate_StatementListItem;
|
||||||
|
|
||||||
|
// 13.5.1 #sec-expression-statement-runtime-semantics-evaluation
|
||||||
|
// ExpressionStatement : Expression `;`
|
||||||
|
function* Evaluate_ExpressionStatement(ExpressionStatement) {
|
||||||
|
const exprRef = yield* Evaluate(ExpressionStatement.expression);
|
||||||
|
return GetValue(exprRef);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function EvaluateBinopValues(operator, lval, rval) {
|
||||||
|
switch (operator) {
|
||||||
|
case '*':
|
||||||
|
case '/':
|
||||||
|
case '%':
|
||||||
|
return EvaluateBinopValues_MultiplicativeExpression(operator, lval, rval);
|
||||||
|
|
||||||
|
case '+':
|
||||||
|
return EvaluateBinopValues_AdditiveExpression_Plus(lval, rval);
|
||||||
|
|
||||||
|
case '-':
|
||||||
|
return EvaluateBinopValues_AdditiveExpression_Minus(lval, rval);
|
||||||
|
|
||||||
|
case '<<':
|
||||||
|
case '>>':
|
||||||
|
case '>>>':
|
||||||
|
return EvaluateBinopValues_ShiftExpression(operator, lval, rval);
|
||||||
|
|
||||||
|
case '&':
|
||||||
|
return EvaluateBinopValues_BitwiseANDExpression(lval, rval);
|
||||||
|
case '^':
|
||||||
|
return EvaluateBinopValues_BitwiseXORExpression(lval, rval);
|
||||||
|
case '|':
|
||||||
|
return EvaluateBinopValues_BitwiseORExpression(lval, rval);
|
||||||
|
|
||||||
|
case '**':
|
||||||
|
return EvaluateBinopValues_ExponentiationExpression(lval, rval);
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new OutOfRange('EvaluateBinopValues', operator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function* Evaluate_Expression(Expression) {
|
||||||
|
return EnsureCompletion(yield* Inner_Evaluate_Expression(Expression));
|
||||||
|
}
|
||||||
|
|
||||||
|
// (implicit)
|
||||||
|
function* Inner_Evaluate_Expression(Expression) {
|
||||||
|
switch (true) {
|
||||||
|
case isThis(Expression):
|
||||||
|
return Evaluate_ThisExpression(Expression);
|
||||||
|
|
||||||
|
case isIdentifierReference(Expression):
|
||||||
|
return Evaluate_Identifier(Expression);
|
||||||
|
|
||||||
|
case isLiteral(Expression):
|
||||||
|
return Evaluate_Literal(Expression);
|
||||||
|
|
||||||
|
case isArrayLiteral(Expression):
|
||||||
|
return yield* Evaluate_ArrayLiteral(Expression);
|
||||||
|
|
||||||
|
case isObjectLiteral(Expression):
|
||||||
|
return yield* Evaluate_ObjectLiteral(Expression);
|
||||||
|
|
||||||
|
case isFunctionExpression(Expression):
|
||||||
|
return Evaluate_FunctionExpression(Expression);
|
||||||
|
|
||||||
|
case isClassExpression(Expression):
|
||||||
|
return yield* Evaluate_ClassExpression(Expression);
|
||||||
|
|
||||||
|
case isGeneratorExpression(Expression):
|
||||||
|
return Evaluate_GeneratorExpression(Expression);
|
||||||
|
|
||||||
|
case isAsyncFunctionExpression(Expression):
|
||||||
|
return Evaluate_AsyncFunctionExpression(Expression);
|
||||||
|
|
||||||
|
case isAsyncGeneratorExpression(Expression):
|
||||||
|
return Evaluate_AsyncGeneratorExpression(Expression);
|
||||||
|
|
||||||
|
case isRegularExpressionLiteral(Expression):
|
||||||
|
return Evaluate_RegularExpressionLiteral(Expression);
|
||||||
|
|
||||||
|
case isTemplateLiteral(Expression):
|
||||||
|
return yield* Evaluate_TemplateLiteral(Expression);
|
||||||
|
|
||||||
|
case isActualMemberExpression(Expression):
|
||||||
|
return yield* Evaluate_MemberExpression(Expression);
|
||||||
|
|
||||||
|
case isOptionalExpression(Expression):
|
||||||
|
return yield* Evaluate_OptionalExpression(Expression);
|
||||||
|
|
||||||
|
case isSuperProperty(Expression):
|
||||||
|
return yield* Evaluate_SuperProperty(Expression);
|
||||||
|
|
||||||
|
case isSuperCall(Expression):
|
||||||
|
return yield* Evaluate_SuperCall(Expression);
|
||||||
|
|
||||||
|
case isImportCall(Expression):
|
||||||
|
return yield* Evaluate_ImportCall(Expression);
|
||||||
|
|
||||||
|
case isTaggedTemplate(Expression):
|
||||||
|
return yield* Evaluate_TaggedTemplate(Expression);
|
||||||
|
|
||||||
|
case isMetaProperty(Expression):
|
||||||
|
return yield* Evaluate_MetaProperty(Expression);
|
||||||
|
|
||||||
|
case isActualNewExpression(Expression):
|
||||||
|
return yield* Evaluate_NewExpression(Expression);
|
||||||
|
|
||||||
|
case isActualCallExpression(Expression):
|
||||||
|
return yield* Evaluate_CallExpression(Expression);
|
||||||
|
|
||||||
|
case isActualUpdateExpression(Expression):
|
||||||
|
return yield* Evaluate_UpdateExpression(Expression);
|
||||||
|
|
||||||
|
case isActualUnaryExpression(Expression):
|
||||||
|
return yield* Evaluate_UnaryExpression(Expression);
|
||||||
|
|
||||||
|
case isAwaitExpression(Expression):
|
||||||
|
return yield* Evaluate_AwaitExpression(Expression);
|
||||||
|
|
||||||
|
case isActualExponentiationExpression(Expression):
|
||||||
|
return yield* Evaluate_ExponentiationExpression(Expression);
|
||||||
|
|
||||||
|
case isActualMultiplicativeExpression(Expression):
|
||||||
|
return yield* Evaluate_MultiplicativeExpression(Expression);
|
||||||
|
|
||||||
|
case isActualAdditiveExpression(Expression):
|
||||||
|
return yield* Evaluate_AdditiveExpression(Expression);
|
||||||
|
|
||||||
|
case isActualShiftExpression(Expression):
|
||||||
|
return yield* Evaluate_ShiftExpression(Expression);
|
||||||
|
|
||||||
|
case isActualRelationalExpression(Expression):
|
||||||
|
return yield* Evaluate_RelationalExpression(Expression);
|
||||||
|
|
||||||
|
case isActualEqualityExpression(Expression):
|
||||||
|
return yield* Evaluate_EqualityExpression(Expression);
|
||||||
|
|
||||||
|
case isActualBitwiseANDExpression(Expression):
|
||||||
|
case isActualBitwiseXORExpression(Expression):
|
||||||
|
case isActualBitwiseORExpression(Expression):
|
||||||
|
return yield* Evaluate_BinaryBitwiseExpression(Expression);
|
||||||
|
|
||||||
|
case isActualLogicalANDExpression(Expression):
|
||||||
|
return yield* Evaluate_LogicalANDExpression(Expression);
|
||||||
|
|
||||||
|
case isActualLogicalORExpression(Expression):
|
||||||
|
return yield* Evaluate_LogicalORExpression(Expression);
|
||||||
|
|
||||||
|
case isActualCoalesceExpression(Expression):
|
||||||
|
return yield* Evaluate_CoalesceExpression(Expression);
|
||||||
|
|
||||||
|
case isActualConditionalExpression(Expression):
|
||||||
|
return yield* Evaluate_ConditionalExpression(Expression);
|
||||||
|
|
||||||
|
case isYieldExpression(Expression):
|
||||||
|
return yield* Evaluate_YieldExpression(Expression);
|
||||||
|
|
||||||
|
case isArrowFunction(Expression):
|
||||||
|
return Evaluate_ArrowFunction(Expression);
|
||||||
|
|
||||||
|
case isAsyncArrowFunction(Expression):
|
||||||
|
return Evaluate_AsyncArrowFunction(Expression);
|
||||||
|
|
||||||
|
case isActualAssignmentExpression(Expression):
|
||||||
|
return yield* Evaluate_AssignmentExpression(Expression);
|
||||||
|
|
||||||
|
case isExpressionWithComma(Expression):
|
||||||
|
return yield* Evaluate_ExpressionWithComma(Expression);
|
||||||
|
|
||||||
|
// 12.2.10.5 #sec-grouping-operator-runtime-semantics-evaluation
|
||||||
|
case isParenthesizedExpression(Expression):
|
||||||
|
return yield* Evaluate(Expression.expression);
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new OutOfRange('Evaluate_Expression', Expression);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 15.1.7 #sec-script-semantics-runtime-semantics-evaluation
|
||||||
|
// Script : [empty]
|
||||||
|
//
|
||||||
|
// (implicit)
|
||||||
|
// Script : ScriptBody
|
||||||
|
// ScriptBody : StatementList
|
||||||
|
export function Evaluate_Script(Script) {
|
||||||
|
if (Script.length === 0) {
|
||||||
|
return new NormalCompletion(Value.undefined);
|
||||||
|
}
|
||||||
|
return unwind(Evaluate_StatementList(Script));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 15.2.1.23 #sec-module-semantics-runtime-semantics-evaluation
|
||||||
|
// ModuleBody : ModuleItemList
|
||||||
|
export function* Evaluate_ModuleBody(ModuleBody) {
|
||||||
|
const ModuleItemList = ModuleBody;
|
||||||
|
const result = EnsureCompletion(yield* Evaluate_ModuleItemList(ModuleItemList));
|
||||||
|
if (result.Type === 'normal' && result.Value === undefined) {
|
||||||
|
return new NormalCompletion(Value.undefined);
|
||||||
|
}
|
||||||
|
return Completion(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 15.2.1.23 #sec-module-semantics-runtime-semantics-evaluation
|
||||||
|
// Module : [empty]
|
||||||
|
//
|
||||||
|
// (implicit)
|
||||||
|
// Module : ModuleBody
|
||||||
|
export function Evaluate_Module(Module) {
|
||||||
|
if (Module.length === 0) {
|
||||||
|
return new NormalCompletion(Value.undefined);
|
||||||
|
}
|
||||||
|
return unwind(Evaluate_ModuleBody(Module));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function* Evaluate(Production) {
|
||||||
|
surroundingAgent.runningExecutionContext.callSite.setLocation(Production);
|
||||||
|
|
||||||
|
if (surroundingAgent.hostDefinedOptions.onNodeEvaluation) {
|
||||||
|
surroundingAgent.hostDefinedOptions.onNodeEvaluation(Production, surroundingAgent.currentRealmRecord);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (true) {
|
||||||
|
case isImportDeclaration(Production):
|
||||||
|
return new NormalCompletion(undefined);
|
||||||
|
case isExportDeclaration(Production):
|
||||||
|
return yield* Evaluate_ExportDeclaration(Production);
|
||||||
|
case isStatement(Production):
|
||||||
|
case isDeclaration(Production):
|
||||||
|
return yield* Evaluate_Statement(Production);
|
||||||
|
case isExpression(Production):
|
||||||
|
return yield* Evaluate_Expression(Production);
|
||||||
|
default:
|
||||||
|
throw new OutOfRange('Evaluate', Production);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,212 @@
|
|||||||
|
/* eslint-disable no-bitwise */
|
||||||
|
|
||||||
|
// Divide a non-negative `num` by a positive `den`. The quotient is rounded to
|
||||||
|
// its nearest integer, or the even integer if there are two equally near
|
||||||
|
// integer.
|
||||||
|
function roundQuotientBigInt(num, den) {
|
||||||
|
const quo = num / den;
|
||||||
|
const rem = num % den;
|
||||||
|
const rem2 = rem * 2n;
|
||||||
|
if (rem2 > den || (rem2 === den && quo % 2n !== 0n)) {
|
||||||
|
return quo + 1n;
|
||||||
|
} else {
|
||||||
|
return quo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const throwawayArray = new Float64Array(1);
|
||||||
|
const throwawayArrayInt = new Uint32Array(throwawayArray.buffer);
|
||||||
|
|
||||||
|
// Find out if the host's [[BigEndian]] is true or false, by checking the
|
||||||
|
// representation for -0.
|
||||||
|
throwawayArray[0] = -0;
|
||||||
|
const float64High = throwawayArrayInt[0] === 0 ? 1 : 0;
|
||||||
|
|
||||||
|
// Return x * 2 ** exp where x is a Number, and exp is an integer.
|
||||||
|
//
|
||||||
|
// Derived from
|
||||||
|
// https://github.com/JuliaMath/openlibm/blob/0f22aeb0a9104c52106f42ce1fa8ebe96fb498f1/src/s_scalbn.c.
|
||||||
|
//
|
||||||
|
// License:
|
||||||
|
//
|
||||||
|
// Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
|
||||||
|
// Developed at SunPro, a Sun Microsystems, Inc. business.
|
||||||
|
// Permission to use, copy, modify, and distribute this
|
||||||
|
// software is freely granted, provided that this notice
|
||||||
|
// is preserved.
|
||||||
|
function scalb(x, exp) {
|
||||||
|
if (x === 0 || exp === 0 || !Number.isFinite(x)) {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
if (exp >= 2000) {
|
||||||
|
return x * Infinity;
|
||||||
|
} else if (exp <= -2000) {
|
||||||
|
return x * 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
throwawayArray[0] = x;
|
||||||
|
let origExp = (throwawayArrayInt[float64High] >>> 20) & 0x7ff;
|
||||||
|
if (origExp === 0) {
|
||||||
|
// x is denormalized. Multiply x by 2**54 (1 + number of mantissa bits),
|
||||||
|
// and correspondingly reduce exp by 54.
|
||||||
|
throwawayArray[0] *= 18014398509481984;
|
||||||
|
exp -= 54;
|
||||||
|
if (exp === 0) {
|
||||||
|
return throwawayArray[0];
|
||||||
|
}
|
||||||
|
origExp = (throwawayArrayInt[float64High] >>> 20) & 0x7ff;
|
||||||
|
}
|
||||||
|
const newExp = origExp + exp;
|
||||||
|
if (newExp > 0x7fe) {
|
||||||
|
// Overflow. Return Infinity, but of the appropriate sign.
|
||||||
|
return throwawayArray[0] * Infinity;
|
||||||
|
}
|
||||||
|
if (newExp > 0) {
|
||||||
|
// Normalized, okay.
|
||||||
|
throwawayArrayInt[float64High] ^= (origExp ^ newExp) << 20;
|
||||||
|
return throwawayArray[0];
|
||||||
|
}
|
||||||
|
if (newExp <= -54) {
|
||||||
|
// Underflow. Return 0, but of the appropriate sign.
|
||||||
|
return throwawayArray[0] * 0;
|
||||||
|
}
|
||||||
|
// Denormalized result. Add 54 to newExp and multiply the resultant number by
|
||||||
|
// 2**-54.
|
||||||
|
throwawayArrayInt[float64High] ^= (origExp ^ (newExp + 54)) << 20;
|
||||||
|
return throwawayArray[0] * 5.55111512312578270212e-17;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the minimum number of bits it takes to store `bint`. This function
|
||||||
|
// assumes a host implementation on which a BigInt value cannot exceed 2^(4n),
|
||||||
|
// where n is the maximum length of a String value on that host (usually around
|
||||||
|
// 2^31, but can be up to 2^53 - 1 by spec).
|
||||||
|
function bitLengthBigInt(bint) {
|
||||||
|
if (bint < 0n) {
|
||||||
|
bint = -bint;
|
||||||
|
}
|
||||||
|
let increment = 0;
|
||||||
|
if (bint > ((1n << 32n) - 1n)) {
|
||||||
|
// This number is larger than 2^32 - 1, which is just huge. Let's form an
|
||||||
|
// estimate of how many bits it requires first, accurate to the nearest
|
||||||
|
// multiple of log2(16) = 4, by converting it to a hexadecimal string and
|
||||||
|
// measuring the resulting length.
|
||||||
|
const hexLength = bint.toString(16).length;
|
||||||
|
const estimatedBitLength = (hexLength - 1) * 4;
|
||||||
|
increment += estimatedBitLength;
|
||||||
|
bint >>= BigInt(estimatedBitLength);
|
||||||
|
}
|
||||||
|
// As we are sure that bint is within the range of an unsigned 32-bit
|
||||||
|
// integer, we can use Math.clz32().
|
||||||
|
return 32 - Math.clz32(Number(bint)) + increment;
|
||||||
|
}
|
||||||
|
|
||||||
|
function approximateLog10BigInt(bint) {
|
||||||
|
return bint.toString(10).length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Number of mantissa bits in a IEEE 754-2008 binary64 value.
|
||||||
|
const MANTISSA_BITS = 53;
|
||||||
|
|
||||||
|
// A class representing a decimal number in scientific notation, or otherwise
|
||||||
|
// known as a decimal floating-point number.
|
||||||
|
export default class Scientific {
|
||||||
|
constructor(num, exp = 0n) {
|
||||||
|
if (typeof num !== 'bigint') { // eslint-disable-line valid-typeof
|
||||||
|
throw new TypeError('Numerator must be a BigInt');
|
||||||
|
}
|
||||||
|
if (typeof exp !== 'bigint') { // eslint-disable-line valid-typeof
|
||||||
|
throw new TypeError('Numerator must be a BigInt');
|
||||||
|
}
|
||||||
|
this.num = num;
|
||||||
|
this.exp = exp;
|
||||||
|
}
|
||||||
|
|
||||||
|
negate() {
|
||||||
|
return new this.constructor(-this.num, this.exp);
|
||||||
|
}
|
||||||
|
|
||||||
|
convExp(exp) {
|
||||||
|
if (this.exp === exp) {
|
||||||
|
return this;
|
||||||
|
} else if (this.exp > exp) {
|
||||||
|
return new this.constructor(this.num * (10n ** (this.exp - exp)), exp);
|
||||||
|
}
|
||||||
|
throw new RangeError('Requested exponent must be less than or equal to the current exponent');
|
||||||
|
}
|
||||||
|
|
||||||
|
expAdd(e) {
|
||||||
|
return new this.constructor(this.num, this.exp + e);
|
||||||
|
}
|
||||||
|
|
||||||
|
addSci(sci) {
|
||||||
|
const expectedExp = this.exp < sci.exp ? this.exp : sci.exp;
|
||||||
|
const conv1 = this.convExp(expectedExp);
|
||||||
|
const conv2 = sci.convExp(expectedExp);
|
||||||
|
return new this.constructor(conv1.num + conv2.num, expectedExp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Derived from "Easy Accurate Reading and Writing of Floating-Point Numbers"
|
||||||
|
// by Aubrey Jaffer, <https://arxiv.org/abs/1310.8121v7>.
|
||||||
|
toNumber() {
|
||||||
|
if (this.num === 0n) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.num < 0) {
|
||||||
|
return -new this.constructor(-this.num, this.exp).toNumber();
|
||||||
|
}
|
||||||
|
|
||||||
|
let { num, exp } = this;
|
||||||
|
|
||||||
|
// According to V8, the "Maximum number of significant digits in decimal
|
||||||
|
// representation" for a binary64 value is 772. See [1]. Let's first make
|
||||||
|
// sure we have a reasonably small this.num (≤ 10**800) while not losing
|
||||||
|
// accuracy, so that we can fast-path numbers with astronomical exponents.
|
||||||
|
//
|
||||||
|
// [1]: https://cs.chromium.org/chromium/src/v8/src/conversions.cc?l=565-571&rcl=dadf4cbe89c1e9ee9fed6181216cb4d3ba647a68
|
||||||
|
const approximateDecimalDigits = approximateLog10BigInt(this.num);
|
||||||
|
if (approximateDecimalDigits > 800) {
|
||||||
|
const comp = BigInt(approximateDecimalDigits - 800);
|
||||||
|
// We don't care about rounding as we still have quite a large margin of
|
||||||
|
// error.
|
||||||
|
num /= 10n ** comp;
|
||||||
|
exp += comp;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exp > 310n) {
|
||||||
|
// Largest possible value is < 2e308.
|
||||||
|
return Infinity;
|
||||||
|
} else if (exp < -1150n) {
|
||||||
|
// Smallest possible value is 5e-324, but num may be at most 1e801, so we
|
||||||
|
// are slightly more careful and only fast-path truly miniscule
|
||||||
|
// exponents.
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const expNum = Number(exp);
|
||||||
|
if (expNum >= 0) {
|
||||||
|
const numScaled = num * (5n ** exp);
|
||||||
|
const bex = bitLengthBigInt(numScaled) - MANTISSA_BITS;
|
||||||
|
if (bex <= 0) {
|
||||||
|
return scalb(Number(numScaled), expNum);
|
||||||
|
}
|
||||||
|
const quo = roundQuotientBigInt(numScaled, 1n << BigInt(bex));
|
||||||
|
return scalb(Number(quo), bex + expNum);
|
||||||
|
}
|
||||||
|
const scl = 5n ** -exp;
|
||||||
|
let mantlen = MANTISSA_BITS;
|
||||||
|
let bex = bitLengthBigInt(num) - bitLengthBigInt(scl) - mantlen;
|
||||||
|
const tmp = bex + expNum + 1021 + mantlen;
|
||||||
|
if (tmp < 0) {
|
||||||
|
bex -= tmp + 1;
|
||||||
|
mantlen += tmp;
|
||||||
|
}
|
||||||
|
const numScaled = num << BigInt(-bex);
|
||||||
|
let quo = roundQuotientBigInt(numScaled, scl);
|
||||||
|
if (bitLengthBigInt(quo) > mantlen) {
|
||||||
|
bex += 1;
|
||||||
|
quo = roundQuotientBigInt(numScaled, scl << 1n);
|
||||||
|
}
|
||||||
|
return scalb(Number(quo), bex + expNum);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
// Generated automatically by nearley, version 2.19.0
|
||||||
|
// http://github.com/Hardmath123/nearley
|
||||||
|
function id(x) { return x[0]; }
|
||||||
|
|
||||||
|
import Scientific from './Scientific.mjs';
|
||||||
|
function c(val) {
|
||||||
|
return () => val;
|
||||||
|
}
|
||||||
|
let Lexer = undefined;
|
||||||
|
let ParserRules = [
|
||||||
|
{"name": "StrNumericLiteral", "symbols": ["StrDecimalLiteral"], "postprocess": ([StrDecimalLiteral]) => StrDecimalLiteral},
|
||||||
|
{"name": "StrNumericLiteral", "symbols": ["BinaryIntegerLiteral"], "postprocess": ([BinaryIntegerLiteral]) => BinaryIntegerLiteral},
|
||||||
|
{"name": "StrNumericLiteral", "symbols": ["OctalIntegerLiteral"], "postprocess": ([OctalIntegerLiteral]) => OctalIntegerLiteral},
|
||||||
|
{"name": "StrNumericLiteral", "symbols": ["HexIntegerLiteral"], "postprocess": ([HexIntegerLiteral]) => HexIntegerLiteral},
|
||||||
|
{"name": "StrDecimalLiteral", "symbols": ["StrUnsignedDecimalLiteral"], "postprocess": ([StrUnsignedDecimalLiteral]) => StrUnsignedDecimalLiteral},
|
||||||
|
{"name": "StrDecimalLiteral", "symbols": [{"literal":"+"}, "StrUnsignedDecimalLiteral"], "postprocess": ([_, StrUnsignedDecimalLiteral]) => StrUnsignedDecimalLiteral},
|
||||||
|
{"name": "StrDecimalLiteral", "symbols": [{"literal":"-"}, "StrUnsignedDecimalLiteral"], "postprocess": ([_, StrUnsignedDecimalLiteral]) => StrUnsignedDecimalLiteral.negate()},
|
||||||
|
{"name": "StrUnsignedDecimalLiteral$string$1", "symbols": [{"literal":"I"}, {"literal":"n"}, {"literal":"f"}, {"literal":"i"}, {"literal":"n"}, {"literal":"i"}, {"literal":"t"}, {"literal":"y"}], "postprocess": function joiner(d) {return d.join('');}},
|
||||||
|
{"name": "StrUnsignedDecimalLiteral", "symbols": ["StrUnsignedDecimalLiteral$string$1"], "postprocess": () => new Scientific(1n, 10000n)},
|
||||||
|
{"name": "StrUnsignedDecimalLiteral", "symbols": ["DecimalDigits", {"literal":"."}], "postprocess": ([[DecimalDigits]]) => new Scientific(DecimalDigits)},
|
||||||
|
{"name": "StrUnsignedDecimalLiteral", "symbols": ["DecimalDigits", {"literal":"."}, "DecimalDigits"], "postprocess": ([[first], _, [second, n]]) => new Scientific(first).addSci(new Scientific(second, -n))},
|
||||||
|
{"name": "StrUnsignedDecimalLiteral", "symbols": ["DecimalDigits", {"literal":"."}, "ExponentPart"], "postprocess": ([[DecimalDigits], _, e]) => new Scientific(DecimalDigits, e)},
|
||||||
|
{"name": "StrUnsignedDecimalLiteral", "symbols": ["DecimalDigits", {"literal":"."}, "DecimalDigits", "ExponentPart"], "postprocess": ([[first], _, [second, n], e]) => (new Scientific(first).addSci(new Scientific(second, -n))).expAdd(e)},
|
||||||
|
{"name": "StrUnsignedDecimalLiteral", "symbols": [{"literal":"."}, "DecimalDigits"], "postprocess": ([_, [DecimalDigits, n]]) => new Scientific(DecimalDigits, -n)},
|
||||||
|
{"name": "StrUnsignedDecimalLiteral", "symbols": [{"literal":"."}, "DecimalDigits", "ExponentPart"], "postprocess": ([_, [DecimalDigits, n], e]) => new Scientific(DecimalDigits, e - n)},
|
||||||
|
{"name": "StrUnsignedDecimalLiteral", "symbols": ["DecimalDigits"], "postprocess": ([[DecimalDigits]]) => new Scientific(DecimalDigits)},
|
||||||
|
{"name": "StrUnsignedDecimalLiteral", "symbols": ["DecimalDigits", "ExponentPart"], "postprocess": ([[DecimalDigits], e]) => new Scientific(DecimalDigits, e)},
|
||||||
|
{"name": "NumericLiteral", "symbols": ["DecimalLiteral"], "postprocess": ([DecimalLiteral]) => DecimalLiteral},
|
||||||
|
{"name": "NumericLiteral", "symbols": ["BinaryIntegerLiteral"], "postprocess": ([BinaryIntegerLiteral]) => BinaryIntegerLiteral},
|
||||||
|
{"name": "NumericLiteral", "symbols": ["OctalIntegerLiteral"], "postprocess": ([OctalIntegerLiteral]) => OctalIntegerLiteral},
|
||||||
|
{"name": "NumericLiteral", "symbols": ["HexIntegerLiteral"], "postprocess": ([HexIntegerLiteral]) => HexIntegerLiteral},
|
||||||
|
{"name": "DecimalLiteral", "symbols": ["DecimalIntegerLiteral", {"literal":"."}], "postprocess": ([DecimalIntegerLiteral]) => new Scientific(DecimalIntegerLiteral)},
|
||||||
|
{"name": "DecimalLiteral", "symbols": ["DecimalIntegerLiteral", {"literal":"."}, "DecimalDigits"], "postprocess": ([DecimalIntegerLiteral, _, [DecimalDigits, n]]) => new Scientific(DecimalIntegerLiteral).addSci(new Scientific(DecimalDigits, -n))},
|
||||||
|
{"name": "DecimalLiteral", "symbols": ["DecimalIntegerLiteral", {"literal":"."}, "ExponentPart"], "postprocess": ([DecimalIntegerLiteral, _, e]) => new Scientific(DecimalIntegerLiteral, e)},
|
||||||
|
{"name": "DecimalLiteral", "symbols": ["DecimalIntegerLiteral", {"literal":"."}, "DecimalDigits", "ExponentPart"], "postprocess": ([DecimalIntegerLiteral, _, [DecimalDigits, n], e]) => new Scientific(DecimalIntegerLiteral).addSci(new Scientific(DecimalDigits, -n)).expAdd(e)},
|
||||||
|
{"name": "DecimalLiteral", "symbols": [{"literal":"."}, "DecimalDigits"], "postprocess": ([_, [DecimalDigits, n]]) => new Scientific(DecimalDigits, -n)},
|
||||||
|
{"name": "DecimalLiteral", "symbols": [{"literal":"."}, "DecimalDigits", "ExponentPart"], "postprocess": ([_, [DecimalDigits, n], e]) => new Scientific(DecimalDigits, e - n)},
|
||||||
|
{"name": "DecimalLiteral", "symbols": ["DecimalIntegerLiteral"], "postprocess": ([DecimalIntegerLiteral]) => new Scientific(DecimalIntegerLiteral)},
|
||||||
|
{"name": "DecimalLiteral", "symbols": ["DecimalIntegerLiteral", "ExponentPart"], "postprocess": ([DecimalIntegerLiteral, e]) => new Scientific(DecimalIntegerLiteral, e)},
|
||||||
|
{"name": "DecimalIntegerLiteral", "symbols": [{"literal":"0"}], "postprocess": c(0n)},
|
||||||
|
{"name": "DecimalIntegerLiteral", "symbols": ["NonZeroDigit"], "postprocess": ([NonZeroDigit]) => NonZeroDigit},
|
||||||
|
{"name": "DecimalIntegerLiteral", "symbols": ["NonZeroDigit", "DecimalDigits"], "postprocess": ([NonZeroDigit, [DecimalDigits, n]]) => NonZeroDigit * (10n ** n) + DecimalDigits},
|
||||||
|
{"name": "DecimalDigits", "symbols": ["DecimalDigit"], "postprocess": ([DecimalDigit]) => [DecimalDigit, 1n]},
|
||||||
|
{"name": "DecimalDigits", "symbols": ["DecimalDigits", "DecimalDigit"], "postprocess": ([[DecimalDigits, n], DecimalDigit]) => [DecimalDigits * 10n + DecimalDigit, n + 1n]},
|
||||||
|
{"name": "DecimalDigit", "symbols": [{"literal":"0"}], "postprocess": c(0n)},
|
||||||
|
{"name": "DecimalDigit", "symbols": [{"literal":"1"}], "postprocess": c(1n)},
|
||||||
|
{"name": "DecimalDigit", "symbols": [{"literal":"2"}], "postprocess": c(2n)},
|
||||||
|
{"name": "DecimalDigit", "symbols": [{"literal":"3"}], "postprocess": c(3n)},
|
||||||
|
{"name": "DecimalDigit", "symbols": [{"literal":"4"}], "postprocess": c(4n)},
|
||||||
|
{"name": "DecimalDigit", "symbols": [{"literal":"5"}], "postprocess": c(5n)},
|
||||||
|
{"name": "DecimalDigit", "symbols": [{"literal":"6"}], "postprocess": c(6n)},
|
||||||
|
{"name": "DecimalDigit", "symbols": [{"literal":"7"}], "postprocess": c(7n)},
|
||||||
|
{"name": "DecimalDigit", "symbols": [{"literal":"8"}], "postprocess": c(8n)},
|
||||||
|
{"name": "DecimalDigit", "symbols": [{"literal":"9"}], "postprocess": c(9n)},
|
||||||
|
{"name": "NonZeroDigit", "symbols": [{"literal":"1"}], "postprocess": c(1n)},
|
||||||
|
{"name": "NonZeroDigit", "symbols": [{"literal":"2"}], "postprocess": c(2n)},
|
||||||
|
{"name": "NonZeroDigit", "symbols": [{"literal":"3"}], "postprocess": c(3n)},
|
||||||
|
{"name": "NonZeroDigit", "symbols": [{"literal":"4"}], "postprocess": c(4n)},
|
||||||
|
{"name": "NonZeroDigit", "symbols": [{"literal":"5"}], "postprocess": c(5n)},
|
||||||
|
{"name": "NonZeroDigit", "symbols": [{"literal":"6"}], "postprocess": c(6n)},
|
||||||
|
{"name": "NonZeroDigit", "symbols": [{"literal":"7"}], "postprocess": c(7n)},
|
||||||
|
{"name": "NonZeroDigit", "symbols": [{"literal":"8"}], "postprocess": c(8n)},
|
||||||
|
{"name": "NonZeroDigit", "symbols": [{"literal":"9"}], "postprocess": c(9n)},
|
||||||
|
{"name": "ExponentPart", "symbols": ["ExponentIndicator", "SignedInteger"], "postprocess": ([_, SignedInteger]) => SignedInteger},
|
||||||
|
{"name": "ExponentIndicator$subexpression$1", "symbols": [/[eE]/], "postprocess": function(d) {return d.join(""); }},
|
||||||
|
{"name": "ExponentIndicator", "symbols": ["ExponentIndicator$subexpression$1"]},
|
||||||
|
{"name": "SignedInteger", "symbols": ["DecimalDigits"], "postprocess": ([[DecimalDigits]]) => DecimalDigits},
|
||||||
|
{"name": "SignedInteger", "symbols": [{"literal":"+"}, "DecimalDigits"], "postprocess": ([_, [DecimalDigits]]) => DecimalDigits},
|
||||||
|
{"name": "SignedInteger", "symbols": [{"literal":"-"}, "DecimalDigits"], "postprocess": ([_, [DecimalDigits]]) => -DecimalDigits},
|
||||||
|
{"name": "BinaryIntegerLiteral$subexpression$1", "symbols": [{"literal":"0"}, /[bB]/], "postprocess": function(d) {return d.join(""); }},
|
||||||
|
{"name": "BinaryIntegerLiteral", "symbols": ["BinaryIntegerLiteral$subexpression$1", "BinaryDigits"], "postprocess": ([_, BinaryDigits]) => new Scientific(BinaryDigits)},
|
||||||
|
{"name": "BinaryDigits", "symbols": ["BinaryDigit"], "postprocess": ([BinaryDigit]) => BinaryDigit},
|
||||||
|
{"name": "BinaryDigits", "symbols": ["BinaryDigits", "BinaryDigit"], "postprocess": ([BinaryDigits, BinaryDigit]) => BinaryDigits * 2n + BinaryDigit},
|
||||||
|
{"name": "BinaryDigit", "symbols": [{"literal":"0"}], "postprocess": c(0n)},
|
||||||
|
{"name": "BinaryDigit", "symbols": [{"literal":"1"}], "postprocess": c(1n)},
|
||||||
|
{"name": "OctalIntegerLiteral$subexpression$1", "symbols": [{"literal":"0"}, /[oO]/], "postprocess": function(d) {return d.join(""); }},
|
||||||
|
{"name": "OctalIntegerLiteral", "symbols": ["OctalIntegerLiteral$subexpression$1", "OctalDigits"], "postprocess": ([_, OctalDigits]) => new Scientific(OctalDigits)},
|
||||||
|
{"name": "OctalDigits", "symbols": ["OctalDigit"], "postprocess": ([OctalDigit]) => OctalDigit},
|
||||||
|
{"name": "OctalDigits", "symbols": ["OctalDigits", "OctalDigit"], "postprocess": ([OctalDigits, OctalDigit]) => OctalDigits * 8n + OctalDigit},
|
||||||
|
{"name": "OctalDigit", "symbols": [{"literal":"0"}], "postprocess": c(0n)},
|
||||||
|
{"name": "OctalDigit", "symbols": [{"literal":"1"}], "postprocess": c(1n)},
|
||||||
|
{"name": "OctalDigit", "symbols": [{"literal":"2"}], "postprocess": c(2n)},
|
||||||
|
{"name": "OctalDigit", "symbols": [{"literal":"3"}], "postprocess": c(3n)},
|
||||||
|
{"name": "OctalDigit", "symbols": [{"literal":"4"}], "postprocess": c(4n)},
|
||||||
|
{"name": "OctalDigit", "symbols": [{"literal":"5"}], "postprocess": c(5n)},
|
||||||
|
{"name": "OctalDigit", "symbols": [{"literal":"6"}], "postprocess": c(6n)},
|
||||||
|
{"name": "OctalDigit", "symbols": [{"literal":"7"}], "postprocess": c(7n)},
|
||||||
|
{"name": "HexIntegerLiteral$subexpression$1", "symbols": [{"literal":"0"}, /[xX]/], "postprocess": function(d) {return d.join(""); }},
|
||||||
|
{"name": "HexIntegerLiteral", "symbols": ["HexIntegerLiteral$subexpression$1", "HexDigits"], "postprocess": ([_, HexDigits]) => new Scientific(HexDigits)},
|
||||||
|
{"name": "HexDigits", "symbols": ["HexDigit"], "postprocess": ([HexDigit]) => HexDigit},
|
||||||
|
{"name": "HexDigits", "symbols": ["HexDigits", "HexDigit"], "postprocess": ([HexDigits, HexDigit]) => HexDigits * 16n + HexDigit},
|
||||||
|
{"name": "HexDigit", "symbols": [{"literal":"0"}], "postprocess": c(0n)},
|
||||||
|
{"name": "HexDigit", "symbols": [{"literal":"1"}], "postprocess": c(1n)},
|
||||||
|
{"name": "HexDigit", "symbols": [{"literal":"2"}], "postprocess": c(2n)},
|
||||||
|
{"name": "HexDigit", "symbols": [{"literal":"3"}], "postprocess": c(3n)},
|
||||||
|
{"name": "HexDigit", "symbols": [{"literal":"4"}], "postprocess": c(4n)},
|
||||||
|
{"name": "HexDigit", "symbols": [{"literal":"5"}], "postprocess": c(5n)},
|
||||||
|
{"name": "HexDigit", "symbols": [{"literal":"6"}], "postprocess": c(6n)},
|
||||||
|
{"name": "HexDigit", "symbols": [{"literal":"7"}], "postprocess": c(7n)},
|
||||||
|
{"name": "HexDigit", "symbols": [{"literal":"8"}], "postprocess": c(8n)},
|
||||||
|
{"name": "HexDigit", "symbols": [{"literal":"9"}], "postprocess": c(9n)},
|
||||||
|
{"name": "HexDigit$subexpression$1", "symbols": [/[aA]/], "postprocess": function(d) {return d.join(""); }},
|
||||||
|
{"name": "HexDigit", "symbols": ["HexDigit$subexpression$1"], "postprocess": c(10n)},
|
||||||
|
{"name": "HexDigit$subexpression$2", "symbols": [/[bB]/], "postprocess": function(d) {return d.join(""); }},
|
||||||
|
{"name": "HexDigit", "symbols": ["HexDigit$subexpression$2"], "postprocess": c(11n)},
|
||||||
|
{"name": "HexDigit$subexpression$3", "symbols": [/[cC]/], "postprocess": function(d) {return d.join(""); }},
|
||||||
|
{"name": "HexDigit", "symbols": ["HexDigit$subexpression$3"], "postprocess": c(12n)},
|
||||||
|
{"name": "HexDigit$subexpression$4", "symbols": [/[dD]/], "postprocess": function(d) {return d.join(""); }},
|
||||||
|
{"name": "HexDigit", "symbols": ["HexDigit$subexpression$4"], "postprocess": c(13n)},
|
||||||
|
{"name": "HexDigit$subexpression$5", "symbols": [/[eE]/], "postprocess": function(d) {return d.join(""); }},
|
||||||
|
{"name": "HexDigit", "symbols": ["HexDigit$subexpression$5"], "postprocess": c(14n)},
|
||||||
|
{"name": "HexDigit$subexpression$6", "symbols": [/[fF]/], "postprocess": function(d) {return d.join(""); }},
|
||||||
|
{"name": "HexDigit", "symbols": ["HexDigit$subexpression$6"], "postprocess": c(15n)}
|
||||||
|
];
|
||||||
|
let ParserStart = "StrNumericLiteral";
|
||||||
|
export default { Lexer, ParserRules, ParserStart };
|
||||||
@@ -0,0 +1,163 @@
|
|||||||
|
@preprocessor esmodule
|
||||||
|
|
||||||
|
@{%
|
||||||
|
import Scientific from './Scientific.mjs';
|
||||||
|
function c(val) {
|
||||||
|
return () => val;
|
||||||
|
}
|
||||||
|
%}
|
||||||
|
|
||||||
|
##################################################
|
||||||
|
# 7.1.3.1 #sec-tonumber-applied-to-the-string-type
|
||||||
|
|
||||||
|
StrNumericLiteral ->
|
||||||
|
StrDecimalLiteral {% ([StrDecimalLiteral]) => StrDecimalLiteral %}
|
||||||
|
| BinaryIntegerLiteral {% ([BinaryIntegerLiteral]) => BinaryIntegerLiteral %}
|
||||||
|
| OctalIntegerLiteral {% ([OctalIntegerLiteral]) => OctalIntegerLiteral %}
|
||||||
|
| HexIntegerLiteral {% ([HexIntegerLiteral]) => HexIntegerLiteral %}
|
||||||
|
|
||||||
|
# #prod-StrDecimalLiteral
|
||||||
|
StrDecimalLiteral ->
|
||||||
|
StrUnsignedDecimalLiteral {% ([StrUnsignedDecimalLiteral]) => StrUnsignedDecimalLiteral %}
|
||||||
|
| "+" StrUnsignedDecimalLiteral {% ([_, StrUnsignedDecimalLiteral]) => StrUnsignedDecimalLiteral %}
|
||||||
|
| "-" StrUnsignedDecimalLiteral {% ([_, StrUnsignedDecimalLiteral]) => StrUnsignedDecimalLiteral.negate() %}
|
||||||
|
|
||||||
|
# #prod-StrUnsignedDecimalLiteral
|
||||||
|
StrUnsignedDecimalLiteral ->
|
||||||
|
"Infinity" {% () => new Scientific(1n, 10000n) %}
|
||||||
|
| DecimalDigits "." {% ([[DecimalDigits]]) => new Scientific(DecimalDigits) %}
|
||||||
|
| DecimalDigits "." DecimalDigits {% ([[first], _, [second, n]]) => new Scientific(first).addSci(new Scientific(second, -n)) %}
|
||||||
|
| DecimalDigits "." ExponentPart {% ([[DecimalDigits], _, e]) => new Scientific(DecimalDigits, e) %}
|
||||||
|
| DecimalDigits "." DecimalDigits ExponentPart {% ([[first], _, [second, n], e]) => (new Scientific(first).addSci(new Scientific(second, -n))).expAdd(e) %}
|
||||||
|
| "." DecimalDigits {% ([_, [DecimalDigits, n]]) => new Scientific(DecimalDigits, -n) %}
|
||||||
|
| "." DecimalDigits ExponentPart {% ([_, [DecimalDigits, n], e]) => new Scientific(DecimalDigits, e - n) %}
|
||||||
|
| DecimalDigits {% ([[DecimalDigits]]) => new Scientific(DecimalDigits) %}
|
||||||
|
| DecimalDigits ExponentPart {% ([[DecimalDigits], e]) => new Scientific(DecimalDigits, e) %}
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# 11.8.3 #sec-literals-numeric-literals
|
||||||
|
|
||||||
|
# #prod-NumericLiteral
|
||||||
|
NumericLiteral ->
|
||||||
|
DecimalLiteral {% ([DecimalLiteral]) => DecimalLiteral %}
|
||||||
|
| BinaryIntegerLiteral {% ([BinaryIntegerLiteral]) => BinaryIntegerLiteral %}
|
||||||
|
| OctalIntegerLiteral {% ([OctalIntegerLiteral]) => OctalIntegerLiteral %}
|
||||||
|
| HexIntegerLiteral {% ([HexIntegerLiteral]) => HexIntegerLiteral %}
|
||||||
|
|
||||||
|
# #prod-DecimalLiteral
|
||||||
|
DecimalLiteral ->
|
||||||
|
DecimalIntegerLiteral "." {% ([DecimalIntegerLiteral]) => new Scientific(DecimalIntegerLiteral) %}
|
||||||
|
| DecimalIntegerLiteral "." DecimalDigits {% ([DecimalIntegerLiteral, _, [DecimalDigits, n]]) => new Scientific(DecimalIntegerLiteral).addSci(new Scientific(DecimalDigits, -n)) %}
|
||||||
|
| DecimalIntegerLiteral "." ExponentPart {% ([DecimalIntegerLiteral, _, e]) => new Scientific(DecimalIntegerLiteral, e) %}
|
||||||
|
| DecimalIntegerLiteral "." DecimalDigits ExponentPart {% ([DecimalIntegerLiteral, _, [DecimalDigits, n], e]) => new Scientific(DecimalIntegerLiteral).addSci(new Scientific(DecimalDigits, -n)).expAdd(e) %}
|
||||||
|
| "." DecimalDigits {% ([_, [DecimalDigits, n]]) => new Scientific(DecimalDigits, -n) %}
|
||||||
|
| "." DecimalDigits ExponentPart {% ([_, [DecimalDigits, n], e]) => new Scientific(DecimalDigits, e - n) %}
|
||||||
|
| DecimalIntegerLiteral {% ([DecimalIntegerLiteral]) => new Scientific(DecimalIntegerLiteral) %}
|
||||||
|
| DecimalIntegerLiteral ExponentPart {% ([DecimalIntegerLiteral, e]) => new Scientific(DecimalIntegerLiteral, e) %}
|
||||||
|
|
||||||
|
# #prod-DecimalIntegerLiteral
|
||||||
|
DecimalIntegerLiteral ->
|
||||||
|
"0" {% c(0n) %}
|
||||||
|
| NonZeroDigit {% ([NonZeroDigit]) => NonZeroDigit %}
|
||||||
|
| NonZeroDigit DecimalDigits {% ([NonZeroDigit, [DecimalDigits, n]]) => NonZeroDigit * (10n ** n) + DecimalDigits %}
|
||||||
|
|
||||||
|
# #prod-DecimalDigits
|
||||||
|
DecimalDigits ->
|
||||||
|
DecimalDigit {% ([DecimalDigit]) => [DecimalDigit, 1n] %}
|
||||||
|
| DecimalDigits DecimalDigit {% ([[DecimalDigits, n], DecimalDigit]) => [DecimalDigits * 10n + DecimalDigit, n + 1n] %}
|
||||||
|
|
||||||
|
# #prod-DecimalDigit
|
||||||
|
DecimalDigit ->
|
||||||
|
"0" {% c(0n) %}
|
||||||
|
| "1" {% c(1n) %}
|
||||||
|
| "2" {% c(2n) %}
|
||||||
|
| "3" {% c(3n) %}
|
||||||
|
| "4" {% c(4n) %}
|
||||||
|
| "5" {% c(5n) %}
|
||||||
|
| "6" {% c(6n) %}
|
||||||
|
| "7" {% c(7n) %}
|
||||||
|
| "8" {% c(8n) %}
|
||||||
|
| "9" {% c(9n) %}
|
||||||
|
|
||||||
|
# #prod-NonZeroDigit
|
||||||
|
NonZeroDigit ->
|
||||||
|
"1" {% c(1n) %}
|
||||||
|
| "2" {% c(2n) %}
|
||||||
|
| "3" {% c(3n) %}
|
||||||
|
| "4" {% c(4n) %}
|
||||||
|
| "5" {% c(5n) %}
|
||||||
|
| "6" {% c(6n) %}
|
||||||
|
| "7" {% c(7n) %}
|
||||||
|
| "8" {% c(8n) %}
|
||||||
|
| "9" {% c(9n) %}
|
||||||
|
|
||||||
|
# #prod-ExponentPart
|
||||||
|
ExponentPart -> ExponentIndicator SignedInteger {% ([_, SignedInteger]) => SignedInteger %}
|
||||||
|
|
||||||
|
# #prod-ExponentIndicator
|
||||||
|
ExponentIndicator -> "e"i
|
||||||
|
|
||||||
|
# #prod-SignedInteger
|
||||||
|
SignedInteger ->
|
||||||
|
DecimalDigits {% ([[DecimalDigits]]) => DecimalDigits %}
|
||||||
|
| "+" DecimalDigits {% ([_, [DecimalDigits]]) => DecimalDigits %}
|
||||||
|
| "-" DecimalDigits {% ([_, [DecimalDigits]]) => -DecimalDigits %}
|
||||||
|
|
||||||
|
# #prod-BinaryIntegerLiteral
|
||||||
|
BinaryIntegerLiteral -> "0b"i BinaryDigits {% ([_, BinaryDigits]) => new Scientific(BinaryDigits) %}
|
||||||
|
|
||||||
|
# #prod-BinaryDigits
|
||||||
|
BinaryDigits ->
|
||||||
|
BinaryDigit {% ([BinaryDigit]) => BinaryDigit %}
|
||||||
|
| BinaryDigits BinaryDigit {% ([BinaryDigits, BinaryDigit]) => BinaryDigits * 2n + BinaryDigit %}
|
||||||
|
|
||||||
|
# #prod-BinaryDigit
|
||||||
|
BinaryDigit ->
|
||||||
|
"0" {% c(0n) %}
|
||||||
|
| "1" {% c(1n) %}
|
||||||
|
|
||||||
|
# #prod-OctalIntegerLiteral
|
||||||
|
OctalIntegerLiteral -> "0o"i OctalDigits {% ([_, OctalDigits]) => new Scientific(OctalDigits) %}
|
||||||
|
|
||||||
|
# #prod-OctalDigits
|
||||||
|
OctalDigits ->
|
||||||
|
OctalDigit {% ([OctalDigit]) => OctalDigit %}
|
||||||
|
| OctalDigits OctalDigit {% ([OctalDigits, OctalDigit]) => OctalDigits * 8n + OctalDigit %}
|
||||||
|
|
||||||
|
# #prod-OctalDigit
|
||||||
|
OctalDigit ->
|
||||||
|
"0" {% c(0n) %}
|
||||||
|
| "1" {% c(1n) %}
|
||||||
|
| "2" {% c(2n) %}
|
||||||
|
| "3" {% c(3n) %}
|
||||||
|
| "4" {% c(4n) %}
|
||||||
|
| "5" {% c(5n) %}
|
||||||
|
| "6" {% c(6n) %}
|
||||||
|
| "7" {% c(7n) %}
|
||||||
|
|
||||||
|
# #prod-HexIntegerLiteral
|
||||||
|
HexIntegerLiteral -> "0x"i HexDigits {% ([_, HexDigits]) => new Scientific(HexDigits) %}
|
||||||
|
|
||||||
|
# #prod-HexDigits
|
||||||
|
HexDigits ->
|
||||||
|
HexDigit {% ([HexDigit]) => HexDigit %}
|
||||||
|
| HexDigits HexDigit {% ([HexDigits, HexDigit]) => HexDigits * 16n + HexDigit %}
|
||||||
|
|
||||||
|
# #prod-HexDigit
|
||||||
|
HexDigit ->
|
||||||
|
"0" {% c(0n) %}
|
||||||
|
| "1" {% c(1n) %}
|
||||||
|
| "2" {% c(2n) %}
|
||||||
|
| "3" {% c(3n) %}
|
||||||
|
| "4" {% c(4n) %}
|
||||||
|
| "5" {% c(5n) %}
|
||||||
|
| "6" {% c(6n) %}
|
||||||
|
| "7" {% c(7n) %}
|
||||||
|
| "8" {% c(8n) %}
|
||||||
|
| "9" {% c(9n) %}
|
||||||
|
| "a"i {% c(10n) %}
|
||||||
|
| "b"i {% c(11n) %}
|
||||||
|
| "c"i {% c(12n) %}
|
||||||
|
| "d"i {% c(13n) %}
|
||||||
|
| "e"i {% c(14n) %}
|
||||||
|
| "f"i {% c(15n) %}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import * as acorn from 'acorn';
|
||||||
|
|
||||||
|
const { isNewLine, nonASCIIwhitespace } = acorn;
|
||||||
|
|
||||||
|
function isWhiteSpace(c) {
|
||||||
|
return c === '\x09' // CHARACTER TABULATION
|
||||||
|
|| c === '\x0B' // LINE TABULATION
|
||||||
|
|| c === '\x0C' // FORM FEED (FF)
|
||||||
|
|| c === '\x20' // SPACE
|
||||||
|
|| c === '\xA0' // NO-BREAK SPACE
|
||||||
|
|| nonASCIIwhitespace.test(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const isLineTerminator = (c) => isNewLine(c.charCodeAt(0), false);
|
||||||
|
|
||||||
|
export const isStrWhiteSpaceChar = (c) => isWhiteSpace(c) || isLineTerminator(c);
|
||||||
|
|
||||||
|
// Returns index of first non-StrWhiteSpaceChar character.
|
||||||
|
export function searchNotStrWhiteSpaceChar(str) {
|
||||||
|
for (let i = 0; i < str.length; i += 1) {
|
||||||
|
if (!isStrWhiteSpaceChar(str[i])) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return str.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns index of last non-StrWhiteSpaceChar character + 1.
|
||||||
|
export function reverseSearchNotStrWhiteSpaceChar(str) {
|
||||||
|
for (let i = str.length - 1; i >= 0; i -= 1) {
|
||||||
|
if (!isStrWhiteSpaceChar(str[i])) {
|
||||||
|
return i + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,349 @@
|
|||||||
|
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) {
|
||||||
|
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 extends Map {
|
||||||
|
constructor(init) {
|
||||||
|
super();
|
||||||
|
if (init !== null && init !== undefined) {
|
||||||
|
for (const [k, v] of init) {
|
||||||
|
this.set(convertValueForKey(k), v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get(key) {
|
||||||
|
return super.get(convertValueForKey(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
set(key, value) {
|
||||||
|
return super.set(convertValueForKey(key), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
has(key) {
|
||||||
|
return super.has(convertValueForKey(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(key) {
|
||||||
|
return super.delete(convertValueForKey(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
* keys() {
|
||||||
|
for (const [key] of this) {
|
||||||
|
yield key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
* values() {
|
||||||
|
for (const [, value] of this) {
|
||||||
|
yield value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
entries() {
|
||||||
|
return this[Symbol.iterator]();
|
||||||
|
}
|
||||||
|
|
||||||
|
forEach(cb) {
|
||||||
|
for (const [key, value] of this) {
|
||||||
|
cb(value, key, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
* [Symbol.iterator]() {
|
||||||
|
for (const [key, value] of super.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 extends Set {
|
||||||
|
constructor(init) {
|
||||||
|
super();
|
||||||
|
if (init !== undefined && init !== null) {
|
||||||
|
for (const item of init) {
|
||||||
|
this.add(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
add(item) {
|
||||||
|
return super.add(convertValueForKey(item));
|
||||||
|
}
|
||||||
|
|
||||||
|
has(item) {
|
||||||
|
return super.has(convertValueForKey(item));
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(item) {
|
||||||
|
return super.delete(convertValueForKey(item));
|
||||||
|
}
|
||||||
|
|
||||||
|
keys() {
|
||||||
|
return this[Symbol.iterator]();
|
||||||
|
}
|
||||||
|
|
||||||
|
values() {
|
||||||
|
return this[Symbol.iterator]();
|
||||||
|
}
|
||||||
|
|
||||||
|
* [Symbol.iterator]() {
|
||||||
|
for (const key of super.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 {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
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) {
|
||||||
|
return this.context.Function.ECMAScriptCode && this.context.Function.ECMAScriptCode.async;
|
||||||
|
}
|
||||||
|
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.loc.start.line;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
get columnNumber() {
|
||||||
|
if (this.lastNode) {
|
||||||
|
return this.lastNode.loc.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.nativeFunction === AwaitFulfilledFunctions) {
|
||||||
|
const asyncContext = reaction.Handler.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,
|
||||||
|
})));
|
||||||
|
}
|
||||||
@@ -0,0 +1,192 @@
|
|||||||
|
import { surroundingAgent } from './engine.mjs';
|
||||||
|
import { Type, Value, wellKnownSymbols } from './value.mjs';
|
||||||
|
import { Realm as APIRealm } from './api.mjs';
|
||||||
|
import {
|
||||||
|
Call, IsArray, Get, LengthOfArrayLike,
|
||||||
|
} 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, ctx) => (ctx.quote ? `'${v.stringValue().replace(/\n/g, '\\n')}'` : v.stringValue()),
|
||||||
|
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) {
|
||||||
|
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 ('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, realm = surroundingAgent.currentRealmRecord) {
|
||||||
|
if (realm instanceof APIRealm) {
|
||||||
|
realm = realm.realm;
|
||||||
|
}
|
||||||
|
const context = {
|
||||||
|
realm,
|
||||||
|
indent: 0,
|
||||||
|
quote: true,
|
||||||
|
inspected: [],
|
||||||
|
};
|
||||||
|
const inner = (v) => INSPECTORS[Type(v)](v, context, inner);
|
||||||
|
return inner(value);
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
import { surroundingAgent } from '../engine.mjs';
|
||||||
|
import { Value } from '../value.mjs';
|
||||||
|
import {
|
||||||
|
CreateMethodProperty,
|
||||||
|
ToString,
|
||||||
|
IterableToList,
|
||||||
|
OrdinaryCreateFromConstructor,
|
||||||
|
} from '../abstract-ops/all.mjs';
|
||||||
|
import { Q, X } from '../completion.mjs';
|
||||||
|
import { captureStack } from '../helpers.mjs';
|
||||||
|
import { BootstrapConstructor } from './Bootstrap.mjs';
|
||||||
|
|
||||||
|
// https://tc39.es/proposal-promise-any/#sec-aggregate-error
|
||||||
|
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]], [[AggregateErrors]] »).
|
||||||
|
const O = Q(OrdinaryCreateFromConstructor(newTarget, '%AggregateError.prototype%', [
|
||||||
|
'ErrorData',
|
||||||
|
'AggregateErrors',
|
||||||
|
]));
|
||||||
|
// 3. Let errorsList be ? IterableToList(errors).
|
||||||
|
const errorsList = errors === Symbol.for('engine262.placeholder')
|
||||||
|
? []
|
||||||
|
: Q(IterableToList(errors));
|
||||||
|
// 4. Set O.[[AggregateErrors]] to errorsList.
|
||||||
|
O.AggregateErrors = errorsList;
|
||||||
|
// 5. 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import { Value } from '../value.mjs';
|
||||||
|
import { RequireInternalSlot, CreateArrayFromList } from '../abstract-ops/all.mjs';
|
||||||
|
import { Q, X } from '../completion.mjs';
|
||||||
|
import { BootstrapPrototype } from './Bootstrap.mjs';
|
||||||
|
|
||||||
|
// https://tc39.es/proposal-promise-any/#sec-aggregate-error.prototype.name
|
||||||
|
function AggregateErrorProto_errors(args, { thisValue }) {
|
||||||
|
// 1. Let E be the this value.
|
||||||
|
const E = thisValue;
|
||||||
|
// 2. If Type(E) is not Object, throw a TypeError exception.
|
||||||
|
// 3. If E does not have an [[ErrorData]] internal slot, throw a TypeError exception.
|
||||||
|
// 4. If E does not have an [[AggregateErrors]] internal slot, throw a TypeError exception.
|
||||||
|
Q(RequireInternalSlot(E, 'AggregateErrors'));
|
||||||
|
// 5. Return ! CreateArrayFromList(E.[[AggregateErrors]]).
|
||||||
|
return X(CreateArrayFromList(E.AggregateErrors));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function BootstrapAggregateErrorPrototype(realmRec) {
|
||||||
|
const proto = BootstrapPrototype(realmRec, [
|
||||||
|
['name', new Value('AggregateError')],
|
||||||
|
['message', new Value('')],
|
||||||
|
['errors', [AggregateErrorProto_errors]],
|
||||||
|
], realmRec.Intrinsics['%Error.prototype%'], 'AggregateError');
|
||||||
|
|
||||||
|
realmRec.Intrinsics['%AggregateError.prototype%'] = proto;
|
||||||
|
}
|
||||||
@@ -0,0 +1,215 @@
|
|||||||
|
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 = new 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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
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';
|
||||||
|
|
||||||
|
function ArrayIteratorPrototype_next(args, { thisValue }) {
|
||||||
|
const O = thisValue;
|
||||||
|
if (Type(O) !== 'Object') {
|
||||||
|
return surroundingAgent.Throw('TypeError', 'NotATypeObject', 'Array Iterator', O);
|
||||||
|
}
|
||||||
|
if (!('IteratedArrayLike' in O)
|
||||||
|
|| !('ArrayLikeNextIndex' in O)
|
||||||
|
|| !('ArrayLikeIterationKind' in O)) {
|
||||||
|
return surroundingAgent.Throw('TypeError', 'NotATypeObject', 'Array Iterator', O);
|
||||||
|
}
|
||||||
|
const a = O.IteratedArrayLike;
|
||||||
|
if (Type(a) === 'Undefined') {
|
||||||
|
return CreateIterResultObject(Value.undefined, Value.true);
|
||||||
|
}
|
||||||
|
const index = O.ArrayLikeNextIndex;
|
||||||
|
const itemKind = O.ArrayLikeIterationKind;
|
||||||
|
let len;
|
||||||
|
if ('TypedArrayName' in a) {
|
||||||
|
if (IsDetachedBuffer(a.ViewedArrayBuffer) === Value.true) {
|
||||||
|
return surroundingAgent.Throw('TypeError', 'ArrayBufferDetached');
|
||||||
|
}
|
||||||
|
len = a.ArrayLength;
|
||||||
|
} else {
|
||||||
|
len = Q(LengthOfArrayLike(a));
|
||||||
|
}
|
||||||
|
if (index >= len.numberValue()) {
|
||||||
|
O.IteratedArrayLike = Value.undefined;
|
||||||
|
return CreateIterResultObject(Value.undefined, Value.true);
|
||||||
|
}
|
||||||
|
O.ArrayLikeNextIndex = index + 1;
|
||||||
|
if (itemKind === 'key') {
|
||||||
|
return CreateIterResultObject(new Value(index), Value.false);
|
||||||
|
}
|
||||||
|
const elementKey = X(ToString(new Value(index)));
|
||||||
|
const elementValue = Q(Get(a, elementKey));
|
||||||
|
let result;
|
||||||
|
if (itemKind === 'value') {
|
||||||
|
result = elementValue;
|
||||||
|
} else {
|
||||||
|
Assert(itemKind === 'key+value');
|
||||||
|
result = X(CreateArrayFromList([new Value(index), elementValue]));
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,592 @@
|
|||||||
|
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, *+∞*, or *-∞*.
|
||||||
|
// 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,
|
||||||
|
})));
|
||||||
|
}
|
||||||
|
|
||||||
|
realmRec.Intrinsics['%Array.prototype%'] = proto;
|
||||||
|
|
||||||
|
realmRec.Intrinsics['%Array.prototype.keys%'] = proto.Get(new Value('keys'), proto);
|
||||||
|
realmRec.Intrinsics['%Array.prototype.entries%'] = proto.Get(new Value('entries'), proto);
|
||||||
|
realmRec.Intrinsics['%Array.prototype.values%'] = proto.Get(new Value('values'), proto);
|
||||||
|
}
|
||||||
@@ -0,0 +1,561 @@
|
|||||||
|
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],
|
||||||
|
]);
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
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';
|
||||||
|
|
||||||
|
// 25.1.4.2.1 #sec-%asyncfromsynciteratorprototype%.next
|
||||||
|
function AsyncFromSyncIteratorPrototype_next([value = Value.undefined], { thisValue }) {
|
||||||
|
const O = thisValue;
|
||||||
|
Assert(Type(O) === 'Object' && 'SyncIteratorRecord' in O);
|
||||||
|
const promiseCapability = X(NewPromiseCapability(surroundingAgent.intrinsic('%Promise%')));
|
||||||
|
const syncIteratorRecord = O.SyncIteratorRecord;
|
||||||
|
const result = IteratorNext(syncIteratorRecord, value);
|
||||||
|
IfAbruptRejectPromise(result, promiseCapability);
|
||||||
|
return X(AsyncFromSyncIteratorContinuation(result, promiseCapability));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 25.1.4.2.2 #sec-%asyncfromsynciteratorprototype%.return
|
||||||
|
function AsyncFromSyncIteratorPrototype_return([value = Value.undefined], { thisValue }) {
|
||||||
|
const O = thisValue;
|
||||||
|
Assert(Type(O) === 'Object' && 'SyncIteratorRecord' in O);
|
||||||
|
const promiseCapability = X(NewPromiseCapability(surroundingAgent.intrinsic('%Promise%')));
|
||||||
|
const syncIterator = O.SyncIteratorRecord.Iterator;
|
||||||
|
const ret = GetMethod(syncIterator, new Value('return'));
|
||||||
|
IfAbruptRejectPromise(ret, promiseCapability);
|
||||||
|
if (ret === Value.undefined) {
|
||||||
|
const iterResult = X(CreateIterResultObject(value, Value.true));
|
||||||
|
X(Call(promiseCapability.Resolve, Value.undefined, [iterResult]));
|
||||||
|
return promiseCapability.Promise;
|
||||||
|
}
|
||||||
|
const result = Call(ret, syncIterator, [value]);
|
||||||
|
IfAbruptRejectPromise(result, promiseCapability);
|
||||||
|
if (Type(result) !== 'Object') {
|
||||||
|
X(Call(promiseCapability.Reject, Value.undefined, [
|
||||||
|
surroundingAgent.Throw('TypeError', 'NotAnObject', result).Value,
|
||||||
|
]));
|
||||||
|
return promiseCapability.Promise;
|
||||||
|
}
|
||||||
|
return X(AsyncFromSyncIteratorContinuation(result, promiseCapability));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 25.1.4.2.3 #sec-%asyncfromsynciteratorprototype%.throw
|
||||||
|
function AsyncFromSyncIteratorPrototype_throw([value = Value.undefined], { thisValue }) {
|
||||||
|
const O = thisValue;
|
||||||
|
Assert(Type(O) === 'Object' && 'SyncIteratorRecord' in O);
|
||||||
|
const promiseCapability = X(NewPromiseCapability(surroundingAgent.intrinsic('%Promise%')));
|
||||||
|
const syncIterator = O.SyncIteratorRecord.Iterator;
|
||||||
|
const thr = GetMethod(syncIterator, new Value('throw'));
|
||||||
|
IfAbruptRejectPromise(thr, promiseCapability);
|
||||||
|
if (thr === Value.undefined) {
|
||||||
|
X(Call(promiseCapability.Reject, Value.undefined, [value]));
|
||||||
|
return promiseCapability.Promise;
|
||||||
|
}
|
||||||
|
const result = Call(thr, syncIterator, [value]);
|
||||||
|
IfAbruptRejectPromise(result, promiseCapability);
|
||||||
|
if (Type(result) !== 'Object') {
|
||||||
|
X(Call(promiseCapability.Reject, Value.undefined, [
|
||||||
|
surroundingAgent.Throw('TypeError', 'NotAnObject', result).Value,
|
||||||
|
]));
|
||||||
|
return promiseCapability.Promise;
|
||||||
|
}
|
||||||
|
return X(AsyncFromSyncIteratorContinuation(result, promiseCapability));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function BootstrapAsyncFromSyncIteratorPrototype(realmRec) {
|
||||||
|
const proto = BootstrapPrototype(realmRec, [
|
||||||
|
['next', AsyncFromSyncIteratorPrototype_next, 1],
|
||||||
|
['return', AsyncFromSyncIteratorPrototype_return, 1],
|
||||||
|
['throw', AsyncFromSyncIteratorPrototype_throw, 1],
|
||||||
|
], realmRec.Intrinsics['%AsyncIteratorPrototype%']);
|
||||||
|
|
||||||
|
realmRec.Intrinsics['%AsyncFromSyncIteratorPrototype%'] = proto;
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
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';
|
||||||
|
|
||||||
|
function AsyncFunctionConstructor(args, { NewTarget }) {
|
||||||
|
const C = surroundingAgent.activeFunctionObject;
|
||||||
|
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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import { BootstrapPrototype } from './Bootstrap.mjs';
|
||||||
|
|
||||||
|
export function BootstrapAsyncFunctionPrototype(realmRec) {
|
||||||
|
const proto = BootstrapPrototype(realmRec, [], realmRec.Intrinsics['%Function.prototype%'], 'AsyncFunction');
|
||||||
|
|
||||||
|
realmRec.Intrinsics['%AsyncFunction.prototype%'] = proto;
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
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';
|
||||||
|
|
||||||
|
function AsyncGeneratorFunctionConstructor(args, { NewTarget }) {
|
||||||
|
const C = surroundingAgent.activeFunctionObject;
|
||||||
|
return Q(CreateDynamicFunction(C, NewTarget, 'async generator', 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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
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';
|
||||||
|
|
||||||
|
function AsyncGeneratorPrototype_next([value = Value.undefined], { thisValue }) {
|
||||||
|
const generator = thisValue;
|
||||||
|
const completion = new NormalCompletion(value);
|
||||||
|
return X(AsyncGeneratorEnqueue(generator, completion));
|
||||||
|
}
|
||||||
|
|
||||||
|
function AsyncGeneratorPrototype_return([value = Value.undefined], { thisValue }) {
|
||||||
|
const generator = thisValue;
|
||||||
|
const completion = new Completion('return', value, undefined);
|
||||||
|
return X(AsyncGeneratorEnqueue(generator, completion));
|
||||||
|
}
|
||||||
|
|
||||||
|
function AsyncGeneratorPrototype_throw([exception = Value.undefined], { thisValue }) {
|
||||||
|
const generator = thisValue;
|
||||||
|
const completion = new ThrowCompletion(exception);
|
||||||
|
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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import { wellKnownSymbols } from '../value.mjs';
|
||||||
|
import { BootstrapPrototype } from './Bootstrap.mjs';
|
||||||
|
|
||||||
|
function AsyncIteratorPrototype_asyncIterator(args, { thisValue }) {
|
||||||
|
return thisValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function BootstrapAsyncIteratorPrototype(realmRec) {
|
||||||
|
const proto = BootstrapPrototype(realmRec, [
|
||||||
|
[wellKnownSymbols.asyncIterator, AsyncIteratorPrototype_asyncIterator, 0],
|
||||||
|
], realmRec.Intrinsics['%Object.prototype%']);
|
||||||
|
|
||||||
|
realmRec.Intrinsics['%AsyncIteratorPrototype%'] = proto;
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
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';
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import {
|
||||||
|
OrdinaryCreateFromConstructor,
|
||||||
|
ToBoolean,
|
||||||
|
} from '../abstract-ops/all.mjs';
|
||||||
|
import { Type, Value } from '../value.mjs';
|
||||||
|
import { Q } from '../completion.mjs';
|
||||||
|
import { BootstrapConstructor } from './Bootstrap.mjs';
|
||||||
|
|
||||||
|
function BooleanConstructor([value = Value.undefined], { NewTarget }) {
|
||||||
|
const b = ToBoolean(value);
|
||||||
|
if (Type(NewTarget) === 'Undefined') {
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
const O = Q(OrdinaryCreateFromConstructor(NewTarget, '%Boolean.prototype%', ['BooleanData']));
|
||||||
|
O.BooleanData = b;
|
||||||
|
return O;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function BootstrapBoolean(realmRec) {
|
||||||
|
const cons = BootstrapConstructor(
|
||||||
|
realmRec, BooleanConstructor, 'Boolean', 1,
|
||||||
|
realmRec.Intrinsics['%Boolean.prototype%'], [],
|
||||||
|
);
|
||||||
|
|
||||||
|
realmRec.Intrinsics['%Boolean%'] = cons;
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
function BooleanProto_toString(argList, { thisValue }) {
|
||||||
|
const b = Q(thisBooleanValue(thisValue));
|
||||||
|
if (b === Value.true) {
|
||||||
|
return new Value('true');
|
||||||
|
}
|
||||||
|
return new Value('false');
|
||||||
|
}
|
||||||
|
|
||||||
|
function BooleanProto_valueOf(argList, { thisValue }) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,275 @@
|
|||||||
|
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], { 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 ? GetViewValue(v, byteOffset, littleEndian, BigInt64).
|
||||||
|
return Q(GetViewValue(v, byteOffset, littleEndian, 'BigInt64'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// #sec-dataview.prototype.getbiguint64
|
||||||
|
function DataViewProto_getBigUint64([byteOffset = 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 ? 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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,203 @@
|
|||||||
|
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';
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,697 @@
|
|||||||
|
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 }) {
|
||||||
|
const t = LocalTime(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 u = TimeClip(UTC(date));
|
||||||
|
thisValue.DateValue = 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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import {
|
||||||
|
DefinePropertyOrThrow,
|
||||||
|
OrdinaryCreateFromConstructor,
|
||||||
|
ToString,
|
||||||
|
} from '../abstract-ops/all.mjs';
|
||||||
|
import {
|
||||||
|
Descriptor,
|
||||||
|
Type,
|
||||||
|
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';
|
||||||
|
|
||||||
|
function ErrorConstructor([message = Value.undefined], { NewTarget }) {
|
||||||
|
let newTarget;
|
||||||
|
if (Type(NewTarget) === 'Undefined') {
|
||||||
|
newTarget = surroundingAgent.activeFunctionObject;
|
||||||
|
} else {
|
||||||
|
newTarget = NewTarget;
|
||||||
|
}
|
||||||
|
const O = Q(OrdinaryCreateFromConstructor(newTarget, '%Error.prototype%', ['ErrorData']));
|
||||||
|
if (Type(message) !== 'Undefined') {
|
||||||
|
const msg = Q(ToString(message));
|
||||||
|
const msgDesc = Descriptor({
|
||||||
|
Value: msg,
|
||||||
|
Writable: Value.true,
|
||||||
|
Enumerable: Value.false,
|
||||||
|
Configurable: Value.true,
|
||||||
|
});
|
||||||
|
X(DefinePropertyOrThrow(O, new Value('message'), msgDesc));
|
||||||
|
}
|
||||||
|
|
||||||
|
X(captureStack(O)); // non-spec
|
||||||
|
|
||||||
|
return O;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function BootstrapError(realmRec) {
|
||||||
|
const error = BootstrapConstructor(realmRec, ErrorConstructor, 'Error', 1, realmRec.Intrinsics['%Error.prototype%'], []);
|
||||||
|
|
||||||
|
realmRec.Intrinsics['%Error%'] = error;
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
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';
|
||||||
|
|
||||||
|
function ErrorProto_toString(args, { thisValue }) {
|
||||||
|
const O = thisValue;
|
||||||
|
if (Type(O) !== 'Object') {
|
||||||
|
return surroundingAgent.Throw('TypeError', 'NotAnObject', O);
|
||||||
|
}
|
||||||
|
let name = Q(Get(O, new Value('name')));
|
||||||
|
if (Type(name) === 'Undefined') {
|
||||||
|
name = new Value('Error');
|
||||||
|
} else {
|
||||||
|
name = Q(ToString(name));
|
||||||
|
}
|
||||||
|
let msg = Q(Get(O, new Value('message')));
|
||||||
|
if (Type(msg) === 'Undefined') {
|
||||||
|
msg = new Value('');
|
||||||
|
} else {
|
||||||
|
msg = Q(ToString(msg));
|
||||||
|
}
|
||||||
|
if (name.stringValue() === '') {
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
if (msg.stringValue() === '') {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
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';
|
||||||
|
|
||||||
|
// https://tc39.es/proposal-weakrefs/#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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
import { surroundingAgent } from '../engine.mjs';
|
||||||
|
import { Value, Type } from '../value.mjs';
|
||||||
|
import {
|
||||||
|
CleanupFinalizationRegistry,
|
||||||
|
RequireInternalSlot,
|
||||||
|
IsCallable,
|
||||||
|
SameValue,
|
||||||
|
} from '../abstract-ops/all.mjs';
|
||||||
|
import { Q } from '../completion.mjs';
|
||||||
|
import { BootstrapPrototype } from './Bootstrap.mjs';
|
||||||
|
|
||||||
|
// https://tc39.es/proposal-weakrefs/#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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://tc39.es/proposal-weakrefs/#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 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://tc39.es/proposal-weakrefs/#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 not undefined 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function BootstrapFinalizationRegistryPrototype(realmRec) {
|
||||||
|
const proto = BootstrapPrototype(realmRec, [
|
||||||
|
['register', FinalizationRegistryProto_register, 2],
|
||||||
|
['unregister', FinalizationRegistryProto_unregister, 1],
|
||||||
|
['cleanupSome', FinalizationRegistryProto_cleanupSome, 0],
|
||||||
|
], realmRec.Intrinsics['%Object.prototype%'], 'FinalizationRegistry');
|
||||||
|
|
||||||
|
realmRec.Intrinsics['%FinalizationRegistry.prototype%'] = proto;
|
||||||
|
}
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import { surroundingAgent } from '../engine.mjs';
|
||||||
|
import { Q } from '../completion.mjs';
|
||||||
|
import { CreateDynamicFunction } from '../runtime-semantics/all.mjs';
|
||||||
|
import { BootstrapConstructor } from './Bootstrap.mjs';
|
||||||
|
|
||||||
|
function FunctionConstructor(args, { NewTarget }) {
|
||||||
|
const C = surroundingAgent.activeFunctionObject;
|
||||||
|
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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,181 @@
|
|||||||
|
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';
|
||||||
|
|
||||||
|
function FunctionProto_apply([thisArg = Value.undefined, argArray = Value.undefined], { thisValue: func }) {
|
||||||
|
if (IsCallable(func) === Value.false) {
|
||||||
|
return surroundingAgent.Throw('TypeError', 'NotAFunction', func);
|
||||||
|
}
|
||||||
|
if (Type(argArray) === 'Undefined' || Type(argArray) === 'Null') {
|
||||||
|
PrepareForTailCall();
|
||||||
|
return Q(Call(func, thisArg));
|
||||||
|
}
|
||||||
|
const argList = Q(CreateListFromArrayLike(argArray));
|
||||||
|
PrepareForTailCall();
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 9.4.1.3 #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;
|
||||||
|
}
|
||||||
|
|
||||||
|
function FunctionProto_bind([thisArg = Value.undefined, ...args], { thisValue }) {
|
||||||
|
const Target = thisValue;
|
||||||
|
if (IsCallable(Target) === Value.false) {
|
||||||
|
return surroundingAgent.Throw('TypeError', 'NotAFunction', Target);
|
||||||
|
}
|
||||||
|
// Let args be a new (possibly empty) List consisting of all
|
||||||
|
// of the argument values provided after thisArg in order.
|
||||||
|
const F = Q(BoundFunctionCreate(Target, thisArg, args));
|
||||||
|
const targetHasLength = Q(HasOwnProperty(Target, new Value('length')));
|
||||||
|
let L;
|
||||||
|
if (targetHasLength === Value.true) {
|
||||||
|
let targetLen = Q(Get(Target, new Value('length')));
|
||||||
|
if (Type(targetLen) !== 'Number') {
|
||||||
|
L = 0;
|
||||||
|
} else {
|
||||||
|
targetLen = Q(ToInteger(targetLen)).numberValue();
|
||||||
|
L = Math.max(0, targetLen - args.length);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
L = 0;
|
||||||
|
}
|
||||||
|
X(SetFunctionLength(F, new Value(L)));
|
||||||
|
let targetName = Q(Get(Target, new Value('name')));
|
||||||
|
if (Type(targetName) !== 'String') {
|
||||||
|
targetName = new Value('');
|
||||||
|
}
|
||||||
|
SetFunctionName(F, targetName, new Value('bound'));
|
||||||
|
return F;
|
||||||
|
}
|
||||||
|
|
||||||
|
function FunctionProto_call([thisArg = Value.undefined, ...args], { thisValue: func }) {
|
||||||
|
if (IsCallable(func) === Value.false) {
|
||||||
|
return surroundingAgent.Throw('TypeError', 'NotAFunction', func);
|
||||||
|
}
|
||||||
|
const argList = [];
|
||||||
|
for (const arg of args) {
|
||||||
|
argList.push(arg);
|
||||||
|
}
|
||||||
|
PrepareForTailCall();
|
||||||
|
return Q(Call(func, thisArg, argList));
|
||||||
|
}
|
||||||
|
|
||||||
|
function FunctionProto_toString(args, { thisValue: func }) {
|
||||||
|
if ('BoundTargetFunction' in func || 'nativeFunction' in func) {
|
||||||
|
const name = func.properties.get(new Value('name'));
|
||||||
|
if (name !== undefined) {
|
||||||
|
return new Value(`function ${name.Value.stringValue()}() { [native code] }`);
|
||||||
|
}
|
||||||
|
return new Value('function() { [native code] }');
|
||||||
|
}
|
||||||
|
if (Type(func) === 'Object' && 'SourceText' in func && Type(func.SourceText) === 'String' && X(HostHasSourceTextAvailable(func)) === Value.true) {
|
||||||
|
return func.SourceText;
|
||||||
|
}
|
||||||
|
if (Type(func) === 'Object' && IsCallable(func) === Value.true) {
|
||||||
|
return new Value('function() { [native code] }');
|
||||||
|
}
|
||||||
|
return surroundingAgent.Throw('TypeError', 'NotAFunction', func);
|
||||||
|
}
|
||||||
|
|
||||||
|
function FunctionProto_hasInstance([V = Value.undefined], { thisValue }) {
|
||||||
|
const F = thisValue;
|
||||||
|
return Q(OrdinaryHasInstance(F, V));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function BootstrapFunctionPrototype(realmRec) {
|
||||||
|
const proto = CreateBuiltinFunction(() => Value.undefined, [], 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],
|
||||||
|
]);
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user