mirror of
https://github.com/bendtherules/completion-viz.git
synced 2026-08-18 13:42:51 +00:00
Delete engine262 project
This commit is contained in:
@@ -1,3 +0,0 @@
|
||||
!.eslintrc.js
|
||||
test/test262/test262
|
||||
test/json/JSONTestSuite
|
||||
@@ -1,81 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const Module = require('module');
|
||||
|
||||
const ModuleFindPath = Module._findPath;
|
||||
const hacks = [
|
||||
'eslint-plugin-engine262',
|
||||
];
|
||||
Module._findPath = (request, paths, isMain) => {
|
||||
const r = ModuleFindPath(request, paths, isMain);
|
||||
if (!r && hacks.includes(request)) {
|
||||
return require.resolve(`./test/${request}`);
|
||||
}
|
||||
return r;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: 'airbnb-base',
|
||||
plugins: ['engine262'],
|
||||
parser: '@babel/eslint-parser',
|
||||
parserOptions: {
|
||||
ecmaVersion: 2020,
|
||||
requireConfigFile: false,
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: ['*.js'],
|
||||
parserOptions: { sourceType: 'script' },
|
||||
},
|
||||
],
|
||||
globals: {
|
||||
globalThis: false,
|
||||
Atomics: false,
|
||||
BigInt: false,
|
||||
BigUint64Array: false,
|
||||
SharedArrayBuffer: false,
|
||||
},
|
||||
rules: {
|
||||
'arrow-parens': ['error', 'always'],
|
||||
'brace-style': ['error', '1tbs', { allowSingleLine: false }],
|
||||
'curly': ['error', 'all'],
|
||||
'engine262/no-use-in-def': 'error',
|
||||
'engine262/valid-feature': 'error',
|
||||
'engine262/valid-throw': 'error',
|
||||
'import/order': ['error', { 'newlines-between': 'never' }],
|
||||
'import/no-extraneous-dependencies': ['error', { devDependencies: true }],
|
||||
'no-multiple-empty-lines': ['error', { maxBOF: 0, max: 2 }],
|
||||
'no-unused-vars': ['error', {
|
||||
vars: 'all',
|
||||
varsIgnorePattern: '^_',
|
||||
args: 'after-used',
|
||||
argsIgnorePattern: '^_',
|
||||
}],
|
||||
'no-empty': ['error', { allowEmptyCatch: true }],
|
||||
'quote-props': ['error', 'consistent'],
|
||||
'strict': ['error', 'global'],
|
||||
|
||||
'camelcase': 'off',
|
||||
'class-methods-use-this': 'off',
|
||||
'global-require': 'off',
|
||||
'import/extensions': 'off',
|
||||
'import/no-cycle': 'off',
|
||||
'import/no-mutable-exports': 'off',
|
||||
'import/prefer-default-export': 'off',
|
||||
'lines-between-class-members': 'off',
|
||||
'max-classes-per-file': 'off',
|
||||
'max-len': 'off',
|
||||
'no-bitwise': 'off',
|
||||
'no-constant-condition': 'off',
|
||||
'no-continue': 'off',
|
||||
'no-else-return': 'off',
|
||||
'no-lonely-if': 'off',
|
||||
'no-loop-func': 'off',
|
||||
'no-param-reassign': 'off',
|
||||
'no-restricted-syntax': 'off',
|
||||
'no-underscore-dangle': 'off',
|
||||
'no-use-before-define': 'off',
|
||||
'prefer-destructuring': 'off',
|
||||
},
|
||||
};
|
||||
Vendored
-12
@@ -1,12 +0,0 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: [engine262, devsnek] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
patreon: # Replace with a single Patreon username
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
otechie: # Replace with a single Otechie username
|
||||
custom: # Replace with a single custom sponsorship URL
|
||||
-55
@@ -1,55 +0,0 @@
|
||||
name: Publish Package
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
publish-gpr:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
# Set everything up
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-node@master
|
||||
with:
|
||||
node-version: 12
|
||||
registry-url: https://npm.pkg.github.com/
|
||||
scope: '@engine262'
|
||||
always-auth: true # required if using yarn
|
||||
- run: git submodule update --init --recursive
|
||||
|
||||
# Run tests and whatnot
|
||||
- run: npm install
|
||||
- run: npm run build
|
||||
- run: npm run lint
|
||||
- run: npm run coverage
|
||||
env:
|
||||
CONTINUOUS_INTEGRATION: 1
|
||||
|
||||
# Upload coverage data
|
||||
- name: Coveralls
|
||||
uses: coverallsapp/github-action@master
|
||||
with:
|
||||
github-token: ${{github.token}}
|
||||
|
||||
# Publish built package
|
||||
- run: npm publish --access=public
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{github.token}}
|
||||
|
||||
# Push build to to gh-pages
|
||||
- run: |
|
||||
git config --global user.email "gha@example.com"
|
||||
git config --global user.name "GHA"
|
||||
git remote add github "https://$GITHUB_ACTOR:$BETTER_GITHUB_TOKEN@github.com/$GITHUB_REPOSITORY.git"
|
||||
git fetch github
|
||||
git checkout gh-pages
|
||||
cp dist/* .
|
||||
git add engine262.*
|
||||
git commit -m "autobuild" || exit 0 # exit silently if nothing changed
|
||||
git push -u github gh-pages
|
||||
env:
|
||||
BETTER_GITHUB_TOKEN: ${{secrets.BETTER_GITHUB_TOKEN}}
|
||||
@@ -1,6 +0,0 @@
|
||||
node_modules
|
||||
dist
|
||||
test.mjs
|
||||
.eslintcache
|
||||
coverage
|
||||
.nyc_output
|
||||
@@ -1,6 +0,0 @@
|
||||
[submodule "test/JSONTestSuite"]
|
||||
path = test/json/JSONTestSuite
|
||||
url = https://github.com/nst/JSONTestSuite
|
||||
[submodule "test/test262/test262"]
|
||||
path = test/test262/test262
|
||||
url = https://github.com/tc39/test262
|
||||
@@ -1,9 +0,0 @@
|
||||
test
|
||||
src
|
||||
scripts
|
||||
coverage
|
||||
rollup.config.js
|
||||
.eslintcache
|
||||
.eslintrc.js
|
||||
.eslintignore
|
||||
.travis.yml
|
||||
@@ -1 +0,0 @@
|
||||
package-lock=false
|
||||
@@ -1,46 +0,0 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at devsnek@users.noreply.github.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
|
||||
|
||||
[homepage]: http://contributor-covenant.org
|
||||
[version]: http://contributor-covenant.org/version/1/4/
|
||||
@@ -1,19 +0,0 @@
|
||||
Copyright (c) 2018 engine262 Contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to
|
||||
deal in the Software without restriction, including without limitation the
|
||||
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
sell copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
IN THE SOFTWARE.
|
||||
@@ -1,163 +0,0 @@
|
||||
# engine262
|
||||
|
||||
An implementation of ECMA-262 in JavaScript
|
||||
|
||||
Goals
|
||||
- 100% Spec Compliance
|
||||
- Introspection
|
||||
- Ease of modification
|
||||
|
||||
Non-Goals
|
||||
- Speed at the expense of any of the goals
|
||||
|
||||
This project is bound by a [Code of Conduct][COC].
|
||||
|
||||
Join us on [#engine262 on freenode][irc] ([web][irc-webchat]).
|
||||
|
||||
## Why this exists
|
||||
|
||||
While helping develop new features for JavaScript, I've found that one of the
|
||||
most useful methods of finding what works and what doesn't is being able to
|
||||
actually run code using the new feature. [Babel][] is fantastic for this, but
|
||||
sometimes features just can't be nicely represented with it. Similarly,
|
||||
implementing a feature in one of the engines is a large undertaking, involving
|
||||
long compile times and annoying bugs with the optimizing compilers.
|
||||
|
||||
engine262 is a tool to allow JavaScript developers to have a sandbox where new
|
||||
features can be quickly prototyped and explored. As an example, adding
|
||||
[do expressions][] to this engine is as simple as the following diff:
|
||||
|
||||
```diff
|
||||
--- a/src/evaluator.mjs
|
||||
+++ b/src/evaluator.mjs
|
||||
@@ -232,6 +232,8 @@ export function* Evaluate(node) {
|
||||
case 'GeneratorBody':
|
||||
case 'AsyncGeneratorBody':
|
||||
return yield* Evaluate_AnyFunctionBody(node);
|
||||
+ case 'DoExpression':
|
||||
+ return yield* Evaluate_Block(node.Block);
|
||||
default:
|
||||
throw new OutOfRange('Evaluate', node);
|
||||
}
|
||||
--- a/src/parser/ExpressionParser.mjs
|
||||
+++ b/src/parser/ExpressionParser.mjs
|
||||
@@ -579,6 +579,12 @@ export class ExpressionParser extends FunctionParser {
|
||||
return this.parseRegularExpressionLiteral();
|
||||
case Token.LPAREN:
|
||||
return this.parseParenthesizedExpression();
|
||||
+ case Token.DO: {
|
||||
+ const node = this.startNode();
|
||||
+ this.next();
|
||||
+ node.Block = this.parseBlock();
|
||||
+ return this.finishNode(node, 'DoExpression');
|
||||
+ }
|
||||
default:
|
||||
return this.unexpected();
|
||||
}
|
||||
```
|
||||
|
||||
This simplicity applies to many other proposals, such as [optional chaining][],
|
||||
[pattern matching][], [the pipeline operator][], and more. This engine has also
|
||||
been used to find bugs in ECMA-262 and test262, the test suite for
|
||||
conforming JavaScript implementations.
|
||||
|
||||
## Requirements
|
||||
|
||||
To run engine262 itself, a engine with support for recent ECMAScript features
|
||||
is needed. Additionally, the CLI (`bin/engine262.js`) and test262 runner
|
||||
(`test/test262.js`) require a recent version of Node.js.
|
||||
|
||||
## Using engine262
|
||||
|
||||
Use it online: https://engine262.js.org
|
||||
|
||||
You can install the latest engine262 build from [GitHub Packages][].
|
||||
|
||||
If you install it globally, you can use the CLI like so:
|
||||
|
||||
`$ engine262`
|
||||
|
||||
Or, you can install it locally and use the API:
|
||||
|
||||
```js
|
||||
'use strict';
|
||||
|
||||
const {
|
||||
Agent,
|
||||
setSurroundingAgent,
|
||||
ManagedRealm,
|
||||
Value,
|
||||
|
||||
CreateDataProperty,
|
||||
|
||||
inspect,
|
||||
} = require('engine262');
|
||||
|
||||
const agent = new Agent({
|
||||
// onDebugger() {},
|
||||
// ensureCanCompileStrings() {},
|
||||
// hasSourceTextAvailable() {},
|
||||
// onNodeEvaluation() {},
|
||||
// features: [],
|
||||
});
|
||||
setSurroundingAgent(agent);
|
||||
|
||||
const realm = new ManagedRealm({
|
||||
// promiseRejectionTracker() {},
|
||||
// resolveImportedModule() {},
|
||||
// getImportMetaProperties() {},
|
||||
// finalizeImportMeta() {},
|
||||
// randomSeed() {},
|
||||
});
|
||||
|
||||
realm.scope(() => {
|
||||
// Add print function from host
|
||||
const print = new Value((args) => {
|
||||
console.log(...args.map((tmp) => inspect(tmp)));
|
||||
return Value.undefined;
|
||||
});
|
||||
CreateDataProperty(realm.GlobalObject, new Value('print'), print);
|
||||
});
|
||||
|
||||
realm.evaluateScript(`
|
||||
'use strict';
|
||||
|
||||
async function* numbers() {
|
||||
let i = 0;
|
||||
while (true) {
|
||||
const n = await Promise.resolve(i++);
|
||||
yield n;
|
||||
}
|
||||
}
|
||||
|
||||
(async () => {
|
||||
for await (const item of numbers()) {
|
||||
print(item);
|
||||
}
|
||||
})();
|
||||
`);
|
||||
|
||||
// a stream of numbers fills your console. it fills you with determination.
|
||||
```
|
||||
|
||||
## Related Projects
|
||||
|
||||
Many people and organizations have attempted to write a JavaScript interpreter
|
||||
in JavaScript much like engine262, with different goals. Some of them are
|
||||
included here for reference, though engine262 is not based on any of them.
|
||||
|
||||
- https://github.com/facebook/prepack
|
||||
- https://github.com/mozilla/narcissus
|
||||
- https://github.com/NeilFraser/JS-Interpreter
|
||||
- https://github.com/metaes/metaes
|
||||
- https://github.com/Siubaak/sval
|
||||
|
||||
[Babel]: https://babeljs.io/
|
||||
[COC]: https://github.com/engine262/engine262/blob/master/CODE_OF_CONDUCT.md
|
||||
[do expressions]: https://github.com/tc39/proposal-do-expressions
|
||||
[irc]: ircs://chat.freenode.net:6697/engine262
|
||||
[irc-webchat]: https://webchat.freenode.net/?channels=engine262
|
||||
[optional chaining]: https://github.com/tc39/proposal-optional-chaining
|
||||
[pattern matching]: https://github.com/tc39/proposal-pattern-matching
|
||||
[the pipeline operator]: https://github.com/tc39/proposal-pipeline-operator
|
||||
[GitHub Packages]: https://github.com/engine262/engine262/packages
|
||||
@@ -1,217 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
'use strict';
|
||||
|
||||
/* eslint-disable import/order */
|
||||
|
||||
try {
|
||||
require('@snek/source-map-support/register');
|
||||
} catch {
|
||||
// empty
|
||||
}
|
||||
|
||||
const repl = require('repl');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const util = require('util');
|
||||
const packageJson = require('../package.json');
|
||||
const snekparse = require('./snekparse');
|
||||
const {
|
||||
Agent,
|
||||
setSurroundingAgent,
|
||||
|
||||
FEATURES,
|
||||
inspect,
|
||||
|
||||
Value,
|
||||
|
||||
CreateDataProperty,
|
||||
OrdinaryObjectCreate,
|
||||
Type,
|
||||
|
||||
Completion,
|
||||
AbruptCompletion,
|
||||
Throw,
|
||||
} = require('..');
|
||||
const { createRealm } = require('./test262_realm');
|
||||
|
||||
const execArgv = [];
|
||||
let entry;
|
||||
const programArgv = [];
|
||||
|
||||
{
|
||||
let target = execArgv;
|
||||
process.argv.slice(2).forEach((a) => {
|
||||
if (a.startsWith('--')) {
|
||||
target.push(a);
|
||||
} else if (!entry) {
|
||||
entry = a;
|
||||
target = programArgv;
|
||||
} else {
|
||||
target.push(a);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const help = `
|
||||
engine262 v${require('../package.json').version}
|
||||
|
||||
Usage:
|
||||
|
||||
engine262 [options]
|
||||
engine262 [options] [input file]
|
||||
engine262 [input file]
|
||||
|
||||
Options:
|
||||
|
||||
-h, --help Show help (this screen)
|
||||
-m, --module Evaluate contents of input-file as a module.
|
||||
Must be followed by input-file
|
||||
--features=... A comma separated list of features. If no features
|
||||
are provided, the available features are listed.
|
||||
|
||||
`;
|
||||
|
||||
const argv = snekparse(execArgv);
|
||||
|
||||
if (argv.h || argv.help) {
|
||||
process.stdout.write(help);
|
||||
process.exit(0);
|
||||
} else if (argv.features === true) {
|
||||
let nameLength = 0;
|
||||
let flagLength = 0;
|
||||
FEATURES.forEach((f) => {
|
||||
if (f.name.length > nameLength) {
|
||||
nameLength = f.name.length;
|
||||
}
|
||||
if (f.flag.length > flagLength) {
|
||||
flagLength = f.flag.length;
|
||||
}
|
||||
});
|
||||
const log = (n, f, u) => {
|
||||
process.stdout.write(`${n.padEnd(nameLength, ' ')} ${f.padEnd(flagLength, ' ')} ${u}\n`);
|
||||
};
|
||||
log('name', 'flag', 'url');
|
||||
log('----', '----', '---');
|
||||
FEATURES.forEach((f) => {
|
||||
log(f.name, f.flag, f.url);
|
||||
});
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
let features;
|
||||
if (argv.features === 'all') {
|
||||
features = FEATURES.map((f) => f.flag);
|
||||
} else if (argv.features) {
|
||||
features = argv.features.split(',');
|
||||
} else {
|
||||
features = [];
|
||||
}
|
||||
|
||||
const agent = new Agent({ features });
|
||||
setSurroundingAgent(agent);
|
||||
|
||||
const { realm, resolverCache } = createRealm({ printCompatMode: true });
|
||||
realm.scope(() => {
|
||||
const console = OrdinaryObjectCreate(realm.Intrinsics['%Object.prototype%']);
|
||||
CreateDataProperty(realm.GlobalObject, new Value('console'), console);
|
||||
|
||||
const format = (args) => args.map((a, i) => {
|
||||
if (i === 0 && Type(a) === 'String') {
|
||||
return a.stringValue();
|
||||
}
|
||||
return inspect(a);
|
||||
}).join(' ');
|
||||
|
||||
const log = new Value((args) => {
|
||||
process.stdout.write(`${format(args)}\n`);
|
||||
return Value.undefined;
|
||||
});
|
||||
|
||||
CreateDataProperty(console, new Value('log'), log);
|
||||
|
||||
const error = new Value((args) => {
|
||||
process.stderr.write(`${format(args)}\n`);
|
||||
return Value.undefined;
|
||||
});
|
||||
|
||||
CreateDataProperty(console, new Value('error'), error);
|
||||
|
||||
const debug = new Value((args) => {
|
||||
process.stderr.write(`${util.format(...args)}\n`);
|
||||
return Value.undefined;
|
||||
});
|
||||
|
||||
CreateDataProperty(console, new Value('debug'), debug);
|
||||
});
|
||||
|
||||
if (argv.inspector) {
|
||||
const inspector = require('../inspector');
|
||||
inspector.attachRealm(realm);
|
||||
}
|
||||
|
||||
function oneShotEval(source, filename) {
|
||||
realm.scope(() => {
|
||||
let result;
|
||||
if (argv.m || argv.module || filename.endsWith('.mjs')) {
|
||||
result = realm.createSourceTextModule(filename, source);
|
||||
if (!(result instanceof AbruptCompletion)) {
|
||||
const module = result;
|
||||
resolverCache.set(filename, result);
|
||||
result = module.Link();
|
||||
if (!(result instanceof AbruptCompletion)) {
|
||||
result = module.Evaluate();
|
||||
}
|
||||
if (!(result instanceof AbruptCompletion)) {
|
||||
if (result.PromiseState === 'rejected') {
|
||||
result = Throw(result.PromiseResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result = realm.evaluateScript(source, { specifier: filename });
|
||||
}
|
||||
if (result instanceof AbruptCompletion) {
|
||||
const inspected = inspect(result);
|
||||
process.stderr.write(`${inspected}\n`);
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (entry) {
|
||||
const source = fs.readFileSync(entry, 'utf8');
|
||||
oneShotEval(source, path.resolve(entry));
|
||||
} else if (!process.stdin.isTTY) {
|
||||
process.stdin.setEncoding('utf8');
|
||||
let source = '';
|
||||
process.stdin.on('data', (data) => {
|
||||
source += data;
|
||||
});
|
||||
process.stdin.once('end', () => {
|
||||
oneShotEval(source, process.cwd());
|
||||
});
|
||||
} else {
|
||||
process.stdout.write(`${packageJson.name} v${packageJson.version}
|
||||
Please report bugs to ${packageJson.bugs.url}
|
||||
`);
|
||||
repl.start({
|
||||
prompt: '> ',
|
||||
eval: (cmd, context, filename, callback) => {
|
||||
try {
|
||||
const result = realm.evaluateScript(cmd, { specifier: '(engine262)' });
|
||||
callback(null, result);
|
||||
} catch (e) {
|
||||
callback(e, null);
|
||||
}
|
||||
},
|
||||
preview: false,
|
||||
completer: () => [],
|
||||
writer: (o) => realm.scope(() => {
|
||||
if (o instanceof Value || o instanceof Completion) {
|
||||
return inspect(o);
|
||||
}
|
||||
return util.inspect(o);
|
||||
}),
|
||||
});
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
function snekparse(args) {
|
||||
if (typeof args === 'string') {
|
||||
args = args.split(' ');
|
||||
}
|
||||
const argv = [];
|
||||
for (let i = 0; i < args.length; i += 1) {
|
||||
const arg = args[i];
|
||||
if (/^--.+=/.test(arg)) {
|
||||
const match = arg.match(/^--([^=]+)=([\s\S]*)$/);
|
||||
argv[match[1]] = match[2];
|
||||
} else if (/^--no-.+/.test(arg)) {
|
||||
argv[arg.match(/^--no-(.+)/)[1]] = false;
|
||||
} else if (/^--.+/.test(arg)) {
|
||||
const key = arg.match(/^--(.+)/)[1];
|
||||
const next = args[i + 1];
|
||||
if (typeof next !== 'undefined' && !/^-/.test(next)) {
|
||||
argv[key] = next;
|
||||
} else if (/^(true|false)$/.test(next)) {
|
||||
argv[key] = next === 'true';
|
||||
} else {
|
||||
argv[key] = true;
|
||||
}
|
||||
} else if (/^-[^-]+/.test(arg)) {
|
||||
const letters = arg.slice(1, -1).split('');
|
||||
let broken = false;
|
||||
for (const j of letters) {
|
||||
const next = arg.slice(j + 2);
|
||||
|
||||
if (next === '-') {
|
||||
argv[letters[j]] = next;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (/[A-Za-z]/.test(letters[j]) && /=/.test(next)) {
|
||||
argv[letters[j]] = next.split('=')[1];
|
||||
broken = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (/[A-Za-z]/.test(letters[j])
|
||||
&& /-?\d+(\.\d*)?(e-?\d+)?$/.test(next)) {
|
||||
argv[letters[j]] = next;
|
||||
broken = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (letters[j + 1] && letters[j + 1].match(/\W/)) {
|
||||
argv[letters[j]] = arg.slice(j + 2);
|
||||
broken = true;
|
||||
break;
|
||||
} else {
|
||||
argv[letters[j]] = true;
|
||||
}
|
||||
}
|
||||
|
||||
const key = arg.slice(-1)[0];
|
||||
if (!broken && key !== '-') {
|
||||
if (args[i + 1] && !/^(-|--)[^-]/.test(args[i + 1])) {
|
||||
argv[key] = args[i + 1];
|
||||
} else if (args[i + 1] && /true|false/.test(args[i + 1])) {
|
||||
argv[key] = args[i + 1] === 'true';
|
||||
} else {
|
||||
argv[key] = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
argv.push(arg);
|
||||
}
|
||||
}
|
||||
|
||||
return argv;
|
||||
}
|
||||
|
||||
module.exports = snekparse;
|
||||
@@ -1,129 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const {
|
||||
Value,
|
||||
CreateDataProperty,
|
||||
DetachArrayBuffer,
|
||||
OrdinaryObjectCreate,
|
||||
ToString,
|
||||
Type,
|
||||
Throw,
|
||||
AbruptCompletion,
|
||||
ManagedRealm,
|
||||
inspect,
|
||||
gc,
|
||||
} = require('..');
|
||||
|
||||
const createRealm = ({ printCompatMode = false } = {}) => {
|
||||
const resolverCache = new Map();
|
||||
const trackedPromises = new Set();
|
||||
|
||||
const realm = new ManagedRealm({
|
||||
promiseRejectionTracker(promise, operation) {
|
||||
switch (operation) {
|
||||
case 'reject':
|
||||
trackedPromises.add(promise);
|
||||
break;
|
||||
case 'handle':
|
||||
trackedPromises.delete(promise);
|
||||
break;
|
||||
/* istanbul ignore next */
|
||||
default:
|
||||
throw new RangeError('promiseRejectionTracker', operation);
|
||||
}
|
||||
},
|
||||
resolveImportedModule(referencingScriptOrModule, specifier) {
|
||||
try {
|
||||
const base = path.dirname(referencingScriptOrModule.HostDefined.specifier);
|
||||
const resolved = path.resolve(base, specifier);
|
||||
if (resolverCache.has(resolved)) {
|
||||
return resolverCache.get(resolved);
|
||||
}
|
||||
const source = fs.readFileSync(resolved, 'utf8');
|
||||
const m = realm.createSourceTextModule(resolved, source);
|
||||
resolverCache.set(resolved, m);
|
||||
return m;
|
||||
} catch (e) {
|
||||
return Throw(e.name, 'Raw', e.message);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
return realm.scope(() => {
|
||||
const $262 = OrdinaryObjectCreate(realm.Intrinsics['%Object.prototype%']);
|
||||
|
||||
let printHandle;
|
||||
const setPrintHandle = (f) => {
|
||||
printHandle = f;
|
||||
};
|
||||
CreateDataProperty(realm.GlobalObject, new Value('print'), new Value((args) => {
|
||||
/* istanbul ignore next */
|
||||
if (printHandle !== undefined) {
|
||||
printHandle(...args);
|
||||
} else {
|
||||
if (printCompatMode) {
|
||||
for (let i = 0; i < args.length; i += 1) {
|
||||
const arg = args[i];
|
||||
const s = ToString(arg);
|
||||
if (s instanceof AbruptCompletion) {
|
||||
return s;
|
||||
}
|
||||
process.stdout.write(s.stringValue());
|
||||
if (i !== args.length - 1) {
|
||||
process.stdout.write(' ');
|
||||
}
|
||||
}
|
||||
process.stdout.write('\n');
|
||||
return Value.undefined;
|
||||
} else {
|
||||
const formatted = args.map((a, i) => {
|
||||
if (i === 0 && Type(a) === 'String') {
|
||||
return a.stringValue();
|
||||
}
|
||||
return inspect(a, realm);
|
||||
}).join(' ');
|
||||
console.log(formatted); // eslint-disable-line no-console
|
||||
}
|
||||
}
|
||||
return Value.undefined;
|
||||
}));
|
||||
|
||||
[
|
||||
['global', realm.GlobalObject],
|
||||
['createRealm', () => {
|
||||
const info = createRealm();
|
||||
return info.$262;
|
||||
}],
|
||||
['evalScript', ([sourceText]) => realm.evaluateScript(sourceText.stringValue())],
|
||||
['detachArrayBuffer', ([arrayBuffer]) => DetachArrayBuffer(arrayBuffer)],
|
||||
['gc', () => {
|
||||
gc();
|
||||
return Value.undefined;
|
||||
}],
|
||||
['spec', ([v]) => {
|
||||
if (v.nativeFunction && v.nativeFunction.section) {
|
||||
return new Value(v.nativeFunction.section);
|
||||
}
|
||||
return Value.undefined;
|
||||
}],
|
||||
].forEach(([name, value]) => {
|
||||
const v = value instanceof Value ? value : new Value(value);
|
||||
CreateDataProperty($262, new Value(name), v);
|
||||
});
|
||||
|
||||
CreateDataProperty(realm.GlobalObject, new Value('$262'), $262);
|
||||
CreateDataProperty(realm.GlobalObject, new Value('$'), $262);
|
||||
|
||||
return {
|
||||
realm,
|
||||
$262,
|
||||
resolverCache,
|
||||
trackedPromises,
|
||||
setPrintHandle,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = { createRealm };
|
||||
@@ -1,330 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const engine262 = require('..');
|
||||
|
||||
const contexts = [];
|
||||
|
||||
class InspectorContext {
|
||||
constructor(realm) {
|
||||
this.realm = realm;
|
||||
this.idToObject = new Map();
|
||||
this.objectToId = new Map();
|
||||
this.objectCounter = 0;
|
||||
this.previewStack = [];
|
||||
}
|
||||
|
||||
internObject(object, group = 'default') {
|
||||
if (this.objectToId.has(object)) {
|
||||
return this.objectToId.get(object);
|
||||
}
|
||||
const id = `${group}:${this.objectCounter}`;
|
||||
this.objectCounter += 1;
|
||||
this.idToObject.set(id, object);
|
||||
this.objectToId.set(object, id);
|
||||
return id;
|
||||
}
|
||||
|
||||
releaseObjectGroup(group) {
|
||||
for (const [id, object] of this.idToObject.entries()) {
|
||||
if (id.startsWith(group)) {
|
||||
this.idToObject.delete(id);
|
||||
this.objectToId.delete(object);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getObject(objectId) {
|
||||
return this.idToObject.get(objectId);
|
||||
}
|
||||
|
||||
toRemoteObject(object, options) {
|
||||
const result = {};
|
||||
switch (engine262.Type(object)) {
|
||||
case 'Object':
|
||||
result.objectId = this.internObject(object, options.objectGroup);
|
||||
if ('Call' in object) {
|
||||
result.type = 'function';
|
||||
} else {
|
||||
result.type = 'object';
|
||||
if ('PromiseState' in object) {
|
||||
result.subtype = 'promise';
|
||||
} else if ('MapData' in object) {
|
||||
result.subtype = 'map';
|
||||
} else if ('SetData' in object) {
|
||||
result.subtype = 'set';
|
||||
} else if ('ErrorData' in object) {
|
||||
result.subtype = 'error';
|
||||
} else if ('TypedArrayName' in object) {
|
||||
result.subtype = 'typedarray';
|
||||
} else if ('DataView' in object) {
|
||||
result.subtype = 'dataview';
|
||||
} else if ('ProxyTarget' in object) {
|
||||
result.subtype = 'proxy';
|
||||
} else if ('DateValue' in object) {
|
||||
result.subtype = 'date';
|
||||
} else if ('GeneratorState' in object) {
|
||||
result.subtype = 'generator';
|
||||
} else if (engine262.IsArray(object) === engine262.Value.true) {
|
||||
result.subtype = 'array';
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'Null':
|
||||
result.type = 'object';
|
||||
result.subtype = 'null';
|
||||
result.value = null;
|
||||
break;
|
||||
case 'Undefined':
|
||||
result.type = 'undefined';
|
||||
break;
|
||||
case 'String':
|
||||
result.type = 'string';
|
||||
result.value = object.stringValue();
|
||||
break;
|
||||
case 'Number': {
|
||||
result.type = 'number';
|
||||
const v = object.numberValue();
|
||||
if (!Number.isFinite(v)) {
|
||||
result.unserializableValue = v.toString();
|
||||
} else {
|
||||
result.value = v;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'Boolean':
|
||||
result.type = 'boolean';
|
||||
result.value = object.booleanValue();
|
||||
break;
|
||||
case 'BigInt':
|
||||
result.type = 'bigint';
|
||||
result.unserializableValue = `${object.bigintValue().toString()}n`;
|
||||
break;
|
||||
case 'Symbol':
|
||||
result.type = 'symbol';
|
||||
result.description = object.Description === engine262.Value.undefined
|
||||
? undefined
|
||||
: object.Description.stringValue();
|
||||
break;
|
||||
default:
|
||||
throw new RangeError();
|
||||
}
|
||||
if (options.generatePreview
|
||||
&& result.type === 'object'
|
||||
&& result.subtype !== 'null'
|
||||
&& !this.previewStack.includes(object)) {
|
||||
this.previewStack.push(object);
|
||||
const properties = this.getPropertyPreview(object, options);
|
||||
let entries;
|
||||
if ('MapData' in object) {
|
||||
entries = object.MapData.map((d) => ({
|
||||
key: this.toRemoteObject(d.Key, options).preview,
|
||||
value: this.toRemoteObject(d.Value, options).preview,
|
||||
}));
|
||||
}
|
||||
this.previewStack.pop();
|
||||
result.preview = {
|
||||
type: result.type,
|
||||
subtype: result.subtype,
|
||||
overflow: properties.length > 5,
|
||||
properties: properties.slice(0, 5),
|
||||
entries,
|
||||
};
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
getProperties(object, options) {
|
||||
const wrap = (v) => this.toRemoteObject(v, options);
|
||||
|
||||
const properties = [];
|
||||
const internalProperties = [];
|
||||
|
||||
let p = object;
|
||||
while (p !== engine262.Value.null) {
|
||||
const keys = p.OwnPropertyKeys();
|
||||
if (keys instanceof engine262.AbruptCompletion) {
|
||||
return keys;
|
||||
}
|
||||
|
||||
for (const key of keys) {
|
||||
const desc = p.GetOwnProperty(key);
|
||||
if (desc instanceof engine262.AbruptCompletion) {
|
||||
return desc;
|
||||
}
|
||||
if (options.accessorPropertiesOnly && desc.Value) {
|
||||
continue;
|
||||
}
|
||||
const descriptor = {
|
||||
name: key.stringValue
|
||||
? key.stringValue()
|
||||
: undefined,
|
||||
value: desc.Value ? wrap(desc.Value) : undefined,
|
||||
writable: desc.Writable === engine262.Value.true,
|
||||
get: desc.Get ? wrap(desc.Get) : undefined,
|
||||
set: desc.Set ? wrap(desc.Set) : undefined,
|
||||
configurable: desc.Configurable === engine262.Value.true,
|
||||
enumerable: desc.Enumerable === engine262.Value.true,
|
||||
wasThrown: false,
|
||||
isOwn: p === object,
|
||||
symbol: key.stringValue ? undefined : wrap(key),
|
||||
};
|
||||
properties.push(descriptor);
|
||||
}
|
||||
|
||||
if (options.ownProperties) {
|
||||
break;
|
||||
}
|
||||
p = p.GetPrototypeOf();
|
||||
if (p instanceof engine262.AbruptCompletion) {
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
if ('PromiseState' in object) {
|
||||
internalProperties.push({
|
||||
name: '[[PromiseState]]',
|
||||
value: {
|
||||
type: 'string',
|
||||
value: object.PromiseState,
|
||||
},
|
||||
});
|
||||
internalProperties.push({
|
||||
name: '[[PromiseResult]]',
|
||||
value: wrap(object.PromiseResult),
|
||||
});
|
||||
}
|
||||
|
||||
return { properties, internalProperties };
|
||||
}
|
||||
|
||||
getPropertyPreview(object, options) {
|
||||
const wrap = (v) => this.toRemoteObject(v, options);
|
||||
|
||||
const keys = object.OwnPropertyKeys();
|
||||
if (keys instanceof engine262.AbruptCompletion) {
|
||||
return keys;
|
||||
}
|
||||
|
||||
const properties = [];
|
||||
for (const key of keys) {
|
||||
const desc = object.GetOwnProperty(key);
|
||||
if (desc instanceof engine262.AbruptCompletion) {
|
||||
return desc;
|
||||
}
|
||||
const descriptor = {
|
||||
name: key.stringValue
|
||||
? key.stringValue()
|
||||
: `Symbol(${key.Description.stringValue ? key.Description.stringValue() : ''})`,
|
||||
};
|
||||
if (desc.Value) {
|
||||
desc.valuePreview = wrap(desc.Value).preview;
|
||||
switch (engine262.Type(desc.Value)) {
|
||||
case 'Object':
|
||||
if ('Call' in desc.Value) {
|
||||
descriptor.type = 'function';
|
||||
} else {
|
||||
descriptor.type = 'object';
|
||||
if ('PromiseState' in desc.Value) {
|
||||
descriptor.subtype = 'promise';
|
||||
} else if ('MapData' in desc.Value) {
|
||||
descriptor.subtype = 'map';
|
||||
} else if ('SetData' in desc.Value) {
|
||||
descriptor.subtype = 'set';
|
||||
} else if ('ErrorData' in desc.Value) {
|
||||
descriptor.subtype = 'error';
|
||||
} else if ('TypedArrayName' in desc.Value) {
|
||||
descriptor.subtype = 'typedarray';
|
||||
} else if ('DataView' in desc.Value) {
|
||||
descriptor.subtype = 'dataview';
|
||||
} else if ('ProxyTarget' in desc.Value) {
|
||||
descriptor.subtype = 'proxy';
|
||||
} else if ('DateValue' in desc.Value) {
|
||||
descriptor.subtype = 'date';
|
||||
} else if ('GeneratorState' in desc.Value) {
|
||||
descriptor.subtype = 'generator';
|
||||
} else if (engine262.IsArray(desc.Value) === engine262.Value.true) {
|
||||
descriptor.subtype = 'array';
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'Null':
|
||||
descriptor.type = 'object';
|
||||
descriptor.subtype = 'null';
|
||||
descriptor.value = 'null';
|
||||
break;
|
||||
case 'Undefined':
|
||||
descriptor.type = 'undefined';
|
||||
descriptor.value = 'undefined';
|
||||
break;
|
||||
case 'String':
|
||||
descriptor.type = 'string';
|
||||
descriptor.value = desc.Value.stringValue();
|
||||
break;
|
||||
case 'Number': {
|
||||
descriptor.type = 'number';
|
||||
descriptor.value = desc.Value.numberValue().toString();
|
||||
break;
|
||||
}
|
||||
case 'Boolean':
|
||||
descriptor.type = 'boolean';
|
||||
descriptor.value = desc.Value.booleanValue().toString();
|
||||
break;
|
||||
case 'BigInt':
|
||||
descriptor.type = 'bigint';
|
||||
descriptor.value = `${desc.Value.bigintValue().toString()}n`;
|
||||
break;
|
||||
case 'Symbol': {
|
||||
descriptor.type = 'symbol';
|
||||
const description = desc.Value.Description === engine262.Value.undefined
|
||||
? ''
|
||||
: desc.Value.Description.stringValue();
|
||||
descriptor.value = `Symbol(${description})`;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new RangeError();
|
||||
}
|
||||
} else {
|
||||
desc.type = 'accessor';
|
||||
}
|
||||
properties.push(descriptor);
|
||||
}
|
||||
|
||||
if ('PromiseState' in object) {
|
||||
properties.push({
|
||||
name: '[[PromiseState]]',
|
||||
type: 'string',
|
||||
value: object.PromiseState,
|
||||
});
|
||||
}
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
createEvaluationResult(completion, options) {
|
||||
if (completion instanceof engine262.AbruptCompletion) {
|
||||
return {
|
||||
exceptionDetails: {
|
||||
text: 'uh oh',
|
||||
lineNumber: 0,
|
||||
columnNumber: 0,
|
||||
exception: this.toRemoteObject(completion.Value, options),
|
||||
},
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
result: this.toRemoteObject(completion.Value, options),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function attachRealm(realm) {
|
||||
contexts.push(new InspectorContext(realm));
|
||||
}
|
||||
|
||||
function getContext(id) {
|
||||
return contexts[id] || contexts[0];
|
||||
}
|
||||
|
||||
module.exports = { attachRealm, getContext };
|
||||
@@ -1,6 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
require('./server');
|
||||
const { attachRealm } = require('./context');
|
||||
|
||||
module.exports = { attachRealm };
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,105 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const engine262 = require('..');
|
||||
const { getContext } = require('./context');
|
||||
|
||||
module.exports = {
|
||||
Debugger: {
|
||||
enable() {
|
||||
return { debuggerId: 'debugger.0' };
|
||||
},
|
||||
setAsyncCallStackDepth() {},
|
||||
setBlackboxPatterns() {},
|
||||
setPauseOnExceptions() {},
|
||||
},
|
||||
Profiler: {
|
||||
enable() {},
|
||||
},
|
||||
Runtime: {
|
||||
enable() {},
|
||||
compileScript() {
|
||||
return { scriptId: 'script.0' };
|
||||
},
|
||||
callFunctionOn(options) {
|
||||
const context = getContext(options.executionContextId);
|
||||
const { Value: F } = context.realm.evaluateScript(`(${options.functionDeclaration})`);
|
||||
const thisValue = options.objectId
|
||||
? context.getObject(options.objectId)
|
||||
: engine262.Value.undefined;
|
||||
const args = options.arguments.map((a) => {
|
||||
if ('value' in a) {
|
||||
return new engine262.Value(context.realm, a.value);
|
||||
}
|
||||
if ('objectId' in a) {
|
||||
return context.getObject(a.objectId);
|
||||
}
|
||||
if ('unserializableValue' in a) {
|
||||
throw new RangeError();
|
||||
}
|
||||
return engine262.Value.undefined;
|
||||
});
|
||||
const r = engine262.Call(F, thisValue, args);
|
||||
return context.createEvaluationResult(r, options);
|
||||
},
|
||||
evaluate(options) {
|
||||
if (options.throwOnSideEffect || options.awaitPromise) {
|
||||
return {
|
||||
exceptionDetails: {
|
||||
text: 'unsupported',
|
||||
lineNumber: 0,
|
||||
columnNumber: 0,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const context = getContext(options.contextId);
|
||||
const r = context.realm.evaluateScript(options.expression);
|
||||
return context.createEvaluationResult(r, options);
|
||||
},
|
||||
getHeapUsage() {
|
||||
return { usedSize: 0, totalSize: 0 };
|
||||
},
|
||||
getIsolateId() {
|
||||
return { id: 'isolate.0' };
|
||||
},
|
||||
getProperties(options) {
|
||||
const context = getContext();
|
||||
const object = context.getObject(options.objectId);
|
||||
|
||||
const properties = context.getProperties(object, options);
|
||||
if (properties instanceof engine262.AbruptCompletion) {
|
||||
return context.createEvaluationResult(properties, options);
|
||||
}
|
||||
|
||||
return {
|
||||
result: properties.properties,
|
||||
internalProperties: properties.internalProperties,
|
||||
};
|
||||
},
|
||||
globalLexicalScopeNames({ executionContextId }) {
|
||||
const context = getContext(executionContextId);
|
||||
const envRec = context.realm.realm.GlobalEnv.EnvironmentRecord;
|
||||
const names = Map.prototype.keys.call(envRec.DeclarativeRecord.bindings);
|
||||
return {
|
||||
names: [...names],
|
||||
};
|
||||
},
|
||||
releaseObjectGroup({ objectGroup }) {
|
||||
getContext().releaseObjectGroup(objectGroup);
|
||||
},
|
||||
runIfWaitingForDebugger(params, ctx) {
|
||||
ctx.sendEvent('Runtime.executionContextCreated', {
|
||||
context: {
|
||||
id: '0',
|
||||
origin: 'file://',
|
||||
name: 'context.0',
|
||||
auxData: {},
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
HeapProfiler: {
|
||||
enable() {},
|
||||
collectGarbage() {},
|
||||
},
|
||||
};
|
||||
@@ -1,87 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const http = require('http');
|
||||
const WebSocket = require('ws'); // eslint-disable-line import/no-extraneous-dependencies
|
||||
const packageJson = require('../package.json');
|
||||
const protocol = require('./js_protocol.json');
|
||||
const methods = require('./methods');
|
||||
|
||||
const server = http.createServer((req, res) => {
|
||||
if (req.method !== 'GET') {
|
||||
res.writeHead(405);
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
|
||||
const json = (obj) => {
|
||||
const s = JSON.stringify(obj);
|
||||
res.writeHead(200, {
|
||||
'Content-Type': 'application/json',
|
||||
'Content-Length': Buffer.byteLength(s),
|
||||
});
|
||||
res.end(s);
|
||||
};
|
||||
|
||||
switch (req.url) {
|
||||
case '/json':
|
||||
case '/json/list':
|
||||
json([{
|
||||
description: `${packageJson.name} instance`,
|
||||
devtoolsFrontendUrl: 'chrome-devtools://devtools/bundled/js_app.html?experiments=true&v8only=true&ws=localhost:9229/',
|
||||
devtoolsFrontendUrlCompat: 'chrome-devtools://devtools/bundled/inspector.html?experiments=true&v8only=true&ws=localhost:9229/',
|
||||
faviconUrl: 'https://avatars0.githubusercontent.com/u/51185628',
|
||||
id: 'inspector.0',
|
||||
title: 'engine262',
|
||||
type: 'node',
|
||||
url: `file://${process.cwd()}`,
|
||||
webSocketDebuggerUrl: 'ws://localhost:9229/',
|
||||
}]);
|
||||
break;
|
||||
case '/json/version':
|
||||
json({
|
||||
'Browser': `${packageJson.name}/v${packageJson.version}`,
|
||||
'Protocol-Version': `${protocol.version.major}.${protocol.version.minor}`,
|
||||
});
|
||||
break;
|
||||
case '/json/protocol':
|
||||
json(protocol);
|
||||
break;
|
||||
default:
|
||||
res.writeHead(404);
|
||||
res.end();
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
const wss = new WebSocket.Server({ server });
|
||||
|
||||
wss.on('connection', (ws) => {
|
||||
const send = (obj) => {
|
||||
const s = JSON.stringify(obj);
|
||||
// console.log('<-', s);
|
||||
ws.send(s);
|
||||
};
|
||||
|
||||
ws._socket.unref();
|
||||
|
||||
const context = {
|
||||
sendEvent(event, params) {
|
||||
send({ method: event, params });
|
||||
},
|
||||
};
|
||||
|
||||
ws.on('message', (data) => {
|
||||
// console.log('->', data);
|
||||
const { id, method, params } = JSON.parse(data);
|
||||
const [k, v] = method.split('.');
|
||||
Promise.resolve(methods[k][v](params, context))
|
||||
.then((result = {}) => {
|
||||
send({ id, result });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
server.listen(9229, '127.0.0.1', () => {
|
||||
console.log('Debugger listening at localhost:9229'); // eslint-disable-line no-console
|
||||
});
|
||||
server.unref();
|
||||
@@ -1,54 +0,0 @@
|
||||
{
|
||||
"name": "engine262",
|
||||
"version": "0.0.1",
|
||||
"description": "Implementation of ECMA-262 in JavaScript",
|
||||
"author": "engine262 Contributors",
|
||||
"license": "MIT",
|
||||
"homepage": "https://github.com/engine262/engine262#readme",
|
||||
"bugs": {
|
||||
"url": "https://github.com/engine262/engine262/issues"
|
||||
},
|
||||
"main": "dist/engine262",
|
||||
"scripts": {
|
||||
"lint": "eslint rollup.config.js test/ src/ bin/ inspector/ scripts/ --cache --ext=js,mjs",
|
||||
"build": "npm run build:engine",
|
||||
"build:engine": "rollup -c",
|
||||
"test": "bash test/test_root.sh",
|
||||
"test:test262": "node test/test262/test262.js",
|
||||
"test:supplemental": "node test/supplemental.js",
|
||||
"test:json": "node test/json/json.js",
|
||||
"coverage": "nyc --reporter=lcov npm run test",
|
||||
"prepublishOnly": "node scripts/tag_version_with_git_hash.js",
|
||||
"postpublish": "git reset --hard HEAD"
|
||||
},
|
||||
"bin": {
|
||||
"engine262": "bin/engine262.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/engine262/engine262.git"
|
||||
},
|
||||
"publishConfig": {
|
||||
"registry": "https://npm.pkg.github.com/@engine262"
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.11.1",
|
||||
"@babel/eslint-parser": "^7.11.3",
|
||||
"@rollup/plugin-babel": "^5.1.0",
|
||||
"@rollup/plugin-commonjs": "^14.0.0",
|
||||
"@rollup/plugin-node-resolve": "^8.4.0",
|
||||
"@snek/source-map-support": "^1.0.4",
|
||||
"acorn": "^7.4.0",
|
||||
"eslint": "^7.2.0",
|
||||
"eslint-config-airbnb-base": "^14.2.0",
|
||||
"eslint-plugin-import": "^2.22.0",
|
||||
"glob": "^7.1.6",
|
||||
"minimatch": "^3.0.4",
|
||||
"nyc": "^15.1.0",
|
||||
"rollup": "^2.23.1",
|
||||
"test262-stream": "^1.3.0",
|
||||
"unicode-13.0.0": "^0.8.0",
|
||||
"ws": "^7.2.3"
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const { execSync } = require('child_process');
|
||||
const { babel } = require('@rollup/plugin-babel');
|
||||
const commonjs = require('@rollup/plugin-commonjs');
|
||||
const { nodeResolve } = require('@rollup/plugin-node-resolve');
|
||||
const { name, version } = require('./package.json');
|
||||
|
||||
const hash = execSync('git rev-parse HEAD', { encoding: 'utf8' }).trim();
|
||||
|
||||
const banner = `/*!
|
||||
* engine262 ${version} ${hash}
|
||||
*
|
||||
* ${fs.readFileSync('./LICENSE', 'utf8').trim().split('\n').join('\n * ')}
|
||||
*/
|
||||
`;
|
||||
|
||||
module.exports = () => ({
|
||||
input: './src/api.mjs',
|
||||
plugins: [
|
||||
commonjs(),
|
||||
nodeResolve(),
|
||||
babel({
|
||||
babelHelpers: 'bundled',
|
||||
exclude: 'node_modules/**',
|
||||
plugins: [
|
||||
'./scripts/transform.js',
|
||||
],
|
||||
}),
|
||||
],
|
||||
output: [
|
||||
{
|
||||
file: 'dist/engine262.js',
|
||||
format: 'umd',
|
||||
sourcemap: true,
|
||||
name,
|
||||
banner,
|
||||
},
|
||||
{
|
||||
file: 'dist/engine262.mjs',
|
||||
format: 'es',
|
||||
sourcemap: true,
|
||||
banner,
|
||||
},
|
||||
],
|
||||
onwarn(warning, warn) {
|
||||
if (warning.code === 'CIRCULAR_DEPENDENCY') {
|
||||
// Squelch.
|
||||
return;
|
||||
}
|
||||
process.exitCode = 1;
|
||||
warn(warning);
|
||||
},
|
||||
});
|
||||
@@ -1,20 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const { execSync } = require('child_process');
|
||||
const fs = require('fs');
|
||||
|
||||
const pjsonPath = require.resolve('../package.json');
|
||||
|
||||
const pjson = JSON.parse(fs.readFileSync(pjsonPath, 'utf8'));
|
||||
|
||||
process.stdout.write('Checking package.json for git revision...\n');
|
||||
|
||||
if (!pjson.version.includes('-')) {
|
||||
process.stdout.write('Inserting git revision into package.json...\n');
|
||||
|
||||
const hash = execSync('git rev-parse HEAD', { encoding: 'utf8' }).trim();
|
||||
pjson.version = `${pjson.version}-${hash}`;
|
||||
fs.writeFileSync(pjsonPath, `${JSON.stringify(pjson, null, 2)}\n`);
|
||||
}
|
||||
|
||||
process.stdout.write('Done!\n');
|
||||
@@ -1,260 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const { relative, resolve } = require('path');
|
||||
|
||||
const COMPLETION_PATH = resolve('./src/completion.mjs');
|
||||
const ABSTRACT_OPS_PATH = resolve('./src/abstract-ops/all.mjs');
|
||||
const VALUE_PATH = resolve('./src/value.mjs');
|
||||
|
||||
function fileToImport(file, refPath) {
|
||||
return relative(file.opts.filename, refPath)
|
||||
.replace(/\\/g, '/') // Support building on Windows
|
||||
.replace('../', './');
|
||||
}
|
||||
|
||||
function findParentStatementPath(path) {
|
||||
while (path && !path.isStatement()) {
|
||||
path = path.parentPath;
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
function getEnclosingConditionalExpression(path) {
|
||||
while (path && !path.isStatement()) {
|
||||
if (path.isConditionalExpression()) {
|
||||
return path;
|
||||
}
|
||||
path = path.parentPath;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
module.exports = ({ types: t, template }) => {
|
||||
function createImportCompletion(file) {
|
||||
const r = fileToImport(file, COMPLETION_PATH);
|
||||
return template.ast(`
|
||||
import { Completion } from "${r}";
|
||||
`);
|
||||
}
|
||||
|
||||
function createImportAbruptCompletion(file) {
|
||||
const r = fileToImport(file, COMPLETION_PATH);
|
||||
return template.ast(`
|
||||
import { AbruptCompletion } from "${r}";
|
||||
`);
|
||||
}
|
||||
|
||||
function createImportAssert(file) {
|
||||
const r = fileToImport(file, ABSTRACT_OPS_PATH);
|
||||
return template.ast(`
|
||||
import { Assert } from "${r}";
|
||||
`);
|
||||
}
|
||||
|
||||
function createImportCall(file) {
|
||||
const r = fileToImport(file, ABSTRACT_OPS_PATH);
|
||||
return template.ast(`
|
||||
import { Call } from "${r}";
|
||||
`);
|
||||
}
|
||||
|
||||
function createImportValue(file) {
|
||||
const r = fileToImport(file, VALUE_PATH);
|
||||
return template.ast(`
|
||||
import { Value } from "${r}";
|
||||
`);
|
||||
}
|
||||
|
||||
function addSectionFromComments(path) {
|
||||
if (path.node.leadingComments) {
|
||||
for (const c of path.node.leadingComments) {
|
||||
const lines = c.value.split('\n');
|
||||
for (const line of lines) {
|
||||
if (/#sec/.test(line)) {
|
||||
const section = line.split(' ').find((l) => l.includes('#sec'));
|
||||
const url = section.includes('https') ? section : `https://tc39.es/ecma262/${section}`;
|
||||
path.insertAfter(template.ast(`${path.node.id ? path.node.id.name : path.node.declarations[0].id.name}.section = '${url}';`));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const MACROS = {
|
||||
Q: {
|
||||
template: template(`
|
||||
let ID = ARGUMENT;
|
||||
/* istanbul ignore if */
|
||||
if (ID instanceof AbruptCompletion) {
|
||||
return ID;
|
||||
}
|
||||
/* istanbul ignore if */
|
||||
if (ID instanceof Completion) {
|
||||
ID = ID.Value;
|
||||
}
|
||||
`, { preserveComments: true }),
|
||||
imports: ['AbruptCompletion', 'Completion'],
|
||||
},
|
||||
X: {
|
||||
template: template(`
|
||||
let ID = ARGUMENT;
|
||||
Assert(!(ID instanceof AbruptCompletion), SOURCE + ' returned an abrupt completion');
|
||||
/* istanbul ignore if */
|
||||
if (ID instanceof Completion) {
|
||||
ID = ID.Value;
|
||||
}
|
||||
`, { preserveComments: true }),
|
||||
imports: ['Assert', 'Completion', 'AbruptCompletion'],
|
||||
},
|
||||
IfAbruptRejectPromise: {
|
||||
template: template(`
|
||||
/* istanbul ignore if */
|
||||
if (ID instanceof AbruptCompletion) {
|
||||
const hygenicTemp2 = Call(CAPABILITY.Reject, Value.undefined, [ID.Value]);
|
||||
if (hygenicTemp2 instanceof AbruptCompletion) {
|
||||
return hygenicTemp2;
|
||||
}
|
||||
return CAPABILITY.Promise;
|
||||
}
|
||||
/* istanbul ignore if */
|
||||
if (ID instanceof Completion) {
|
||||
ID = ID.Value;
|
||||
}
|
||||
`, { preserveComments: true }),
|
||||
imports: ['Call', 'Value', 'AbruptCompletion', 'Completion'],
|
||||
},
|
||||
};
|
||||
MACROS.ReturnIfAbrupt = MACROS.Q;
|
||||
const MACRO_NAMES = Object.keys(MACROS);
|
||||
|
||||
return {
|
||||
visitor: {
|
||||
Program: {
|
||||
enter(path, state) {
|
||||
state.needed = {};
|
||||
},
|
||||
exit(path, state) {
|
||||
if (state.needed.Completion && !state.file.opts.filename.endsWith('completion.mjs')) {
|
||||
path.node.body.unshift(createImportCompletion(state.file));
|
||||
}
|
||||
if (state.needed.AbruptCompletion && !state.file.opts.filename.endsWith('completion.mjs')) {
|
||||
path.node.body.unshift(createImportAbruptCompletion(state.file));
|
||||
}
|
||||
if (state.needed.Assert) {
|
||||
path.node.body.unshift(createImportAssert(state.file));
|
||||
}
|
||||
if (state.needed.Call) {
|
||||
path.node.body.unshift(createImportCall(state.file));
|
||||
}
|
||||
if (state.needed.Value) {
|
||||
path.node.body.unshift(createImportValue(state.file));
|
||||
}
|
||||
},
|
||||
},
|
||||
CallExpression(path, state) {
|
||||
if (!t.isIdentifier(path.node.callee)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const macroName = path.node.callee.name;
|
||||
if (MACRO_NAMES.includes(macroName)) {
|
||||
const enclosingConditional = getEnclosingConditionalExpression(path);
|
||||
if (enclosingConditional !== null) {
|
||||
if (enclosingConditional.parentPath.isVariableDeclarator()) {
|
||||
const declaration = enclosingConditional.parentPath.parentPath;
|
||||
const id = enclosingConditional.parentPath.get('id');
|
||||
declaration.replaceWithMultiple(template.ast(`
|
||||
let ${id};
|
||||
if (${enclosingConditional.get('test')}) {
|
||||
${id} = ${enclosingConditional.get('consequent')}
|
||||
} else {
|
||||
${id} = ${enclosingConditional.get('alternate')}
|
||||
}
|
||||
`));
|
||||
return;
|
||||
} else {
|
||||
throw path.buildCodeFrameError('Macros may not be used within conditional expressions');
|
||||
}
|
||||
}
|
||||
|
||||
const macro = MACROS[macroName];
|
||||
const [argument] = path.node.arguments;
|
||||
|
||||
if (macro === MACROS.Q && (t.isReturnStatement(path.parentPath) || path.parentPath.isArrowFunctionExpression())) {
|
||||
path.replaceWith(path.node.arguments[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (path.parentPath.isArrowFunctionExpression()) {
|
||||
throw path.buildCodeFrameError('Macros may not be the sole expression of an arrow function');
|
||||
}
|
||||
|
||||
const statementPath = findParentStatementPath(path);
|
||||
|
||||
macro.imports.forEach((i) => {
|
||||
state.needed[i] = path.scope.getBinding(i) === undefined;
|
||||
});
|
||||
|
||||
if (macro === MACROS.Q && t.isIdentifier(argument)) {
|
||||
const binding = path.scope.getBinding(argument.name);
|
||||
binding.path.parent.kind = 'let';
|
||||
statementPath.insertBefore(template(`
|
||||
/* istanbul ignore if */
|
||||
if (ID instanceof AbruptCompletion) {
|
||||
return ID;
|
||||
}
|
||||
/* istanbul ignore if */
|
||||
if (ID instanceof Completion) {
|
||||
ID = ID.Value;
|
||||
}
|
||||
`, { preserveComments: true })({ ID: argument }));
|
||||
path.replaceWith(argument);
|
||||
} else {
|
||||
if (macro === MACROS.IfAbruptRejectPromise) {
|
||||
const [, capability] = path.node.arguments;
|
||||
if (!t.isIdentifier(argument)) {
|
||||
throw path.get('arguments.0').buildCodeFrameError('First argument to IfAbruptRejectPromise should be an identifier');
|
||||
}
|
||||
if (!t.isIdentifier(capability)) {
|
||||
throw path.get('arguments.1').buildCodeFrameError('Second argument to IfAbruptRejectPromise should be an identifier');
|
||||
}
|
||||
const binding = path.scope.getBinding(argument.name);
|
||||
binding.path.parent.kind = 'let';
|
||||
statementPath.insertBefore(macro.template({ ID: argument, CAPABILITY: capability }));
|
||||
path.remove();
|
||||
} else {
|
||||
const id = statementPath.scope.generateUidIdentifier();
|
||||
const replacement = {
|
||||
ARGUMENT: argument,
|
||||
ID: id,
|
||||
};
|
||||
if (macro === MACROS.X) {
|
||||
replacement.SOURCE = t.stringLiteral(path.get('arguments.0').getSource());
|
||||
}
|
||||
statementPath.insertBefore(macro.template(replacement));
|
||||
path.replaceWith(id);
|
||||
}
|
||||
}
|
||||
} else if (macroName === 'Assert') {
|
||||
path.node.arguments.push(t.stringLiteral(path.get('arguments.0').getSource()));
|
||||
}
|
||||
},
|
||||
SwitchCase(path) {
|
||||
const n = path.node.consequent[0];
|
||||
if (t.isThrowStatement(n) && t.isNewExpression(n.argument) && n.argument.callee.name === 'OutOfRange') {
|
||||
path.node.leadingComments = path.node.leadingComments || [];
|
||||
path.node.leadingComments.push({ type: 'CommentBlock', value: 'istanbul ignore next' });
|
||||
}
|
||||
},
|
||||
FunctionDeclaration(path) {
|
||||
addSectionFromComments(path);
|
||||
},
|
||||
VariableDeclaration(path) {
|
||||
if (path.get('declarations.0.init').isArrowFunctionExpression()) {
|
||||
addSectionFromComments(path);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -1,32 +0,0 @@
|
||||
export * from './arguments-operations.mjs';
|
||||
export * from './array-objects.mjs';
|
||||
export * from './arraybuffer-objects.mjs';
|
||||
export * from './async-function-operations.mjs';
|
||||
export * from './async-generator-objects.mjs';
|
||||
export * from './data-types-and-values.mjs';
|
||||
export * from './dataview-objects.mjs';
|
||||
export * from './date-objects.mjs';
|
||||
export * from './execution-contexts.mjs';
|
||||
export * from './function-operations.mjs';
|
||||
export * from './generator-operations.mjs';
|
||||
export * from './global-object.mjs';
|
||||
export * from './immutable-prototype-objects.mjs';
|
||||
export * from './integer-indexed-objects.mjs';
|
||||
export * from './iterator-operations.mjs';
|
||||
export * from './module-namespace-exotic-objects.mjs';
|
||||
export * from './module-records.mjs';
|
||||
export * from './notational-conventions.mjs';
|
||||
export * from './object-operations.mjs';
|
||||
export * from './objects.mjs';
|
||||
export * from './promise-operations.mjs';
|
||||
export * from './proxy-objects.mjs';
|
||||
export * from './realms.mjs';
|
||||
export * from './reference-operations.mjs';
|
||||
export * from './regexp-objects.mjs';
|
||||
export * from './spec-types.mjs';
|
||||
export * from './string-objects.mjs';
|
||||
export * from './symbol-objects.mjs';
|
||||
export * from './testing-comparison.mjs';
|
||||
export * from './type-conversion.mjs';
|
||||
export * from './typedarray-objects.mjs';
|
||||
export * from './weak-operations.mjs';
|
||||
@@ -1,248 +0,0 @@
|
||||
import { surroundingAgent } from '../engine.mjs';
|
||||
import {
|
||||
Descriptor,
|
||||
Value,
|
||||
wellKnownSymbols,
|
||||
} from '../value.mjs';
|
||||
import { BoundNames } from '../static-semantics/all.mjs';
|
||||
import { Q, X } from '../completion.mjs';
|
||||
import { ValueSet } from '../helpers.mjs';
|
||||
import {
|
||||
Assert,
|
||||
CreateBuiltinFunction,
|
||||
CreateDataProperty,
|
||||
DefinePropertyOrThrow,
|
||||
SetFunctionLength,
|
||||
ToString,
|
||||
SameValue,
|
||||
MakeBasicObject,
|
||||
OrdinaryObjectCreate,
|
||||
OrdinaryGetOwnProperty,
|
||||
OrdinaryDefineOwnProperty,
|
||||
OrdinaryGet,
|
||||
OrdinarySet,
|
||||
OrdinaryDelete,
|
||||
Get,
|
||||
Set,
|
||||
HasOwnProperty,
|
||||
IsAccessorDescriptor,
|
||||
IsDataDescriptor,
|
||||
} from './all.mjs';
|
||||
|
||||
// This file covers abstract operations defined in
|
||||
// 9.4.4 #sec-arguments-exotic-objects
|
||||
|
||||
|
||||
function ArgumentsGetOwnProperty(P) {
|
||||
const args = this;
|
||||
const desc = OrdinaryGetOwnProperty(args, P);
|
||||
if (desc === Value.undefined) {
|
||||
return desc;
|
||||
}
|
||||
const map = args.ParameterMap;
|
||||
const isMapped = X(HasOwnProperty(map, P));
|
||||
if (isMapped === Value.true) {
|
||||
desc.Value = Get(map, P);
|
||||
}
|
||||
return desc;
|
||||
}
|
||||
|
||||
function ArgumentsDefineOwnProperty(P, Desc) {
|
||||
const args = this;
|
||||
const map = args.ParameterMap;
|
||||
const isMapped = X(HasOwnProperty(map, P));
|
||||
let newArgDesc = Desc;
|
||||
if (isMapped === Value.true && IsDataDescriptor(Desc) === true) {
|
||||
if (Desc.Value === undefined && Desc.Writable !== undefined && Desc.Writable === Value.false) {
|
||||
newArgDesc = Descriptor({ ...Desc });
|
||||
newArgDesc.Value = X(Get(map, P));
|
||||
}
|
||||
}
|
||||
const allowed = Q(OrdinaryDefineOwnProperty(args, P, newArgDesc));
|
||||
if (allowed === Value.false) {
|
||||
return Value.false;
|
||||
}
|
||||
if (isMapped === Value.true) {
|
||||
if (IsAccessorDescriptor(Desc) === true) {
|
||||
map.Delete(P);
|
||||
} else {
|
||||
if (Desc.Value !== undefined) {
|
||||
const setStatus = Set(map, P, Desc.Value, Value.false);
|
||||
Assert(setStatus === Value.true);
|
||||
}
|
||||
if (Desc.Writable !== undefined && Desc.Writable === Value.false) {
|
||||
map.Delete(P);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Value.true;
|
||||
}
|
||||
|
||||
function ArgumentsGet(P, Receiver) {
|
||||
const args = this;
|
||||
const map = args.ParameterMap;
|
||||
const isMapped = X(HasOwnProperty(map, P));
|
||||
if (isMapped === Value.false) {
|
||||
return Q(OrdinaryGet(args, P, Receiver));
|
||||
} else {
|
||||
return Get(map, P);
|
||||
}
|
||||
}
|
||||
|
||||
function ArgumentsSet(P, V, Receiver) {
|
||||
const args = this;
|
||||
let isMapped;
|
||||
let map;
|
||||
if (SameValue(args, Receiver) === Value.false) {
|
||||
isMapped = false;
|
||||
} else {
|
||||
map = args.ParameterMap;
|
||||
isMapped = X(HasOwnProperty(map, P)) === Value.true;
|
||||
}
|
||||
if (isMapped) {
|
||||
const setStatus = Set(map, P, V, Value.false);
|
||||
Assert(setStatus === Value.true);
|
||||
}
|
||||
return Q(OrdinarySet(args, P, V, Receiver));
|
||||
}
|
||||
|
||||
function ArgumentsDelete(P) {
|
||||
const args = this;
|
||||
const map = args.ParameterMap;
|
||||
const isMapped = X(HasOwnProperty(map, P));
|
||||
const result = Q(OrdinaryDelete(args, P));
|
||||
if (result === Value.true && isMapped === Value.true) {
|
||||
map.Delete(P);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 9.4.4.6 #sec-createunmappedargumentsobject
|
||||
export function CreateUnmappedArgumentsObject(argumentsList) {
|
||||
const len = argumentsList.length;
|
||||
const obj = OrdinaryObjectCreate(surroundingAgent.intrinsic('%Object.prototype%'), ['ParameterMap']);
|
||||
obj.ParameterMap = Value.undefined;
|
||||
DefinePropertyOrThrow(obj, new Value('length'), Descriptor({
|
||||
Value: new Value(len),
|
||||
Writable: Value.true,
|
||||
Enumerable: Value.false,
|
||||
Configurable: Value.true,
|
||||
}));
|
||||
let index = 0;
|
||||
while (index < len) {
|
||||
const val = argumentsList[index];
|
||||
const idxStr = X(ToString(new Value(index)));
|
||||
X(CreateDataProperty(obj, idxStr, val));
|
||||
index += 1;
|
||||
}
|
||||
X(DefinePropertyOrThrow(obj, wellKnownSymbols.iterator, Descriptor({
|
||||
Value: surroundingAgent.intrinsic('%Array.prototype.values%'),
|
||||
Writable: Value.true,
|
||||
Enumerable: Value.false,
|
||||
Configurable: Value.true,
|
||||
})));
|
||||
X(DefinePropertyOrThrow(obj, new Value('callee'), Descriptor({
|
||||
Get: surroundingAgent.intrinsic('%ThrowTypeError%'),
|
||||
Set: surroundingAgent.intrinsic('%ThrowTypeError%'),
|
||||
Enumerable: Value.false,
|
||||
Configurable: Value.false,
|
||||
})));
|
||||
return obj;
|
||||
}
|
||||
|
||||
function ArgGetterSteps() {
|
||||
const f = this;
|
||||
const name = f.Name;
|
||||
const env = f.Env;
|
||||
return env.GetBindingValue(name, Value.false);
|
||||
}
|
||||
|
||||
// 9.4.4.7.1 #sec-makearggetter
|
||||
function MakeArgGetter(name, env) {
|
||||
const steps = ArgGetterSteps;
|
||||
const getter = X(CreateBuiltinFunction(steps, ['Name', 'Env']));
|
||||
getter.Name = name;
|
||||
getter.Env = env;
|
||||
return getter;
|
||||
}
|
||||
|
||||
function ArgSetterSteps([value]) {
|
||||
Assert(value !== undefined);
|
||||
const f = this;
|
||||
const name = f.Name;
|
||||
const env = f.Env;
|
||||
return env.SetMutableBinding(name, value, Value.false);
|
||||
}
|
||||
|
||||
// 9.4.4.7.2 #sec-makeargsetter
|
||||
function MakeArgSetter(name, env) {
|
||||
const steps = ArgSetterSteps;
|
||||
const setter = X(CreateBuiltinFunction(steps, ['Name', 'Env']));
|
||||
SetFunctionLength(setter, new Value(1));
|
||||
setter.Name = name;
|
||||
setter.Env = env;
|
||||
return setter;
|
||||
}
|
||||
|
||||
// 9.4.4.7 #sec-createmappedargumentsobject
|
||||
export function CreateMappedArgumentsObject(func, formals, argumentsList, env) {
|
||||
// Assert: formals does not contain a rest parameter, any binding
|
||||
// patterns, or any initializers. It may contain duplicate identifiers.
|
||||
const len = argumentsList.length;
|
||||
const obj = X(MakeBasicObject(['Prototype', 'Extensible', 'ParameterMap']));
|
||||
obj.GetOwnProperty = ArgumentsGetOwnProperty;
|
||||
obj.DefineOwnProperty = ArgumentsDefineOwnProperty;
|
||||
obj.Get = ArgumentsGet;
|
||||
obj.Set = ArgumentsSet;
|
||||
obj.Delete = ArgumentsDelete;
|
||||
obj.Prototype = surroundingAgent.intrinsic('%Object.prototype%');
|
||||
const map = OrdinaryObjectCreate(Value.null);
|
||||
obj.ParameterMap = map;
|
||||
const parameterNames = BoundNames(formals);
|
||||
const numberOfParameters = parameterNames.length;
|
||||
let index = 0;
|
||||
while (index < len) {
|
||||
const val = argumentsList[index];
|
||||
const idxStr = X(ToString(new Value(index)));
|
||||
X(CreateDataProperty(obj, idxStr, val));
|
||||
index += 1;
|
||||
}
|
||||
X(DefinePropertyOrThrow(obj, new Value('length'), Descriptor({
|
||||
Value: new Value(len),
|
||||
Writable: Value.true,
|
||||
Enumerable: Value.false,
|
||||
Configurable: Value.true,
|
||||
})));
|
||||
const mappedNames = new ValueSet();
|
||||
index = numberOfParameters - 1;
|
||||
while (index >= 0) {
|
||||
const name = parameterNames[index];
|
||||
if (!mappedNames.has(name)) {
|
||||
mappedNames.add(name);
|
||||
if (index < len) {
|
||||
const g = MakeArgGetter(name, env);
|
||||
const p = MakeArgSetter(name, env);
|
||||
X(map.DefineOwnProperty(X(ToString(new Value(index))), Descriptor({
|
||||
Set: p,
|
||||
Get: g,
|
||||
Enumerable: Value.false,
|
||||
Configurable: Value.true,
|
||||
})));
|
||||
}
|
||||
}
|
||||
index -= 1;
|
||||
}
|
||||
X(DefinePropertyOrThrow(obj, wellKnownSymbols.iterator, Descriptor({
|
||||
Value: surroundingAgent.intrinsic('%Array.prototype.values%'),
|
||||
Writable: Value.true,
|
||||
Enumerable: Value.false,
|
||||
Configurable: Value.true,
|
||||
})));
|
||||
X(DefinePropertyOrThrow(obj, new Value('callee'), Descriptor({
|
||||
Value: func,
|
||||
Writable: Value.true,
|
||||
Enumerable: Value.false,
|
||||
Configurable: Value.true,
|
||||
})));
|
||||
return obj;
|
||||
}
|
||||
@@ -1,250 +0,0 @@
|
||||
import { surroundingAgent } from '../engine.mjs';
|
||||
import {
|
||||
Descriptor,
|
||||
Type,
|
||||
Value,
|
||||
wellKnownSymbols,
|
||||
} from '../value.mjs';
|
||||
import { Q, X } from '../completion.mjs';
|
||||
import {
|
||||
AbstractRelationalComparison,
|
||||
Assert,
|
||||
Call,
|
||||
Construct,
|
||||
Get,
|
||||
GetFunctionRealm,
|
||||
IsDataDescriptor,
|
||||
IsArray,
|
||||
IsConstructor,
|
||||
OrdinaryDefineOwnProperty,
|
||||
OrdinaryGetOwnProperty,
|
||||
OrdinaryObjectCreate,
|
||||
MakeBasicObject,
|
||||
SameValue,
|
||||
ToBoolean,
|
||||
ToNumber,
|
||||
ToString,
|
||||
ToUint32,
|
||||
IsPropertyKey,
|
||||
IsNonNegativeInteger,
|
||||
isArrayIndex,
|
||||
} from './all.mjs';
|
||||
|
||||
// #sec-array-exotic-objects-defineownproperty-p-desc
|
||||
function ArrayDefineOwnProperty(P, Desc) {
|
||||
const A = this;
|
||||
|
||||
Assert(IsPropertyKey(P));
|
||||
if (Type(P) === 'String' && P.stringValue() === 'length') {
|
||||
return Q(ArraySetLength(A, Desc));
|
||||
} else if (isArrayIndex(P)) {
|
||||
const oldLenDesc = OrdinaryGetOwnProperty(A, new Value('length'));
|
||||
Assert(X(IsDataDescriptor(oldLenDesc)));
|
||||
Assert(oldLenDesc.Configurable === Value.false);
|
||||
const oldLen = oldLenDesc.Value;
|
||||
const index = X(ToUint32(P));
|
||||
if (index.numberValue() >= oldLen.numberValue() && oldLenDesc.Writable === Value.false) {
|
||||
return Value.false;
|
||||
}
|
||||
const succeeded = X(OrdinaryDefineOwnProperty(A, P, Desc));
|
||||
if (succeeded === Value.false) {
|
||||
return Value.false;
|
||||
}
|
||||
if (index.numberValue() >= oldLen.numberValue()) {
|
||||
oldLenDesc.Value = new Value(index.numberValue() + 1);
|
||||
const succeeded = OrdinaryDefineOwnProperty(A, new Value('length'), oldLenDesc); // eslint-disable-line no-shadow
|
||||
Assert(succeeded === Value.true);
|
||||
}
|
||||
return Value.true;
|
||||
}
|
||||
return OrdinaryDefineOwnProperty(A, P, Desc);
|
||||
}
|
||||
|
||||
export function isArrayExoticObject(O) {
|
||||
return O.DefineOwnProperty === ArrayDefineOwnProperty;
|
||||
}
|
||||
|
||||
// 9.4.2.2 #sec-arraycreate
|
||||
export function ArrayCreate(length, proto) {
|
||||
Assert(X(IsNonNegativeInteger(length)) === Value.true);
|
||||
if (Object.is(length.numberValue(), -0)) {
|
||||
length = new Value(0);
|
||||
}
|
||||
if (length.numberValue() > (2 ** 32) - 1) {
|
||||
return surroundingAgent.Throw('RangeError', 'InvalidArrayLength', length);
|
||||
}
|
||||
if (proto === undefined) {
|
||||
proto = surroundingAgent.intrinsic('%Array.prototype%');
|
||||
}
|
||||
const A = X(MakeBasicObject(['Prototype', 'Extensible']));
|
||||
A.Prototype = proto;
|
||||
A.DefineOwnProperty = ArrayDefineOwnProperty;
|
||||
|
||||
X(OrdinaryDefineOwnProperty(A, new Value('length'), Descriptor({
|
||||
Value: length,
|
||||
Writable: Value.true,
|
||||
Enumerable: Value.false,
|
||||
Configurable: Value.false,
|
||||
})));
|
||||
|
||||
return A;
|
||||
}
|
||||
|
||||
// 9.4.2.3 #sec-arrayspeciescreate
|
||||
export function ArraySpeciesCreate(originalArray, length) {
|
||||
Assert(Type(length) === 'Number' && Number.isInteger(length.numberValue()) && length.numberValue() >= 0);
|
||||
if (Object.is(length.numberValue(), -0)) {
|
||||
length = new Value(+0);
|
||||
}
|
||||
const isArray = Q(IsArray(originalArray));
|
||||
if (isArray === Value.false) {
|
||||
return Q(ArrayCreate(length));
|
||||
}
|
||||
let C = Q(Get(originalArray, new Value('constructor')));
|
||||
if (IsConstructor(C) === Value.true) {
|
||||
const thisRealm = surroundingAgent.currentRealmRecord;
|
||||
const realmC = Q(GetFunctionRealm(C));
|
||||
if (thisRealm !== realmC) {
|
||||
if (SameValue(C, realmC.Intrinsics['%Array%']) === Value.true) {
|
||||
C = Value.undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Type(C) === 'Object') {
|
||||
C = Q(Get(C, wellKnownSymbols.species));
|
||||
if (C === Value.null) {
|
||||
C = Value.undefined;
|
||||
}
|
||||
}
|
||||
if (C === Value.undefined) {
|
||||
return Q(ArrayCreate(length));
|
||||
}
|
||||
if (IsConstructor(C) === Value.false) {
|
||||
return surroundingAgent.Throw('TypeError', 'NotAConstructor', C);
|
||||
}
|
||||
return Q(Construct(C, [length]));
|
||||
}
|
||||
|
||||
// 9.4.2.4 #sec-arraysetlength
|
||||
export function ArraySetLength(A, Desc) {
|
||||
if (Desc.Value === undefined) {
|
||||
return OrdinaryDefineOwnProperty(A, new Value('length'), Desc);
|
||||
}
|
||||
const newLenDesc = Descriptor({ ...Desc });
|
||||
const newLen = Q(ToUint32(Desc.Value)).numberValue();
|
||||
const numberLen = Q(ToNumber(Desc.Value)).numberValue();
|
||||
if (newLen !== numberLen) {
|
||||
return surroundingAgent.Throw('RangeError', 'InvalidArrayLength', Desc.Value);
|
||||
}
|
||||
newLenDesc.Value = new Value(newLen);
|
||||
const oldLenDesc = OrdinaryGetOwnProperty(A, new Value('length'));
|
||||
Assert(X(IsDataDescriptor(oldLenDesc)));
|
||||
Assert(oldLenDesc.Configurable === Value.false);
|
||||
const oldLen = oldLenDesc.Value.numberValue();
|
||||
if (newLen >= oldLen) {
|
||||
return OrdinaryDefineOwnProperty(A, new Value('length'), newLenDesc);
|
||||
}
|
||||
if (oldLenDesc.Writable === Value.false) {
|
||||
return Value.false;
|
||||
}
|
||||
let newWritable;
|
||||
if (newLenDesc.Writable === undefined || newLenDesc.Writable === Value.true) {
|
||||
newWritable = true;
|
||||
} else {
|
||||
newWritable = false;
|
||||
newLenDesc.Writable = Value.true;
|
||||
}
|
||||
const succeeded = X(OrdinaryDefineOwnProperty(A, new Value('length'), newLenDesc));
|
||||
if (succeeded === Value.false) {
|
||||
return Value.false;
|
||||
}
|
||||
const keys = [];
|
||||
A.properties.forEach((value, key) => {
|
||||
if (isArrayIndex(key) && Number(key.stringValue()) >= newLen) {
|
||||
keys.push(key);
|
||||
}
|
||||
});
|
||||
keys.sort((a, b) => Number(b.stringValue()) - Number(a.stringValue()));
|
||||
for (const P of keys) {
|
||||
const deleteSucceeded = X(A.Delete(P));
|
||||
if (deleteSucceeded === Value.false) {
|
||||
newLenDesc.Value = new Value(X(ToUint32(P)).numberValue() + 1);
|
||||
if (newWritable === false) {
|
||||
newLenDesc.Writable = Value.false;
|
||||
}
|
||||
X(OrdinaryDefineOwnProperty(A, new Value('length'), newLenDesc));
|
||||
return Value.false;
|
||||
}
|
||||
}
|
||||
if (newWritable === false) {
|
||||
const s = OrdinaryDefineOwnProperty(A, new Value('length'), Descriptor({ Writable: Value.false }));
|
||||
Assert(s === Value.true);
|
||||
}
|
||||
return Value.true;
|
||||
}
|
||||
|
||||
// 22.1.3.1.1 #sec-isconcatspreadable
|
||||
export function IsConcatSpreadable(O) {
|
||||
if (Type(O) !== 'Object') {
|
||||
return Value.false;
|
||||
}
|
||||
const spreadable = Q(Get(O, wellKnownSymbols.isConcatSpreadable));
|
||||
if (spreadable !== Value.undefined) {
|
||||
return ToBoolean(spreadable);
|
||||
}
|
||||
return Q(IsArray(O));
|
||||
}
|
||||
|
||||
// 22.1.3.27.1 #sec-sortcompare
|
||||
export function SortCompare(x, y, comparefn) {
|
||||
if (x === Value.undefined && y === Value.undefined) {
|
||||
return new Value(+0);
|
||||
}
|
||||
if (x === Value.undefined) {
|
||||
return new Value(1);
|
||||
}
|
||||
if (y === Value.undefined) {
|
||||
return new Value(-1);
|
||||
}
|
||||
if (comparefn !== Value.undefined) {
|
||||
const callRes = Q(Call(comparefn, Value.undefined, [x, y]));
|
||||
const v = Q(ToNumber(callRes));
|
||||
if (v.isNaN()) {
|
||||
return new Value(+0);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
const xString = Q(ToString(x));
|
||||
const yString = Q(ToString(y));
|
||||
const xSmaller = AbstractRelationalComparison(xString, yString);
|
||||
if (xSmaller === Value.true) {
|
||||
return new Value(-1);
|
||||
}
|
||||
const ySmaller = AbstractRelationalComparison(yString, xString);
|
||||
if (ySmaller === Value.true) {
|
||||
return new Value(1);
|
||||
}
|
||||
return new Value(+0);
|
||||
}
|
||||
|
||||
// 22.1.5.1 #sec-createarrayiterator
|
||||
export function CreateArrayIterator(array, kind) {
|
||||
// 1. Assert: Type(array) is Object.
|
||||
Assert(Type(array) === 'Object');
|
||||
// 2. Assert: kind is key+value, key, or value.
|
||||
Assert(kind === 'key+value' || kind === 'key' || kind === 'value');
|
||||
// 3. Let iterator be ObjectCreate(%ArrayIteratorPrototype%, « [[IteratedArrayLike]], [[ArrayLikeNextIndex]], [[ArrayLikeIterationKind]] »).
|
||||
const iterator = OrdinaryObjectCreate(surroundingAgent.intrinsic('%ArrayIterator.prototype%'), [
|
||||
'IteratedArrayLike',
|
||||
'ArrayLikeNextIndex',
|
||||
'ArrayLikeIterationKind',
|
||||
]);
|
||||
// 4. Set iterator.[[IteratedArrayLike]] to array.
|
||||
iterator.IteratedArrayLike = array;
|
||||
// 5. Set iterator.[[ArrayLikeNextIndex]] to 0.
|
||||
iterator.ArrayLikeNextIndex = 0;
|
||||
// 6. Set iterator.[[ArrayLikeIterationKind]] to kind.
|
||||
iterator.ArrayLikeIterationKind = kind;
|
||||
// 7. Return iterator.
|
||||
return iterator;
|
||||
}
|
||||
@@ -1,213 +0,0 @@
|
||||
import { surroundingAgent } from '../engine.mjs';
|
||||
import { Type, Value } from '../value.mjs';
|
||||
import { Q, X, NormalCompletion } from '../completion.mjs';
|
||||
import {
|
||||
Assert, OrdinaryCreateFromConstructor,
|
||||
IsNonNegativeInteger, CreateByteDataBlock,
|
||||
SameValue, IsConstructor, CopyDataBlockBytes,
|
||||
typedArrayInfoByType,
|
||||
} from './all.mjs';
|
||||
|
||||
// #sec-allocatearraybuffer
|
||||
export function AllocateArrayBuffer(constructor, byteLength) {
|
||||
// 1. Let obj be ? OrdinaryCreateFromConstructor(constructor, "%ArrayBuffer.prototype%", « [[ArrayBufferData]], [[ArrayBufferByteLength]], [[ArrayBufferDetachKey]] »).
|
||||
const obj = Q(OrdinaryCreateFromConstructor(constructor, '%ArrayBuffer.prototype%', [
|
||||
'ArrayBufferData', 'ArrayBufferByteLength', 'ArrayBufferDetachKey',
|
||||
]));
|
||||
// 2. Assert: ! IsNonNegativeInteger(byteLength) is true.
|
||||
Assert(X(IsNonNegativeInteger(byteLength)) === Value.true);
|
||||
// 3. Let block be ? CreateByteDataBlock(byteLength).
|
||||
const block = Q(CreateByteDataBlock(byteLength));
|
||||
// 4. Set obj.[[ArrayBufferData]] to block.
|
||||
obj.ArrayBufferData = block;
|
||||
// 5. Set obj.[[ArrayBufferByteLength]] to byteLength.
|
||||
obj.ArrayBufferByteLength = byteLength;
|
||||
// 6. Return obj.
|
||||
return obj;
|
||||
}
|
||||
|
||||
// #sec-isdetachedbuffer
|
||||
export function IsDetachedBuffer(arrayBuffer) {
|
||||
// 1. Assert: Type(arrayBuffer) is Object and it has an [[ArrayBufferData]] internal slot.
|
||||
Assert(Type(arrayBuffer) === 'Object' && 'ArrayBufferData' in arrayBuffer);
|
||||
// 2. If arrayBuffer.[[ArrayBufferData]] is null, return true.
|
||||
if (arrayBuffer.ArrayBufferData === Value.null) {
|
||||
return Value.true;
|
||||
}
|
||||
// 3. Return false.
|
||||
return Value.false;
|
||||
}
|
||||
|
||||
// #sec-detacharraybuffer
|
||||
export function DetachArrayBuffer(arrayBuffer, key) {
|
||||
// 1. Assert: Type(arrayBuffer) is Object and it has [[ArrayBufferData]], [[ArrayBufferByteLength]], and [[ArrayBufferDetachKey]] internal slots.
|
||||
Assert(Type(arrayBuffer) === 'Object'
|
||||
&& 'ArrayBufferData' in arrayBuffer
|
||||
&& 'ArrayBufferByteLength' in arrayBuffer
|
||||
&& 'ArrayBufferDetachKey' in arrayBuffer);
|
||||
// 2. Assert: IsSharedArrayBuffer(arrayBuffer) is false.
|
||||
Assert(IsSharedArrayBuffer(arrayBuffer) === Value.false);
|
||||
// 3. If key is not present, set key to undefined.
|
||||
if (key === undefined) {
|
||||
key = Value.undefined;
|
||||
}
|
||||
// 4. If SameValue(arrayBuffer.[[ArrayBufferDetachKey]], key) is false, throw a TypeError exception.
|
||||
if (SameValue(arrayBuffer.ArrayBufferDetachKey, key) === Value.false) {
|
||||
return surroundingAgent.Throw('TypeError', 'BufferDetachKeyMismatch', key, arrayBuffer);
|
||||
}
|
||||
// 5. Set arrayBuffer.[[ArrayBufferData]] to null.
|
||||
arrayBuffer.ArrayBufferData = Value.null;
|
||||
// 6. Set arrayBuffer.[[ArrayBufferByteLength]] to 0.
|
||||
arrayBuffer.ArrayBufferByteLength = new Value(0);
|
||||
// 7. Return NormalCompletion(null).
|
||||
return NormalCompletion(Value.null);
|
||||
}
|
||||
|
||||
// #sec-issharedarraybuffer
|
||||
export function IsSharedArrayBuffer(_obj) {
|
||||
return Value.false;
|
||||
}
|
||||
|
||||
export function CloneArrayBuffer(srcBuffer, srcByteOffset, srcLength, cloneConstructor) {
|
||||
// 1. Assert: Type(srcBuffer) is Object and it has an [[ArrayBufferData]] internal slot.
|
||||
Assert(Type(srcBuffer) === 'Object' && 'ArrayBufferData' in srcBuffer);
|
||||
// 2. Assert: IsConstructor(cloneConstructor) is true.
|
||||
Assert(IsConstructor(cloneConstructor) === Value.true);
|
||||
// 3. Let targetBuffer be ? AllocateArrayBuffer(cloneConstructor, srcLength).
|
||||
const targetBuffer = Q(AllocateArrayBuffer(cloneConstructor, srcLength));
|
||||
// 4. If IsDetachedBuffer(srcBuffer) is true, throw a TypeError exception.
|
||||
if (IsDetachedBuffer(srcBuffer) === Value.true) {
|
||||
return surroundingAgent.Throw('TypeError', 'ArrayBufferDetached');
|
||||
}
|
||||
// 5. Let srcBlock be srcBuffer.[[ArrayBufferData]].
|
||||
const srcBlock = srcBuffer.ArrayBufferData;
|
||||
// 6. Let targetBlock be targetBuffer.[[ArrayBufferData]].
|
||||
const targetBlock = targetBuffer.ArrayBufferData;
|
||||
// 7. Perform CopyDataBlockBytes(targetBlock, 0, srcBlock, srcByteOffset, srcLength).
|
||||
CopyDataBlockBytes(targetBlock, 0, srcBlock, srcByteOffset.numberValue(), srcLength.numberValue());
|
||||
// 8. Return targetBuffer.
|
||||
return targetBuffer;
|
||||
}
|
||||
|
||||
// #sec-isbigintelementtype
|
||||
export function IsBigIntElementType(type) {
|
||||
// 1. If type is BigUint64 or BigInt64, return true.
|
||||
if (type === 'BigUint64' || type === 'BigInt64') {
|
||||
return Value.true;
|
||||
}
|
||||
// 2. Return false
|
||||
return Value.false;
|
||||
}
|
||||
|
||||
const throwawayBuffer = new ArrayBuffer(8);
|
||||
const throwawayDataView = new DataView(throwawayBuffer);
|
||||
const throwawayArray = new Uint8Array(throwawayBuffer);
|
||||
|
||||
// #sec-rawbytestonumeric
|
||||
export function RawBytesToNumeric(type, rawBytes, isLittleEndian) {
|
||||
// 1. Let elementSize be the Element Size value specified in Table 61 for Element Type type.
|
||||
const elementSize = typedArrayInfoByType[type].ElementSize;
|
||||
Assert(elementSize === rawBytes.length);
|
||||
const dataViewType = type === 'Uint8C' ? 'Uint8' : type;
|
||||
Object.assign(throwawayArray, rawBytes);
|
||||
return new Value(throwawayDataView[`get${dataViewType}`](0, isLittleEndian === Value.true));
|
||||
}
|
||||
|
||||
// #sec-getvaluefrombuffer
|
||||
export function GetValueFromBuffer(arrayBuffer, byteIndex, type, isTypedArray, order, isLittleEndian) {
|
||||
// 1. Assert: IsDetachedBuffer(arrayBuffer) is false.
|
||||
Assert(IsDetachedBuffer(arrayBuffer) === Value.false);
|
||||
// 2. Assert: There are sufficient bytes in arrayBuffer starting at byteIndex to represent a value of type.
|
||||
// 3. Assert: ! IsNonNegativeInteger(byteIndex) is true.
|
||||
Assert(X(IsNonNegativeInteger(byteIndex)) === Value.true);
|
||||
// 4. Let block be arrayBuffer.[[ArrayBufferData]].
|
||||
const block = arrayBuffer.ArrayBufferData;
|
||||
// 5. Let elementSize be the Element Size value specified in Table 61 for Element Type type.
|
||||
const elementSize = typedArrayInfoByType[type].ElementSize;
|
||||
// 6. If IsSharedArrayBuffer(arrayBuffer) is true, then
|
||||
if (IsSharedArrayBuffer(arrayBuffer) === Value.true) {
|
||||
Assert(false);
|
||||
}
|
||||
// 7. Else, let rawValue be a List of elementSize containing, in order, the elementSize sequence of bytes starting with block[byteIndex].
|
||||
const rawValue = [...block.subarray(byteIndex.numberValue(), byteIndex.numberValue() + elementSize)];
|
||||
// 8. If isLittleEndian is not present, set isLittleEndian to the value of the [[LittleEndian]] field of the surrounding agent's Agent Record.
|
||||
if (isLittleEndian === undefined) {
|
||||
isLittleEndian = surroundingAgent.AgentRecord.LittleEndian;
|
||||
}
|
||||
// 9. Return RawBytesToNumeric(type, rawValue, isLittleEndian).
|
||||
return RawBytesToNumeric(type, rawValue, isLittleEndian);
|
||||
}
|
||||
|
||||
const float32NaNLE = Object.freeze([0, 0, 192, 127]);
|
||||
const float32NaNBE = Object.freeze([127, 192, 0, 0]);
|
||||
const float64NaNLE = Object.freeze([0, 0, 0, 0, 0, 0, 248, 127]);
|
||||
const float64NaNBE = Object.freeze([127, 248, 0, 0, 0, 0, 0, 0]);
|
||||
|
||||
// #sec-numerictorawbytes
|
||||
export function NumericToRawBytes(type, value, isLittleEndian) {
|
||||
Assert(Type(isLittleEndian) === 'Boolean');
|
||||
isLittleEndian = isLittleEndian === Value.true;
|
||||
let rawBytes;
|
||||
// One day, we will write our own IEEE 754 and two's complement encoder…
|
||||
if (type === 'Float32') {
|
||||
if (Number.isNaN(value.numberValue())) {
|
||||
rawBytes = isLittleEndian ? [...float32NaNLE] : [...float32NaNBE];
|
||||
} else {
|
||||
throwawayDataView.setFloat32(0, value.numberValue(), isLittleEndian);
|
||||
rawBytes = [...throwawayArray.subarray(0, 4)];
|
||||
}
|
||||
} else if (type === 'Float64') {
|
||||
if (Number.isNaN(value.numberValue())) {
|
||||
rawBytes = isLittleEndian ? [...float64NaNLE] : [...float64NaNBE];
|
||||
} else {
|
||||
throwawayDataView.setFloat64(0, value.numberValue(), isLittleEndian);
|
||||
rawBytes = [...throwawayArray.subarray(0, 8)];
|
||||
}
|
||||
} else {
|
||||
// a. Let n be the Element Size value specified in Table 61 for Element Type type.
|
||||
const n = typedArrayInfoByType[type].ElementSize;
|
||||
// b. Let convOp be the abstract operation named in the Conversion Operation column in Table 61 for Element Type type.
|
||||
const convOp = typedArrayInfoByType[type].ConversionOperation;
|
||||
// c. Let intValue be convOp(value) treated as a mathematical value, whether the result is a BigInt or Number.
|
||||
const intValue = X(convOp(value));
|
||||
const dataViewType = type === 'Uint8C' ? 'Uint8' : type;
|
||||
throwawayDataView[`set${dataViewType}`](0, intValue.bigintValue ? intValue.bigintValue() : intValue.numberValue(), isLittleEndian);
|
||||
rawBytes = [...throwawayArray.subarray(0, n)];
|
||||
}
|
||||
return rawBytes;
|
||||
}
|
||||
|
||||
// #sec-setvalueinbuffer
|
||||
export function SetValueInBuffer(arrayBuffer, byteIndex, type, value, isTypedArray, order, isLittleEndian) {
|
||||
// 1. Assert: IsDetachedBuffer(arrayBuffer) is false.
|
||||
Assert(IsDetachedBuffer(arrayBuffer) === Value.false);
|
||||
// 2. Assert: There are sufficient bytes in arrayBuffer starting at byteIndex to represent a value of type.
|
||||
// 3. Assert: ! IsNonNegativeInteger(byteIndex) is true.
|
||||
Assert(X(IsNonNegativeInteger(byteIndex)) === Value.true);
|
||||
// 4. Assert: Type(value) is BigInt if ! IsBigIntElementType(type) is true; otherwise, Type(value) is Number.
|
||||
if (X(IsBigIntElementType(type)) === Value.true) {
|
||||
Assert(Type(value) === 'BigInt');
|
||||
} else {
|
||||
Assert(Type(value) === 'Number');
|
||||
}
|
||||
// 5. Let block be arrayBuffer.[[ArrayBufferData]].
|
||||
const block = arrayBuffer.ArrayBufferData;
|
||||
// 6. Let elementSize be the Element Size value specified in Table 61 for Element Type type.
|
||||
// const elementSize = typedArrayInfo[type].ElementSize;
|
||||
// 7. If isLittleEndian is not present, set isLittleEndian to the value of the [[LittleEndian]] field of the surrounding agent's Agent Record.
|
||||
if (isLittleEndian === undefined) {
|
||||
isLittleEndian = surroundingAgent.AgentRecord.LittleEndian;
|
||||
}
|
||||
// 8. Let rawBytes be NumericToRawBytes(type, value, isLittleEndian).
|
||||
const rawBytes = NumericToRawBytes(type, value, isLittleEndian);
|
||||
// 9. If IsSharedArrayBuffer(arrayBuffer) is true, then
|
||||
if (IsSharedArrayBuffer(arrayBuffer) === Value.true) {
|
||||
Assert(false);
|
||||
}
|
||||
// 10. Else, store the individual bytes of rawBytes into block, in order, starting at block[byteIndex].
|
||||
rawBytes.forEach((byte, i) => {
|
||||
block[byteIndex.numberValue() + i] = byte;
|
||||
});
|
||||
// 11. Return NormalCompletion(undefined).
|
||||
return NormalCompletion(Value.undefined);
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
import { EnsureCompletion, X } from '../completion.mjs';
|
||||
import { surroundingAgent } from '../engine.mjs';
|
||||
import { Evaluate } from '../evaluator.mjs';
|
||||
import { Value } from '../value.mjs';
|
||||
import { resume } from '../helpers.mjs';
|
||||
import { Assert, Call } from './all.mjs';
|
||||
|
||||
// This file covers abstract operations defined in
|
||||
// 25.7 #sec-async-function-objects
|
||||
|
||||
// https://tc39.es/proposal-top-level-await/#sec-asyncblockstart
|
||||
export function AsyncBlockStart(promiseCapability, asyncBody, asyncContext) {
|
||||
asyncContext.promiseCapability = promiseCapability;
|
||||
|
||||
const runningContext = surroundingAgent.runningExecutionContext;
|
||||
asyncContext.codeEvaluationState = (function* resumer() {
|
||||
const result = EnsureCompletion(yield* Evaluate(asyncBody));
|
||||
// Assert: If we return here, the async function either threw an exception or performed an implicit or explicit return; all awaiting is done.
|
||||
surroundingAgent.executionContextStack.pop(asyncContext);
|
||||
if (result.Type === 'normal') {
|
||||
X(Call(promiseCapability.Resolve, Value.undefined, [Value.undefined]));
|
||||
} else if (result.Type === 'return') {
|
||||
X(Call(promiseCapability.Resolve, Value.undefined, [result.Value]));
|
||||
} else {
|
||||
Assert(result.Type === 'throw');
|
||||
X(Call(promiseCapability.Reject, Value.undefined, [result.Value]));
|
||||
}
|
||||
return Value.undefined;
|
||||
}());
|
||||
surroundingAgent.executionContextStack.push(asyncContext);
|
||||
const result = EnsureCompletion(resume(asyncContext, undefined));
|
||||
Assert(surroundingAgent.runningExecutionContext === runningContext);
|
||||
Assert(result.Type === 'normal' && result.Value === Value.undefined);
|
||||
return Value.undefined;
|
||||
}
|
||||
|
||||
// 25.7.5.1 #sec-async-functions-abstract-operations-async-function-start
|
||||
export function AsyncFunctionStart(promiseCapability, asyncFunctionBody) {
|
||||
const runningContext = surroundingAgent.runningExecutionContext;
|
||||
const asyncContext = runningContext.copy();
|
||||
X(AsyncBlockStart(promiseCapability, asyncFunctionBody, asyncContext));
|
||||
}
|
||||
@@ -1,211 +0,0 @@
|
||||
import { surroundingAgent } from '../engine.mjs';
|
||||
import {
|
||||
Q, X,
|
||||
Await,
|
||||
Completion,
|
||||
EnsureCompletion,
|
||||
NormalCompletion,
|
||||
AbruptCompletion,
|
||||
} from '../completion.mjs';
|
||||
import { Evaluate } from '../evaluator.mjs';
|
||||
import { Value, Type } from '../value.mjs';
|
||||
import { resume, handleInResume } from '../helpers.mjs';
|
||||
import {
|
||||
Assert,
|
||||
Call,
|
||||
CreateBuiltinFunction,
|
||||
CreateIterResultObject,
|
||||
GetGeneratorKind,
|
||||
NewPromiseCapability,
|
||||
PerformPromiseThen,
|
||||
PromiseResolve,
|
||||
} from './all.mjs';
|
||||
|
||||
// This file covers abstract operations defined in
|
||||
// 25.5 #sec-asyncgenerator-objects
|
||||
|
||||
// 25.5.3.1 #sec-asyncgeneratorrequest-records
|
||||
class AsyncGeneratorRequestRecord {
|
||||
constructor(completion, promiseCapability) {
|
||||
this.Completion = completion;
|
||||
this.Capability = promiseCapability;
|
||||
}
|
||||
}
|
||||
|
||||
// 25.5.3.2 #sec-asyncgeneratorstart
|
||||
export function AsyncGeneratorStart(generator, generatorBody) {
|
||||
// Assert: generator is an AsyncGenerator instance.
|
||||
Assert(generator.AsyncGeneratorState === Value.undefined);
|
||||
const genContext = surroundingAgent.runningExecutionContext;
|
||||
genContext.Generator = generator;
|
||||
genContext.codeEvaluationState = (function* resumer() {
|
||||
const result = EnsureCompletion(yield* Evaluate(generatorBody));
|
||||
// Assert: If we return here, the async generator either threw an exception or performed either an implicit or explicit return.
|
||||
surroundingAgent.executionContextStack.pop(genContext);
|
||||
generator.AsyncGeneratorState = 'completed';
|
||||
let resultValue;
|
||||
if (result instanceof NormalCompletion) {
|
||||
resultValue = Value.undefined;
|
||||
} else {
|
||||
resultValue = result.Value;
|
||||
if (result.Type !== 'return') {
|
||||
return X(AsyncGeneratorReject(generator, resultValue));
|
||||
}
|
||||
}
|
||||
return X(AsyncGeneratorResolve(generator, resultValue, Value.true));
|
||||
}());
|
||||
generator.AsyncGeneratorContext = genContext;
|
||||
generator.AsyncGeneratorState = 'suspendedStart';
|
||||
generator.AsyncGeneratorQueue = [];
|
||||
return Value.undefined;
|
||||
}
|
||||
|
||||
// 25.5.3.3 #sec-asyncgeneratorresolve
|
||||
function AsyncGeneratorResolve(generator, value, done) {
|
||||
// Assert: generator is an AsyncGenerator instance.
|
||||
const queue = generator.AsyncGeneratorQueue;
|
||||
Assert(queue.length > 0);
|
||||
const next = queue.shift();
|
||||
const promiseCapability = next.Capability;
|
||||
const iteratorResult = X(CreateIterResultObject(value, done));
|
||||
X(Call(promiseCapability.Resolve, Value.undefined, [iteratorResult]));
|
||||
X(AsyncGeneratorResumeNext(generator));
|
||||
return Value.undefined;
|
||||
}
|
||||
|
||||
// 25.5.3.4 #sec-asyncgeneratorreject
|
||||
function AsyncGeneratorReject(generator, exception) {
|
||||
// Assert: generator is an AsyncGenerator instance.
|
||||
const queue = generator.AsyncGeneratorQueue;
|
||||
Assert(queue.length > 0);
|
||||
const next = queue.shift();
|
||||
const promiseCapability = next.Capability;
|
||||
X(Call(promiseCapability.Reject, Value.undefined, [exception]));
|
||||
X(AsyncGeneratorResumeNext(generator));
|
||||
return Value.undefined;
|
||||
}
|
||||
|
||||
// 25.5.3.5.1 #async-generator-resume-next-return-processor-fulfilled
|
||||
function AsyncGeneratorResumeNextReturnProcessorFulfilledFunctions([value = Value.undefined]) {
|
||||
const F = surroundingAgent.activeFunctionObject;
|
||||
F.Generator.AsyncGeneratorState = 'completed';
|
||||
return X(AsyncGeneratorResolve(F.Generator, value, Value.true));
|
||||
}
|
||||
|
||||
// 25.5.3.5.2 #async-generator-resume-next-return-processor-rejected
|
||||
function AsyncGeneratorResumeNextReturnProcessorRejectedFunctions([reason = Value.undefined]) {
|
||||
const F = surroundingAgent.activeFunctionObject;
|
||||
F.Generator.AsyncGeneratorState = 'completed';
|
||||
return X(AsyncGeneratorReject(F.Generator, reason));
|
||||
}
|
||||
|
||||
// 25.5.3.5 #sec-asyncgeneratorresumenext
|
||||
function AsyncGeneratorResumeNext(generator) {
|
||||
// Assert: generator is an AsyncGenerator instance.
|
||||
let state = generator.AsyncGeneratorState;
|
||||
Assert(state !== 'executing');
|
||||
if (state === 'awaiting-return') {
|
||||
return Value.undefined;
|
||||
}
|
||||
const queue = generator.AsyncGeneratorQueue;
|
||||
if (queue.length === 0) {
|
||||
return Value.undefined;
|
||||
}
|
||||
const next = queue[0];
|
||||
Assert(next instanceof AsyncGeneratorRequestRecord);
|
||||
const completion = next.Completion;
|
||||
if (completion instanceof AbruptCompletion) {
|
||||
if (state === 'suspendedStart') {
|
||||
generator.AsyncGeneratorState = 'completed';
|
||||
state = 'completed';
|
||||
}
|
||||
if (state === 'completed') {
|
||||
if (completion.Type === 'return') {
|
||||
generator.AsyncGeneratorState = 'awaiting-return';
|
||||
const promise = Q(PromiseResolve(surroundingAgent.intrinsic('%Promise%'), completion.Value));
|
||||
const stepsFulfilled = AsyncGeneratorResumeNextReturnProcessorFulfilledFunctions;
|
||||
const onFulfilled = X(CreateBuiltinFunction(stepsFulfilled, ['Generator']));
|
||||
onFulfilled.Generator = generator;
|
||||
const stepsRejected = AsyncGeneratorResumeNextReturnProcessorRejectedFunctions;
|
||||
const onRejected = X(CreateBuiltinFunction(stepsRejected, ['Generator']));
|
||||
onRejected.Generator = generator;
|
||||
X(PerformPromiseThen(promise, onFulfilled, onRejected));
|
||||
return Value.undefined;
|
||||
} else {
|
||||
Assert(completion.Type === 'throw');
|
||||
X(AsyncGeneratorReject(generator, completion.Value));
|
||||
return Value.undefined;
|
||||
}
|
||||
}
|
||||
} else if (state === 'completed') {
|
||||
return X(AsyncGeneratorResolve(generator, Value.undefined, Value.true));
|
||||
}
|
||||
Assert(state === 'suspendedStart' || state === 'suspendedYield');
|
||||
const genContext = generator.AsyncGeneratorContext;
|
||||
const callerContext = surroundingAgent.runningExecutionContext;
|
||||
// Suspend callerContext
|
||||
generator.AsyncGeneratorState = 'executing';
|
||||
surroundingAgent.executionContextStack.push(genContext);
|
||||
const result = resume(genContext, completion);
|
||||
Assert(!(result instanceof AbruptCompletion));
|
||||
Assert(surroundingAgent.runningExecutionContext === callerContext);
|
||||
return Value.undefined;
|
||||
}
|
||||
|
||||
// 25.5.3.6 #sec-asyncgeneratorenqueue
|
||||
export function AsyncGeneratorEnqueue(generator, completion) {
|
||||
Assert(completion instanceof Completion);
|
||||
const promiseCapability = X(NewPromiseCapability(surroundingAgent.intrinsic('%Promise%')));
|
||||
if (Type(generator) !== 'Object' || !('AsyncGeneratorState' in generator)) {
|
||||
const badGeneratorError = surroundingAgent.Throw('TypeError', 'NotATypeObject', 'AsyncGenerator', generator).Value;
|
||||
X(Call(promiseCapability.Reject, Value.undefined, [badGeneratorError]));
|
||||
return promiseCapability.Promise;
|
||||
}
|
||||
const queue = generator.AsyncGeneratorQueue;
|
||||
const request = new AsyncGeneratorRequestRecord(completion, promiseCapability);
|
||||
queue.push(request);
|
||||
const state = generator.AsyncGeneratorState;
|
||||
if (state !== 'executing') {
|
||||
X(AsyncGeneratorResumeNext(generator));
|
||||
}
|
||||
return promiseCapability.Promise;
|
||||
}
|
||||
|
||||
// #sec-asyncgeneratoryield
|
||||
export function* AsyncGeneratorYield(value) {
|
||||
// 1. Let genContext be the running execution context.
|
||||
const genContext = surroundingAgent.runningExecutionContext;
|
||||
// 2. Assert: genContext is the execution context of a generator.
|
||||
Assert(genContext.Generator !== Value.undefined);
|
||||
// 3. Let generator be the value of the Generator component of genContext.
|
||||
const generator = genContext.Generator;
|
||||
// 4. Assert: GetGeneratorKind() is async.
|
||||
Assert(GetGeneratorKind() === 'async');
|
||||
// 5. Set value to ? Await(value).
|
||||
value = Q(yield* Await(value));
|
||||
// 6. Set generator.[[AsyncGeneratorState]] to suspendedYield.
|
||||
generator.AsyncGeneratorState = 'suspendedYield';
|
||||
// 7. Remove genContext from the execution context stack and restore the execution context that is at the top of the execution context stack as the running execution context.
|
||||
surroundingAgent.executionContextStack.pop(genContext);
|
||||
// 8. Set the code evaluation state of genContext such that when evaluation is resumed with a Completion resumptionValue the following steps will be performed:
|
||||
const resumptionValue = EnsureCompletion(yield handleInResume(AsyncGeneratorResolve, generator, value, Value.false));
|
||||
|
||||
// a. If resumptionValue.[[Type]] is not return, return Completion(resumptionValue).
|
||||
if (resumptionValue.Type !== 'return') {
|
||||
return Completion(resumptionValue);
|
||||
}
|
||||
// b. Let awaited be Await(resumptionValue.[[Value]]).
|
||||
const awaited = EnsureCompletion(yield* Await(resumptionValue.Value));
|
||||
// c. If awaited.[[Type]] is throw, return Completion(awaited).
|
||||
if (awaited.Type === 'Throw') {
|
||||
return Completion(awaited);
|
||||
}
|
||||
// d. Assert: awaited.[[Type]] is normal.
|
||||
Assert(awaited.Type === 'normal');
|
||||
// e. Return Completion { [[Type]]: return, [[Value]]: awaited.[[Value]], [[Target]]: empty }.
|
||||
return new Completion({ Type: 'return', Value: awaited.Value, Target: undefined });
|
||||
// f. NOTE: When one of the above steps returns, it returns to the evaluation of the YieldExpression production that originally called this abstract operation.
|
||||
|
||||
// 9. Return ! AsyncGeneratorResolve(generator, value, false).
|
||||
// 10. NOTE: This returns to the evaluation of the operation that had most previously resumed evaluation of genContext.
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
import { Type, Value } from '../value.mjs';
|
||||
import { X } from '../completion.mjs';
|
||||
import { CanonicalNumericIndexString } from './all.mjs';
|
||||
|
||||
// This file covers predicates defined in
|
||||
// 6 #sec-ecmascript-data-types-and-values
|
||||
|
||||
// 6.1.7 #integer-index
|
||||
export function isIntegerIndex(V) {
|
||||
if (Type(V) !== 'String') {
|
||||
return false;
|
||||
}
|
||||
const numeric = X(CanonicalNumericIndexString(V));
|
||||
if (numeric === Value.undefined) {
|
||||
return false;
|
||||
}
|
||||
if (Object.is(numeric.numberValue(), +0)) {
|
||||
return true;
|
||||
}
|
||||
return numeric.numberValue() > 0 && Number.isSafeInteger(numeric.numberValue());
|
||||
}
|
||||
|
||||
// 6.1.7 #array-index
|
||||
export function isArrayIndex(V) {
|
||||
if (Type(V) !== 'String') {
|
||||
return false;
|
||||
}
|
||||
const numeric = X(CanonicalNumericIndexString(V));
|
||||
if (numeric === Value.undefined) {
|
||||
return false;
|
||||
}
|
||||
if (!Number.isInteger(numeric.numberValue())) {
|
||||
return false;
|
||||
}
|
||||
if (Object.is(numeric.numberValue(), +0)) {
|
||||
return true;
|
||||
}
|
||||
return numeric.numberValue() > 0 && numeric.numberValue() < (2 ** 32) - 1;
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
import { Q, X } from '../completion.mjs';
|
||||
import { surroundingAgent } from '../engine.mjs';
|
||||
import { Value } from '../value.mjs';
|
||||
import {
|
||||
Assert,
|
||||
GetValueFromBuffer,
|
||||
IsDetachedBuffer,
|
||||
IsBigIntElementType,
|
||||
SetValueInBuffer,
|
||||
ToBoolean,
|
||||
ToIndex,
|
||||
ToNumber,
|
||||
ToBigInt,
|
||||
RequireInternalSlot,
|
||||
typedArrayInfoByType,
|
||||
} from './all.mjs';
|
||||
|
||||
// This file covers abstract operations defined in
|
||||
// 24.3 #sec-dataview-objects
|
||||
|
||||
// 24.3.1.1 #sec-getviewvalue
|
||||
export function GetViewValue(view, requestIndex, isLittleEndian, type) {
|
||||
// 1. Perform ? RequireInternalSlot(view, [[DataView]]).
|
||||
Q(RequireInternalSlot(view, 'DataView'));
|
||||
// 2. Assert: view has a [[ViewedArrayBuffer]] internal slot.
|
||||
Assert('ViewedArrayBuffer' in view);
|
||||
// 3. Let getIndex be ? ToIndex(requestIndex).
|
||||
const getIndex = Q(ToIndex(requestIndex)).numberValue();
|
||||
// 4. Set isLittleEndian to ! ToBoolean(isLittleEndian).
|
||||
isLittleEndian = X(ToBoolean(isLittleEndian));
|
||||
// 5. Let buffer be view.[[ViewedArrayBuffer]].
|
||||
const buffer = view.ViewedArrayBuffer;
|
||||
// 6. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
|
||||
if (IsDetachedBuffer(buffer) === Value.true) {
|
||||
return surroundingAgent.Throw('TypeError', 'ArrayBufferDetached');
|
||||
}
|
||||
// 7. Let viewOffset be view.[[ByteOffset]].
|
||||
const viewOffset = view.ByteOffset.numberValue();
|
||||
// 8. Let viewSize be view.[[ByteLength]].
|
||||
const viewSize = view.ByteLength.numberValue();
|
||||
// 9. Let elementSize be the Element Size value specified in Table 61 for Element Type type.
|
||||
const elementSize = typedArrayInfoByType[type].ElementSize;
|
||||
// 10. If getIndex + elementSize > viewSize, throw a RangeError exception.
|
||||
if (getIndex + elementSize > viewSize) {
|
||||
return surroundingAgent.Throw('RangeError', 'DataViewOOB');
|
||||
}
|
||||
// 11. Let bufferIndex be getIndex + viewOffset.
|
||||
const bufferIndex = new Value(getIndex + viewOffset);
|
||||
// 12. Return GetValueFromBuffer(buffer, bufferIndex, type, false, Unordered, isLittleEndian).
|
||||
return GetValueFromBuffer(buffer, bufferIndex, type, Value.false, 'Unordered', isLittleEndian);
|
||||
}
|
||||
|
||||
// 24.3.1.2 #sec-setviewvalue
|
||||
export function SetViewValue(view, requestIndex, isLittleEndian, type, value) {
|
||||
// 1. Perform ? RequireInternalSlot(view, [[DataView]]).
|
||||
Q(RequireInternalSlot(view, 'DataView'));
|
||||
// 2. Assert: view has a [[ViewedArrayBuffer]] internal slot.
|
||||
Assert('ViewedArrayBuffer' in view);
|
||||
// 3. Let getIndex be ? ToIndex(requestIndex).
|
||||
const getIndex = Q(ToIndex(requestIndex)).numberValue();
|
||||
// 4. If ! IsBigIntElementType(type) is true, let numberValue be ? ToBigInt(value).
|
||||
// 5. Otherwise, let numberValue be ? ToNumber(value).
|
||||
let numberValue;
|
||||
if (X(IsBigIntElementType(type)) === Value.true) {
|
||||
numberValue = Q(ToBigInt(value));
|
||||
} else {
|
||||
numberValue = Q(ToNumber(value));
|
||||
}
|
||||
// 6. Set isLittleEndian to ! ToBoolean(isLittleEndian).
|
||||
isLittleEndian = X(ToBoolean(isLittleEndian));
|
||||
// 7. Let buffer be view.[[ViewedArrayBuffer]].
|
||||
const buffer = view.ViewedArrayBuffer;
|
||||
// 8. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
|
||||
if (IsDetachedBuffer(buffer) === Value.true) {
|
||||
return surroundingAgent.Throw('TypeError', 'ArrayBufferDetached');
|
||||
}
|
||||
// 9. Let viewOffset be view.[[ByteOffset]].
|
||||
const viewOffset = view.ByteOffset.numberValue();
|
||||
// 10. Let viewSize be view.[[ByteLength]].
|
||||
const viewSize = view.ByteLength.numberValue();
|
||||
// 11. Let elementSize be the Element Size value specified in Table 61 for Element Type type.
|
||||
const elementSize = typedArrayInfoByType[type].ElementSize;
|
||||
// 12. If getIndex + elementSize > viewSize, throw a RangeError exception.
|
||||
if (getIndex + elementSize > viewSize) {
|
||||
return surroundingAgent.Throw('RangeError', 'DataViewOOB');
|
||||
}
|
||||
// 13. Let bufferIndex be getIndex + viewOffset.
|
||||
const bufferIndex = new Value(getIndex + viewOffset);
|
||||
// 14. Return SetValueInBuffer(buffer, bufferIndex, type, numberValue, false, Unordered, isLittleEndian).
|
||||
return SetValueInBuffer(buffer, bufferIndex, type, numberValue, Value.false, 'Unordered', isLittleEndian);
|
||||
}
|
||||
@@ -1,233 +0,0 @@
|
||||
// This file covers abstract operations defined in
|
||||
// 20.3 #sec-date-objects
|
||||
|
||||
import {
|
||||
Value,
|
||||
} from '../value.mjs';
|
||||
import { X } from '../completion.mjs';
|
||||
import {
|
||||
ToInteger,
|
||||
} from './all.mjs';
|
||||
|
||||
const mod = (n, m) => {
|
||||
const r = n % m;
|
||||
return Math.floor(r >= 0 ? r : r + m);
|
||||
};
|
||||
|
||||
export const HoursPerDay = 24;
|
||||
export const MinutesPerHour = 60;
|
||||
export const SecondsPerMinute = 60;
|
||||
export const msPerSecond = 1000;
|
||||
export const msPerMinute = msPerSecond * SecondsPerMinute;
|
||||
export const msPerHour = msPerMinute * MinutesPerHour;
|
||||
export const msPerDay = msPerHour * HoursPerDay;
|
||||
|
||||
// 20.3.1.2 #sec-day-number-and-time-within-day
|
||||
export function Day(t) {
|
||||
return new Value(Math.floor(t.numberValue() / msPerDay));
|
||||
}
|
||||
|
||||
export function TimeWithinDay(t) {
|
||||
return new Value(mod(t.numberValue(), msPerDay));
|
||||
}
|
||||
|
||||
// 20.3.1.3 #sec-year-number
|
||||
export function DaysInYear(y) {
|
||||
y = y.numberValue();
|
||||
if (mod(y, 4) !== 0) {
|
||||
return new Value(365);
|
||||
}
|
||||
if (mod(y, 4) === 0 && mod(y, 100) !== 0) {
|
||||
return new Value(366);
|
||||
}
|
||||
if (mod(y, 100) === 0 && mod(y, 400) !== 0) {
|
||||
return new Value(365);
|
||||
}
|
||||
if (mod(y, 400) === 0) {
|
||||
return new Value(366);
|
||||
}
|
||||
}
|
||||
|
||||
export function DayFromYear(y) {
|
||||
y = y.numberValue();
|
||||
return new Value(365 * (y - 1970) + Math.floor((y - 1969) / 4) - Math.floor((y - 1901) / 100) + Math.floor((y - 1601) / 400));
|
||||
}
|
||||
|
||||
export function TimeFromYear(y) {
|
||||
return new Value(msPerDay * DayFromYear(y).numberValue());
|
||||
}
|
||||
|
||||
export const msPerAverageYear = 12 * 30.436875 * msPerDay;
|
||||
|
||||
export function YearFromTime(t) {
|
||||
t = t.numberValue();
|
||||
let year = Math.floor((t + msPerAverageYear / 2) / msPerAverageYear) + 1970;
|
||||
if (TimeFromYear(new Value(year)).numberValue() > t) {
|
||||
year -= 1;
|
||||
}
|
||||
return new Value(year);
|
||||
}
|
||||
|
||||
export function InLeapYear(t) {
|
||||
if (DaysInYear(YearFromTime(t)).numberValue() === 365) {
|
||||
return new Value(0);
|
||||
}
|
||||
if (DaysInYear(YearFromTime(t)).numberValue() === 366) {
|
||||
return new Value(1);
|
||||
}
|
||||
}
|
||||
|
||||
// 20.3.1.4 #sec-month-number
|
||||
export function MonthFromTime(t) {
|
||||
const dayWithinYear = DayWithinYear(t).numberValue();
|
||||
const inLeapYear = InLeapYear(t).numberValue();
|
||||
if (dayWithinYear >= 0 && dayWithinYear < 31) {
|
||||
return new Value(0);
|
||||
}
|
||||
if (dayWithinYear >= 31 && dayWithinYear < 59 + inLeapYear) {
|
||||
return new Value(1);
|
||||
}
|
||||
if (dayWithinYear >= 59 + inLeapYear && dayWithinYear < 90 + inLeapYear) {
|
||||
return new Value(2);
|
||||
}
|
||||
if (dayWithinYear >= 90 + inLeapYear && dayWithinYear < 120 + inLeapYear) {
|
||||
return new Value(3);
|
||||
}
|
||||
if (dayWithinYear >= 120 + inLeapYear && dayWithinYear < 151 + inLeapYear) {
|
||||
return new Value(4);
|
||||
}
|
||||
if (dayWithinYear >= 151 + inLeapYear && dayWithinYear < 181 + inLeapYear) {
|
||||
return new Value(5);
|
||||
}
|
||||
if (dayWithinYear >= 181 + inLeapYear && dayWithinYear < 212 + inLeapYear) {
|
||||
return new Value(6);
|
||||
}
|
||||
if (dayWithinYear >= 212 + inLeapYear && dayWithinYear < 243 + inLeapYear) {
|
||||
return new Value(7);
|
||||
}
|
||||
if (dayWithinYear >= 243 + inLeapYear && dayWithinYear < 273 + inLeapYear) {
|
||||
return new Value(8);
|
||||
}
|
||||
if (dayWithinYear >= 273 + inLeapYear && dayWithinYear < 304 + inLeapYear) {
|
||||
return new Value(9);
|
||||
}
|
||||
if (dayWithinYear >= 304 + inLeapYear && dayWithinYear < 334 + inLeapYear) {
|
||||
return new Value(10);
|
||||
}
|
||||
if (dayWithinYear >= 334 + inLeapYear && dayWithinYear < 365 + inLeapYear) {
|
||||
return new Value(11);
|
||||
}
|
||||
}
|
||||
|
||||
export function DayWithinYear(t) {
|
||||
return new Value(Day(t).numberValue() - DayFromYear(YearFromTime(t)).numberValue());
|
||||
}
|
||||
|
||||
// 20.3.1.5 #sec-date-number
|
||||
export function DateFromTime(t) {
|
||||
const dayWithinYear = DayWithinYear(t).numberValue();
|
||||
const monthFromTime = MonthFromTime(t).numberValue();
|
||||
const inLeapYear = InLeapYear(t).numberValue();
|
||||
switch (monthFromTime) {
|
||||
case 0: return new Value(dayWithinYear + 1);
|
||||
case 1: return new Value(dayWithinYear - 30);
|
||||
case 2: return new Value(dayWithinYear - 58 - inLeapYear);
|
||||
case 3: return new Value(dayWithinYear - 89 - inLeapYear);
|
||||
case 4: return new Value(dayWithinYear - 119 - inLeapYear);
|
||||
case 5: return new Value(dayWithinYear - 150 - inLeapYear);
|
||||
case 6: return new Value(dayWithinYear - 180 - inLeapYear);
|
||||
case 7: return new Value(dayWithinYear - 211 - inLeapYear);
|
||||
case 8: return new Value(dayWithinYear - 242 - inLeapYear);
|
||||
case 9: return new Value(dayWithinYear - 272 - inLeapYear);
|
||||
case 10: return new Value(dayWithinYear - 303 - inLeapYear);
|
||||
case 11: return new Value(dayWithinYear - 333 - inLeapYear);
|
||||
default: // Unreachable
|
||||
}
|
||||
}
|
||||
|
||||
// 20.3.1.6 #sec-week-day
|
||||
export function WeekDay(t) {
|
||||
return new Value(mod(Day(t).numberValue() + 4, 7));
|
||||
}
|
||||
|
||||
// 20.3.1.7 #sec-local-time-zone-adjustment
|
||||
export function LocalTZA(/* t, isUTC */) {
|
||||
// TODO: implement this function properly.
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 20.3.1.8 #sec-localtime
|
||||
export function LocalTime(t) {
|
||||
return new Value(t.numberValue() + LocalTZA(t, true));
|
||||
}
|
||||
|
||||
// 20.3.1.9 #sec-utc-t
|
||||
export function UTC(t) {
|
||||
return new Value(t.numberValue() - LocalTZA(t, false));
|
||||
}
|
||||
|
||||
// 20.3.1.10 #sec-hours-minutes-second-and-milliseconds
|
||||
export function HourFromTime(t) {
|
||||
return new Value(mod(Math.floor(t.numberValue() / msPerHour), HoursPerDay));
|
||||
}
|
||||
|
||||
export function MinFromTime(t) {
|
||||
return new Value(mod(Math.floor(t.numberValue() / msPerMinute), MinutesPerHour));
|
||||
}
|
||||
|
||||
export function SecFromTime(t) {
|
||||
return new Value(mod(Math.floor(t.numberValue() / msPerSecond), SecondsPerMinute));
|
||||
}
|
||||
|
||||
export function msFromTime(t) {
|
||||
return new Value(mod(t.numberValue(), msPerSecond));
|
||||
}
|
||||
|
||||
// 20.3.1.11 #sec-maketime
|
||||
export function MakeTime(hour, min, sec, ms) {
|
||||
if (!Number.isFinite(hour.numberValue()) || !Number.isFinite(min.numberValue()) || !Number.isFinite(sec.numberValue()) || !Number.isFinite(ms.numberValue())) {
|
||||
return new Value(NaN);
|
||||
}
|
||||
const h = X(ToInteger(hour)).numberValue();
|
||||
const m = X(ToInteger(min)).numberValue();
|
||||
const s = X(ToInteger(sec)).numberValue();
|
||||
const milli = X(ToInteger(ms)).numberValue();
|
||||
const t = h * msPerHour + m * msPerMinute + s * msPerSecond + milli;
|
||||
return new Value(t);
|
||||
}
|
||||
|
||||
const daysWithinYearToEndOfMonth = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365];
|
||||
|
||||
// 20.3.1.12 #sec-makeday
|
||||
export function MakeDay(year, month, date) {
|
||||
if (!Number.isFinite(year.numberValue()) || !Number.isFinite(month.numberValue()) || !Number.isFinite(date.numberValue())) {
|
||||
return new Value(NaN);
|
||||
}
|
||||
const y = X(ToInteger(year)).numberValue();
|
||||
const m = X(ToInteger(month)).numberValue();
|
||||
const dt = X(ToInteger(date)).numberValue();
|
||||
const ym = y + Math.floor(m / 12);
|
||||
const mn = mod(m, 12);
|
||||
const ymday = DayFromYear(new Value(ym + (mn > 1 ? 1 : 0))).numberValue() - 365 * (mn > 1 ? 1 : 0) + daysWithinYearToEndOfMonth[mn];
|
||||
const t = new Value(ymday * msPerDay);
|
||||
return new Value(Day(t).numberValue() + dt - 1);
|
||||
}
|
||||
|
||||
// 20.3.1.13 #sec-makedate
|
||||
export function MakeDate(day, time) {
|
||||
if (!Number.isFinite(day.numberValue()) || !Number.isFinite(time.numberValue())) {
|
||||
return new Value(NaN);
|
||||
}
|
||||
return new Value(day.numberValue() * msPerDay + time.numberValue());
|
||||
}
|
||||
|
||||
// 20.3.1.14 #sec-timeclip
|
||||
export function TimeClip(time) {
|
||||
if (!Number.isFinite(time.numberValue())) {
|
||||
return new Value(NaN);
|
||||
}
|
||||
if (Math.abs(time.numberValue()) > 8.64e15) {
|
||||
return new Value(NaN);
|
||||
}
|
||||
return X(ToInteger(time));
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
import { Q } from '../completion.mjs';
|
||||
import { surroundingAgent } from '../engine.mjs';
|
||||
import {
|
||||
GetIdentifierReference,
|
||||
EnvironmentRecord,
|
||||
} from '../environment.mjs';
|
||||
import { Value } from '../value.mjs';
|
||||
import { Assert } from './all.mjs';
|
||||
|
||||
// This file covers abstract operations defined in
|
||||
// 8.3 #sec-execution-contexts
|
||||
|
||||
// 8.3.1 #sec-getactivescriptormodule
|
||||
export function GetActiveScriptOrModule() {
|
||||
for (let i = surroundingAgent.executionContextStack.length - 1; i >= 0; i -= 1) {
|
||||
const e = surroundingAgent.executionContextStack[i];
|
||||
if (e.ScriptOrModule !== Value.null) {
|
||||
return e.ScriptOrModule;
|
||||
}
|
||||
}
|
||||
return Value.null;
|
||||
}
|
||||
|
||||
// 8.3.2 #sec-resolvebinding
|
||||
export function ResolveBinding(name, env, strict) {
|
||||
// 1. If env is not present or if env is undefined, then
|
||||
if (env === undefined || env === Value.undefined) {
|
||||
// a. Set env to the running execution context's LexicalEnvironment.
|
||||
env = surroundingAgent.runningExecutionContext.LexicalEnvironment;
|
||||
}
|
||||
// 2. Assert: env is an Environment Record.
|
||||
Assert(env instanceof EnvironmentRecord);
|
||||
// 3. If the code matching the syntactic production that is being evaluated is contained in strict mode code, let strict be true; else let strict be false.
|
||||
// 4. Return ? GetIdentifierReference(env, name, strict).
|
||||
return GetIdentifierReference(env, name, strict ? Value.true : Value.false);
|
||||
}
|
||||
|
||||
// #sec-getthisenvironment
|
||||
export function GetThisEnvironment() {
|
||||
// 1. Let env be the running execution context's LexicalEnvironment.
|
||||
let env = surroundingAgent.runningExecutionContext.LexicalEnvironment;
|
||||
// 2. Repeat,
|
||||
while (true) {
|
||||
// a. Let exists be env.HasThisBinding().
|
||||
const exists = env.HasThisBinding();
|
||||
// b. If exists is true, return envRec.
|
||||
if (exists === Value.true) {
|
||||
return env;
|
||||
}
|
||||
// c. Let outer be env.[[OuterEnv]].
|
||||
const outer = env.OuterEnv;
|
||||
// d. Assert: outer is not null.
|
||||
Assert(outer !== Value.null);
|
||||
// e. Set env to outer.
|
||||
env = outer;
|
||||
}
|
||||
}
|
||||
|
||||
// 8.3.4 #sec-resolvethisbinding
|
||||
export function ResolveThisBinding() {
|
||||
const envRec = GetThisEnvironment();
|
||||
return Q(envRec.GetThisBinding());
|
||||
}
|
||||
|
||||
// 8.3.5 #sec-getnewtarget
|
||||
export function GetNewTarget() {
|
||||
const envRec = GetThisEnvironment();
|
||||
Assert('NewTarget' in envRec);
|
||||
return envRec.NewTarget;
|
||||
}
|
||||
|
||||
// 8.3.6 #sec-getglobalobject
|
||||
export function GetGlobalObject() {
|
||||
const currentRealm = surroundingAgent.currentRealmRecord;
|
||||
return currentRealm.GlobalObject;
|
||||
}
|
||||
@@ -1,449 +0,0 @@
|
||||
import {
|
||||
surroundingAgent,
|
||||
ExecutionContext,
|
||||
} from '../engine.mjs';
|
||||
import {
|
||||
Descriptor,
|
||||
Type,
|
||||
Value,
|
||||
} from '../value.mjs';
|
||||
import {
|
||||
EnsureCompletion,
|
||||
NormalCompletion,
|
||||
ReturnIfAbrupt,
|
||||
Q, X,
|
||||
} from '../completion.mjs';
|
||||
import { ExpectedArgumentCount } from '../static-semantics/all.mjs';
|
||||
import { EvaluateBody } from '../runtime-semantics/all.mjs';
|
||||
import {
|
||||
FunctionEnvironmentRecord,
|
||||
GlobalEnvironmentRecord,
|
||||
NewFunctionEnvironment,
|
||||
} from '../environment.mjs';
|
||||
import { unwind } from '../helpers.mjs';
|
||||
import {
|
||||
Assert,
|
||||
DefinePropertyOrThrow,
|
||||
GetActiveScriptOrModule,
|
||||
HasOwnProperty,
|
||||
IsConstructor,
|
||||
IsExtensible,
|
||||
IsInteger,
|
||||
MakeBasicObject,
|
||||
OrdinaryObjectCreate,
|
||||
OrdinaryCreateFromConstructor,
|
||||
ToObject,
|
||||
isStrictModeCode,
|
||||
Realm,
|
||||
} from './all.mjs';
|
||||
|
||||
// This file covers abstract operations defined in
|
||||
// 9.2 #sec-ecmascript-function-objects
|
||||
// 9.3 #sec-built-in-function-objects
|
||||
// and
|
||||
// 14.9 #sec-tail-position-calls
|
||||
|
||||
export function isECMAScriptFunctionObject(O) {
|
||||
return 'ECMAScriptCode' in O;
|
||||
}
|
||||
|
||||
export function isFunctionObject(O) {
|
||||
return 'Call' in O;
|
||||
}
|
||||
|
||||
// #sec-prepareforordinarycall
|
||||
export function PrepareForOrdinaryCall(F, newTarget) {
|
||||
// 1. Assert: Type(newTarget) is Undefined or Object.
|
||||
Assert(Type(newTarget) === 'Undefined' || Type(newTarget) === 'Object');
|
||||
// 2. Let callerContext be the running execution context.
|
||||
// const callerContext = surroundingAgent.runningExecutionContext;
|
||||
// 3. Let calleeContext be a new ECMAScript code execution context.
|
||||
const calleeContext = new ExecutionContext();
|
||||
// 4. Set the Function of calleeContext to F.
|
||||
calleeContext.Function = F;
|
||||
// 5. Let calleeRealm be F.[[Realm]].
|
||||
const calleeRealm = F.Realm;
|
||||
// 6. Set the Realm of calleeContext to calleeRealm.
|
||||
calleeContext.Realm = calleeRealm;
|
||||
// 7. Set the ScriptOrModule of calleeContext to F.[[ScriptOrModule]].
|
||||
calleeContext.ScriptOrModule = F.ScriptOrModule;
|
||||
// 8. Let localEnv be NewFunctionEnvironment(F, newTarget).
|
||||
const localEnv = NewFunctionEnvironment(F, newTarget);
|
||||
// 9. Set the LexicalEnvironment of calleeContext to localEnv.
|
||||
calleeContext.LexicalEnvironment = localEnv;
|
||||
// 10. Set the VariableEnvironment of calleeContext to localEnv.
|
||||
calleeContext.VariableEnvironment = localEnv;
|
||||
// 11. Set the VariableEnvironment of calleeContext to localEnv.
|
||||
// 12. Push calleeContext onto the execution context stack; calleeContext is now the running execution context.
|
||||
surroundingAgent.executionContextStack.push(calleeContext);
|
||||
// 13. NOTE: Any exception objects produced after this point are associated with calleeRealm.
|
||||
// 14. Return calleeContext.
|
||||
return calleeContext;
|
||||
}
|
||||
|
||||
// #sec-ordinarycallbindthis
|
||||
export function OrdinaryCallBindThis(F, calleeContext, thisArgument) {
|
||||
// 1. Let thisMode be F.[[ThisMode]].
|
||||
const thisMode = F.ThisMode;
|
||||
// 2. If thisMode is lexical, return NormalCompletion(undefined).
|
||||
if (thisMode === 'lexical') {
|
||||
return NormalCompletion(Value.undefined);
|
||||
}
|
||||
// 3. Let calleeRealm be F.[[Realm]].
|
||||
const calleeRealm = F.Realm;
|
||||
// 4. Let localEnv be the LexicalEnvironment of calleeContext.
|
||||
const localEnv = calleeContext.LexicalEnvironment;
|
||||
let thisValue;
|
||||
// 5. If thisMode is strict, let thisValue be thisArgument.
|
||||
if (thisMode === 'strict') {
|
||||
thisValue = thisArgument;
|
||||
} else { // 6. Else,
|
||||
// a. If thisArgument is undefined or null, then
|
||||
if (thisArgument === Value.undefined || thisArgument === Value.null) {
|
||||
// i. Let globalEnv be calleeRealm.[[GlobalEnv]].
|
||||
const globalEnv = calleeRealm.GlobalEnv;
|
||||
// ii. Assert: globalEnv is a global Environment Record.
|
||||
Assert(globalEnv instanceof GlobalEnvironmentRecord);
|
||||
// iii. Let thisValue be globalEnv.[[GlobalThisValue]].
|
||||
thisValue = globalEnv.GlobalThisValue;
|
||||
} else { // b. Else,
|
||||
// i. Let thisValue be ! ToObject(thisArgument).
|
||||
thisValue = X(ToObject(thisArgument));
|
||||
// ii. NOTE: ToObject produces wrapper objects using calleeRealm.
|
||||
}
|
||||
}
|
||||
// 7. Assert: localEnv is a function Environment Record.
|
||||
Assert(localEnv instanceof FunctionEnvironmentRecord);
|
||||
// 8. Assert: The next step never returns an abrupt completion because localEnv.[[ThisBindingStatus]] is not initialized.
|
||||
Assert(localEnv.ThisBindingStatus !== 'initialized');
|
||||
// 10. Return localEnv.BindThisValue(thisValue).
|
||||
return localEnv.BindThisValue(thisValue);
|
||||
}
|
||||
|
||||
// #sec-ordinarycallevaluatebody
|
||||
export function OrdinaryCallEvaluateBody(F, argumentsList) {
|
||||
// 1. Return the result of EvaluateBody of the parsed code that is F.[[ECMAScriptCode]] passing F and argumentsList as the arguments.
|
||||
return EnsureCompletion(unwind(EvaluateBody(F.ECMAScriptCode, F, argumentsList)));
|
||||
}
|
||||
|
||||
// #sec-ecmascript-function-objects-call-thisargument-argumentslist
|
||||
function FunctionCallSlot(thisArgument, argumentsList) {
|
||||
const F = this;
|
||||
|
||||
// 1. Assert: F is an ECMAScript function object.
|
||||
Assert(isECMAScriptFunctionObject(F));
|
||||
// 2. If F.[[IsClassConstructor]] is true, throw a TypeError exception.
|
||||
if (F.IsClassConstructor === Value.true) {
|
||||
return surroundingAgent.Throw('TypeError', 'ConstructorNonCallable', F);
|
||||
}
|
||||
// 3. Let callerContext be the running execution context.
|
||||
// 4. Let calleeContext be PrepareForOrdinaryCall(F, undefined).
|
||||
const calleeContext = PrepareForOrdinaryCall(F, Value.undefined);
|
||||
// 5. Assert: calleeContext is now the running execution context.
|
||||
Assert(surroundingAgent.runningExecutionContext === calleeContext);
|
||||
// 6. Perform OrdinaryCallBindThis(F, calleeContext, thisArgument).
|
||||
OrdinaryCallBindThis(F, calleeContext, thisArgument);
|
||||
// 7. Let result be OrdinaryCallEvaluateBody(F, argumentsList).
|
||||
const result = OrdinaryCallEvaluateBody(F, argumentsList);
|
||||
// 8. Remove calleeContext from the execution context stack and restore callerContext as the running execution context.
|
||||
surroundingAgent.executionContextStack.pop(calleeContext);
|
||||
// 9. If result.[[Type]] is return, return NormalCompletion(result.[[Value]]).
|
||||
if (result.Type === 'return') {
|
||||
return NormalCompletion(result.Value);
|
||||
}
|
||||
// 10. ReturnIfAbrupt(result).
|
||||
ReturnIfAbrupt(result);
|
||||
// 11. Return NormalCompletion(undefined).
|
||||
return NormalCompletion(Value.undefined);
|
||||
}
|
||||
|
||||
// 9.2.2 #sec-ecmascript-function-objects-construct-argumentslist-newtarget
|
||||
function FunctionConstructSlot(argumentsList, newTarget) {
|
||||
const F = this;
|
||||
|
||||
// 1. Assert: F is an ECMAScript function object.
|
||||
Assert(isECMAScriptFunctionObject(F));
|
||||
// 2. Assert: Type(newTarget) is Object.
|
||||
Assert(Type(newTarget) === 'Object');
|
||||
// 3. Let callerContext be the running execution context.
|
||||
// 4. Let kind be F.[[ConstructorKind]].
|
||||
const kind = F.ConstructorKind;
|
||||
let thisArgument;
|
||||
// 5. If kind is base, then
|
||||
if (kind === 'base') {
|
||||
// a. Let thisArgument be ? OrdinaryCreateFromConstructor(newTarget, "%Object.prototype%").
|
||||
thisArgument = Q(OrdinaryCreateFromConstructor(newTarget, '%Object.prototype%'));
|
||||
}
|
||||
// 6. Let calleeContext be PrepareForOrdinaryCall(F, newTarget).
|
||||
const calleeContext = PrepareForOrdinaryCall(F, newTarget);
|
||||
// 7. Assert: calleeContext is now the running execution context.
|
||||
Assert(surroundingAgent.runningExecutionContext === calleeContext);
|
||||
surroundingAgent.runningExecutionContext.callSite.constructCall = true;
|
||||
// 8. If kind is base, perform OrdinaryCallBindThis(F, calleeContext, thisArgument).
|
||||
if (kind === 'base') {
|
||||
OrdinaryCallBindThis(F, calleeContext, thisArgument);
|
||||
}
|
||||
// 9. Let constructorEnv be the LexicalEnvironment of calleeContext.
|
||||
const constructorEnv = calleeContext.LexicalEnvironment;
|
||||
// 10. Let result be OrdinaryCallEvaluateBody(F, argumentsList).
|
||||
const result = OrdinaryCallEvaluateBody(F, argumentsList);
|
||||
// 11. Remove calleeContext from the execution context stack and restore callerContext as the running execution context.
|
||||
surroundingAgent.executionContextStack.pop(calleeContext);
|
||||
// 12. If result.[[Type]] is return, then
|
||||
if (result.Type === 'return') {
|
||||
// a. If Type(result.[[Value]]) is Object, return NormalCompletion(result.[[Value]]).
|
||||
if (Type(result.Value) === 'Object') {
|
||||
return NormalCompletion(result.Value);
|
||||
}
|
||||
// b. If kind is base, return NormalCompletion(thisArgument).
|
||||
if (kind === 'base') {
|
||||
return NormalCompletion(thisArgument);
|
||||
}
|
||||
// c. If result.[[Value]] is not undefined, throw a TypeError exception.
|
||||
if (result.Value !== Value.undefined) {
|
||||
return surroundingAgent.Throw('TypeError', 'DerivedConstructorReturnedNonObject');
|
||||
}
|
||||
} else { // 13. Else, ReturnIfAbrupt(result).
|
||||
ReturnIfAbrupt(result);
|
||||
}
|
||||
// 14. Return ? constructorEnv.GetThisBinding().
|
||||
return Q(constructorEnv.GetThisBinding());
|
||||
}
|
||||
|
||||
// 9.2.3 #sec-functionallocate
|
||||
export function OrdinaryFunctionCreate(functionPrototype, sourceText, ParameterList, Body, thisMode, Scope) {
|
||||
Assert(Type(functionPrototype) === 'Object');
|
||||
const internalSlotsList = [
|
||||
'Environment',
|
||||
'FormalParameters',
|
||||
'ECMAScriptCode',
|
||||
'ConstructorKind',
|
||||
'Realm',
|
||||
'ScriptOrModule',
|
||||
'ThisMode',
|
||||
'Strict',
|
||||
'HomeObject',
|
||||
'SourceText',
|
||||
'IsClassConstructor',
|
||||
];
|
||||
const F = X(OrdinaryObjectCreate(functionPrototype, internalSlotsList));
|
||||
F.Call = surroundingAgent.hostDefinedOptions.boost
|
||||
? surroundingAgent.hostDefinedOptions.boost.callFunction
|
||||
: FunctionCallSlot;
|
||||
F.SourceText = sourceText;
|
||||
F.Environment = Scope;
|
||||
F.FormalParameters = ParameterList;
|
||||
F.ECMAScriptCode = Body;
|
||||
const Strict = isStrictModeCode(Body);
|
||||
F.Strict = Strict;
|
||||
if (thisMode === 'lexical-this') {
|
||||
F.ThisMode = 'lexical';
|
||||
} else if (Strict) {
|
||||
F.ThisMode = 'strict';
|
||||
} else {
|
||||
F.ThisMode = 'global';
|
||||
}
|
||||
F.IsClassConstructor = Value.false;
|
||||
F.Environment = Scope;
|
||||
F.ScriptOrModule = GetActiveScriptOrModule();
|
||||
F.Realm = surroundingAgent.currentRealmRecord;
|
||||
F.HomeObject = Value.undefined;
|
||||
const len = ExpectedArgumentCount(ParameterList);
|
||||
X(SetFunctionLength(F, new Value(len)));
|
||||
return F;
|
||||
}
|
||||
|
||||
// 9.2.10 #sec-makeconstructor
|
||||
export function MakeConstructor(F, writablePrototype, prototype) {
|
||||
Assert(isECMAScriptFunctionObject(F));
|
||||
Assert(IsConstructor(F) === Value.false);
|
||||
Assert(X(IsExtensible(F)) === Value.true && X(HasOwnProperty(F, new Value('prototype'))) === Value.false);
|
||||
F.Construct = surroundingAgent.hostDefinedOptions.boost
|
||||
? surroundingAgent.hostDefinedOptions.boost.constructFunction
|
||||
: FunctionConstructSlot;
|
||||
F.ConstructorKind = 'base';
|
||||
if (writablePrototype === undefined) {
|
||||
writablePrototype = Value.true;
|
||||
}
|
||||
if (prototype === undefined) {
|
||||
prototype = OrdinaryObjectCreate(surroundingAgent.intrinsic('%Object.prototype%'));
|
||||
X(DefinePropertyOrThrow(prototype, new Value('constructor'), Descriptor({
|
||||
Value: F,
|
||||
Writable: writablePrototype,
|
||||
Enumerable: Value.false,
|
||||
Configurable: Value.true,
|
||||
})));
|
||||
}
|
||||
X(DefinePropertyOrThrow(F, new Value('prototype'), Descriptor({
|
||||
Value: prototype,
|
||||
Writable: writablePrototype,
|
||||
Enumerable: Value.false,
|
||||
Configurable: Value.false,
|
||||
})));
|
||||
return NormalCompletion(Value.undefined);
|
||||
}
|
||||
|
||||
// 9.2.11 #sec-makeclassconstructor
|
||||
export function MakeClassConstructor(F) {
|
||||
Assert(isECMAScriptFunctionObject(F));
|
||||
Assert(F.IsClassConstructor === Value.false);
|
||||
F.IsClassConstructor = Value.true;
|
||||
return NormalCompletion(Value.undefined);
|
||||
}
|
||||
|
||||
// 9.2.12 #sec-makemethod
|
||||
export function MakeMethod(F, homeObject) {
|
||||
Assert(isECMAScriptFunctionObject(F));
|
||||
Assert(Type(homeObject) === 'Object');
|
||||
F.HomeObject = homeObject;
|
||||
return NormalCompletion(Value.undefined);
|
||||
}
|
||||
|
||||
// #sec-setfunctionname
|
||||
export function SetFunctionName(F, name, prefix) {
|
||||
// 1. Assert: F is an extensible object that does not have a "name" own property.
|
||||
Assert(IsExtensible(F) === Value.true && HasOwnProperty(F, new Value('name')) === Value.false);
|
||||
// 2. Assert: Type(name) is either Symbol or String.
|
||||
Assert(Type(name) === 'Symbol' || Type(name) === 'String');
|
||||
// 3. Assert: If prefix is present, then Type(prefix) is String.
|
||||
Assert(!prefix || Type(prefix) === 'String');
|
||||
// 4. If Type(name) is Symbol, then
|
||||
if (Type(name) === 'Symbol') {
|
||||
// a. Let description be name's [[Description]] value.
|
||||
const description = name.Description;
|
||||
// b. If description is undefined, set name to the empty String.
|
||||
if (description === Value.undefined) {
|
||||
name = new Value('');
|
||||
} else {
|
||||
// c. Else, set name to the string-concatenation of "[", description, and "]".
|
||||
name = new Value(`[${description.stringValue()}]`);
|
||||
}
|
||||
}
|
||||
// 5. If F has an [[InitialName]] internal slot, then
|
||||
if ('InitialName' in F) {
|
||||
// a. Set F.[[InitialName]] to name.
|
||||
F.InitialName = name;
|
||||
}
|
||||
// 6. If prefix is present, then
|
||||
if (prefix !== undefined) {
|
||||
// a. Set name to the string-concatenation of prefix, the code unit 0x0020 (SPACE), and name.
|
||||
name = new Value(`${prefix.stringValue()} ${name.stringValue()}`);
|
||||
// b. If F has an [[InitialName]] internal slot, then
|
||||
if ('InitialName' in F) {
|
||||
// i. Optionally, set F.[[InitialName]] to name.
|
||||
}
|
||||
}
|
||||
// 7. Return ! DefinePropertyOrThrow(F, "name", PropertyDescriptor { [[Value]]: name, [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }).
|
||||
return X(DefinePropertyOrThrow(F, new Value('name'), Descriptor({
|
||||
Value: name,
|
||||
Writable: Value.false,
|
||||
Enumerable: Value.false,
|
||||
Configurable: Value.true,
|
||||
})));
|
||||
}
|
||||
|
||||
// 9.2.14 #sec-setfunctionlength
|
||||
export function SetFunctionLength(F, length) {
|
||||
Assert(IsExtensible(F) === Value.true && HasOwnProperty(F, new Value('length')) === Value.false);
|
||||
Assert(Type(length) === 'Number');
|
||||
Assert(length.numberValue() >= 0 && X(IsInteger(length)) === Value.true);
|
||||
return X(DefinePropertyOrThrow(F, new Value('length'), Descriptor({
|
||||
Value: length,
|
||||
Writable: Value.false,
|
||||
Enumerable: Value.false,
|
||||
Configurable: Value.true,
|
||||
})));
|
||||
}
|
||||
|
||||
|
||||
function nativeCall(F, argumentsList, thisArgument, newTarget) {
|
||||
return F.nativeFunction(argumentsList, {
|
||||
thisValue: thisArgument || Value.undefined,
|
||||
NewTarget: newTarget || Value.undefined,
|
||||
});
|
||||
}
|
||||
|
||||
function BuiltinFunctionCall(thisArgument, argumentsList) {
|
||||
const F = this;
|
||||
|
||||
// const callerContext = surroundingAgent.runningExecutionContext;
|
||||
// If callerContext is not already suspended, suspend callerContext.
|
||||
const calleeContext = new ExecutionContext();
|
||||
calleeContext.Function = F;
|
||||
const calleeRealm = F.Realm;
|
||||
calleeContext.Realm = calleeRealm;
|
||||
calleeContext.ScriptOrModule = F.ScriptOrModule;
|
||||
// 8. Perform any necessary implementation-defined initialization of calleeContext.
|
||||
surroundingAgent.executionContextStack.push(calleeContext);
|
||||
const result = nativeCall(F, argumentsList, thisArgument, Value.undefined);
|
||||
// Remove calleeContext from the execution context stack and
|
||||
// restore callerContext as the running execution context.
|
||||
surroundingAgent.executionContextStack.pop(calleeContext);
|
||||
return result;
|
||||
}
|
||||
|
||||
function BuiltinFunctionConstruct(argumentsList, newTarget) {
|
||||
const F = this;
|
||||
|
||||
// const callerContext = surroundingAgent.runningExecutionContext;
|
||||
// If callerContext is not already suspended, suspend callerContext.
|
||||
const calleeContext = new ExecutionContext();
|
||||
calleeContext.Function = F;
|
||||
const calleeRealm = F.Realm;
|
||||
calleeContext.Realm = calleeRealm;
|
||||
calleeContext.ScriptOrModule = F.ScriptOrModule;
|
||||
// 8. Perform any necessary implementation-defined initialization of calleeContext.
|
||||
surroundingAgent.executionContextStack.push(calleeContext);
|
||||
surroundingAgent.runningExecutionContext.callSite.constructCall = true;
|
||||
const result = nativeCall(F, argumentsList, undefined, newTarget);
|
||||
// Remove calleeContext from the execution context stack and
|
||||
// restore callerContext as the running execution context.
|
||||
surroundingAgent.executionContextStack.pop(calleeContext);
|
||||
return result;
|
||||
}
|
||||
|
||||
// 9.3.3 #sec-createbuiltinfunction
|
||||
export function CreateBuiltinFunction(steps, internalSlotsList, realm, prototype, isConstructor = Value.false) {
|
||||
// 1. Assert: steps is either a set of algorithm steps or other definition of a function's behaviour provided in this specification.
|
||||
Assert(typeof steps === 'function');
|
||||
// 2. If realm is not present, set realm to the current Realm Record.
|
||||
if (realm === undefined) {
|
||||
realm = surroundingAgent.currentRealmRecord;
|
||||
}
|
||||
// 3. Assert: realm is a Realm Record.
|
||||
Assert(realm instanceof Realm);
|
||||
// 4. If prototype is not present, set prototype to realm.[[Intrinsics]].[[%Function.prototype%]].
|
||||
if (prototype === undefined) {
|
||||
prototype = realm.Intrinsics['%Function.prototype%'];
|
||||
}
|
||||
// 5. Let func be a new built-in function object that when called performs the action described by steps. The new function object has internal slots whose names are the elements of internalSlotsList.
|
||||
const func = X(MakeBasicObject(['Prototype', 'Extensible', 'Realm', 'ScriptOrModule', 'InitialName'].concat(internalSlotsList)));
|
||||
func.Call = BuiltinFunctionCall;
|
||||
if (isConstructor === Value.true) {
|
||||
func.Construct = BuiltinFunctionConstruct;
|
||||
}
|
||||
func.nativeFunction = steps;
|
||||
// 6. Set func.[[Realm]] to realm.
|
||||
func.Realm = realm;
|
||||
// 7. Set func.[[Prototype]] to prototype.
|
||||
func.Prototype = prototype;
|
||||
// 8. Set func.[[Extensible]] to true.
|
||||
func.Extensible = Value.true;
|
||||
// 9. Set func.[[ScriptOrModule]] to null.
|
||||
func.ScriptOrModule = Value.null;
|
||||
// 10. Set func.[[InitialName]] to null.
|
||||
func.InitialName = Value.null;
|
||||
// 11. Return func.
|
||||
return func;
|
||||
}
|
||||
|
||||
// 14.9.3 #sec-preparefortailcall
|
||||
export function PrepareForTailCall() {
|
||||
// 1. Let leafContext be the running execution context.
|
||||
const leafContext = surroundingAgent.runningExecutionContext;
|
||||
// 2. Suspend leafContext.
|
||||
// 3. Pop leafContext from the execution context stack. The execution context now on the top of the stack becomes the running execution context.
|
||||
surroundingAgent.executionContextStack.pop(leafContext);
|
||||
// 4. Assert: leafContext has no further use. It will never be activated as the running execution context.
|
||||
leafContext.poppedForTailCall = true;
|
||||
}
|
||||
@@ -1,200 +0,0 @@
|
||||
import {
|
||||
Completion,
|
||||
NormalCompletion,
|
||||
Q, X,
|
||||
EnsureCompletion,
|
||||
} from '../completion.mjs';
|
||||
import { surroundingAgent } from '../engine.mjs';
|
||||
import { Value } from '../value.mjs';
|
||||
import { Evaluate } from '../evaluator.mjs';
|
||||
import { resume } from '../helpers.mjs';
|
||||
import {
|
||||
Assert,
|
||||
CreateIterResultObject,
|
||||
RequireInternalSlot,
|
||||
} from './all.mjs';
|
||||
|
||||
// This file covers abstract operations defined in #sec-generator-objects
|
||||
|
||||
// #sec-generatorstart
|
||||
export function GeneratorStart(generator, generatorBody) {
|
||||
// 1. Assert: The value of generator.[[GeneratorState]] is undefined.
|
||||
Assert(generator.GeneratorState === Value.undefined);
|
||||
// 2. Let genContext be the running execution context.
|
||||
const genContext = surroundingAgent.runningExecutionContext;
|
||||
// 3. Set the Generator component of genContext to generator.
|
||||
genContext.Generator = generator;
|
||||
// 4. Set the code evaluation state of genContext such that when evaluation is resumed
|
||||
// for that execution context the following steps will be performed:
|
||||
genContext.codeEvaluationState = (function* resumer() {
|
||||
// a. Let result be the result of evaluating generatorBody.
|
||||
const result = EnsureCompletion(yield* Evaluate(generatorBody));
|
||||
// b. Assert: If we return here, the generator either threw an exception or
|
||||
// performed either an implicit or explicit return.
|
||||
// c. Remove genContext from the execution context stack and restore the execution context
|
||||
// that is at the top of the execution context stack as the running execution context.
|
||||
surroundingAgent.executionContextStack.pop(genContext);
|
||||
// d. Set generator.[[GeneratorState]] to completed.
|
||||
generator.GeneratorState = 'completed';
|
||||
// e. Once a generator enters the completed state it never leaves it and its
|
||||
// associated execution context is never resumed. Any execution state associated
|
||||
// with generator can be discarded at this point.
|
||||
genContext.codeEvaluationState = null;
|
||||
// f. If result.[[Type]] is normal, let resultValue be undefined.
|
||||
let resultValue;
|
||||
if (result.Type === 'normal') {
|
||||
resultValue = Value.undefined;
|
||||
} else if (result.Type === 'return') {
|
||||
// g. Else if result.[[Type]] is return, let resultValue be result.[[Value]].
|
||||
resultValue = result.Value;
|
||||
} else {
|
||||
// i. Assert: result.[[Type]] is throw.
|
||||
Assert(result.Type === 'throw');
|
||||
// ii. Return Completion(result).
|
||||
return Completion(result);
|
||||
}
|
||||
// i. Return CreateIterResultObject(resultValue, true).
|
||||
return X(CreateIterResultObject(resultValue, Value.true));
|
||||
}());
|
||||
// 5. Set generator.[[GeneratorContext]] to genContext.
|
||||
generator.GeneratorContext = genContext;
|
||||
// 6. Set generator.[[GeneratorState]] to suspendedStart.
|
||||
generator.GeneratorState = 'suspendedStart';
|
||||
// 7. Return NormalCompletion(undefined).
|
||||
return NormalCompletion(Value.undefined);
|
||||
}
|
||||
|
||||
// #sec-generatorvalidate
|
||||
export function GeneratorValidate(generator) {
|
||||
// 1. Perform ? RequireInternalSlot(generator, [[GeneratorState]]).
|
||||
Q(RequireInternalSlot(generator, 'GeneratorState'));
|
||||
// 2. Assert: generator also has a [[GeneratorContext]] internal slot.
|
||||
Assert('GeneratorContext' in generator);
|
||||
// 3. Let state be generator.[[GeneratorState]].
|
||||
const state = generator.GeneratorState;
|
||||
// 4. If state is executing, throw a TypeError exception.
|
||||
if (state === 'executing') {
|
||||
return surroundingAgent.Throw('TypeError', 'GeneratorRunning');
|
||||
}
|
||||
// 5. Return state.
|
||||
return state;
|
||||
}
|
||||
|
||||
// #sec-generatorresume
|
||||
export function GeneratorResume(generator, value) {
|
||||
// 1. Let state be ? GeneratorValidate(generator).
|
||||
const state = Q(GeneratorValidate(generator));
|
||||
// 2. If state is completed, return CreateIterResultObject(undefined, true).
|
||||
if (state === 'completed') {
|
||||
return X(CreateIterResultObject(Value.undefined, Value.true));
|
||||
}
|
||||
// 3. Assert: state is either suspendedStart or suspendedYield.
|
||||
Assert(state === 'suspendedStart' || state === 'suspendedYield');
|
||||
// 4. Let genContext be generator.[[GeneratorContext]].
|
||||
const genContext = generator.GeneratorContext;
|
||||
// 5. Let methodContext be the running execution context.
|
||||
// 6. Suspend methodContext.
|
||||
const methodContext = surroundingAgent.runningExecutionContext;
|
||||
// 7. Set generator.[[GeneratorState]] to executing.
|
||||
generator.GeneratorState = 'executing';
|
||||
// 8. Push genContext onto the execution context stack.
|
||||
surroundingAgent.executionContextStack.push(genContext);
|
||||
// 9. Resume the suspended evaluation of genContext using NormalCompletion(value) as
|
||||
// the result of the operation that suspended it. Let result be the value returned by
|
||||
// the resumed computation.
|
||||
const result = EnsureCompletion(resume(genContext, NormalCompletion(value)));
|
||||
// 10. Assert: When we return here, genContext has already been removed from the execution
|
||||
// context stack and methodContext is the currently running execution context.
|
||||
Assert(surroundingAgent.runningExecutionContext === methodContext);
|
||||
// 11. Return Completion(result).
|
||||
return Completion(result);
|
||||
}
|
||||
|
||||
// #sec-generatorresumeabrupt
|
||||
export function GeneratorResumeAbrupt(generator, abruptCompletion) {
|
||||
// 1. Let state be ? GeneratorValidate(generator).
|
||||
let state = Q(GeneratorValidate(generator));
|
||||
// 2. If state is suspendedStart, then
|
||||
if (state === 'suspendedStart') {
|
||||
// a. Set generator.[[GeneratorState]] to completed.
|
||||
generator.GeneratorState = 'completed';
|
||||
// b. Once a generator enters the completed state it never leaves it and its
|
||||
// associated execution context is never resumed. Any execution state associate
|
||||
// with generator can be discarded at this point.
|
||||
generator.GeneratorContext = null;
|
||||
// c. Set state to completed.
|
||||
state = 'completed';
|
||||
}
|
||||
// 3. If state is completed, then
|
||||
if (state === 'completed') {
|
||||
// a. If abruptCompletion.[[Type]] is return, then
|
||||
if (abruptCompletion.Type === 'return') {
|
||||
// i. Return CreateIterResultObject(abruptCompletion.[[Value]], true).
|
||||
return X(CreateIterResultObject(abruptCompletion.Value, Value.true));
|
||||
}
|
||||
// b. Return Completion(abruptCompletion).
|
||||
return Completion(abruptCompletion);
|
||||
}
|
||||
// 4. Assert: state is suspendedYield.
|
||||
Assert(state === 'suspendedYield');
|
||||
// 5. Let genContext be generator.[[GeneratorContext]].
|
||||
const genContext = generator.GeneratorContext;
|
||||
// 6. Let methodContext be the running execution context.
|
||||
// 7. Suspend methodContext.
|
||||
const methodContext = surroundingAgent.runningExecutionContext;
|
||||
// 8. Set generator.[[GeneratorState]] to executing.
|
||||
generator.GeneratorState = 'executing';
|
||||
// 9. Push genContext onto the execution context stack.
|
||||
surroundingAgent.executionContextStack.push(genContext);
|
||||
// 10. Resume the suspended evaluation of genContext using abruptCompletion as the
|
||||
// result of the operation that suspended it. Let result be the completion record
|
||||
// returned by the resumed computation.
|
||||
const result = EnsureCompletion(resume(genContext, abruptCompletion));
|
||||
// 11. Assert: When we return here, genContext has already been removed from the
|
||||
// execution context stack and methodContext is the currently running execution context.
|
||||
Assert(surroundingAgent.runningExecutionContext === methodContext);
|
||||
// 12. Return Completion(result).
|
||||
return Completion(result);
|
||||
}
|
||||
|
||||
// #sec-getgeneratorkind
|
||||
export function GetGeneratorKind() {
|
||||
// 1. Let genContext be the running execution context.
|
||||
const genContext = surroundingAgent.runningExecutionContext;
|
||||
// 2. If genContext does not have a Generator component, return non-generator.
|
||||
if (!genContext.Generator) {
|
||||
return 'non-generator';
|
||||
}
|
||||
// 3. Let generator be the Generator component of genContext.
|
||||
const generator = genContext.Generator;
|
||||
// 4. If generator has an [[AsyncGeneratorState]] internal slot, return async.
|
||||
if ('AsyncGeneratorState' in generator) {
|
||||
return 'async';
|
||||
}
|
||||
// 5. Else, return sync.
|
||||
return 'sync';
|
||||
}
|
||||
|
||||
// #sec-generatoryield
|
||||
export function* GeneratorYield(iterNextObj) {
|
||||
// 1. Assert: iterNextObj is an Object that implements the IteratorResult interface.
|
||||
// 2. Let genContext be the running execution context.
|
||||
const genContext = surroundingAgent.runningExecutionContext;
|
||||
// 3. Assert: genContext is the execution context of a generator.
|
||||
Assert(genContext.Generator !== undefined);
|
||||
// 4. Let generator be the value of the Generator component of genContext.
|
||||
const generator = genContext.Generator;
|
||||
// 5. Assert: GetGeneratorKind is sync.
|
||||
Assert(GetGeneratorKind() === 'sync');
|
||||
// 6. Set generator.GeneratorState to suspendedYield.
|
||||
generator.GeneratorState = 'suspendedYield';
|
||||
// 7. Remove genContext from the execution context stack.
|
||||
surroundingAgent.executionContextStack.pop(genContext);
|
||||
// 8. Set the code evaluation state of genContext such that when evaluation is resumed with
|
||||
// a Completion resumptionValue the following steps will be performed:
|
||||
// a. Return resumptionValue
|
||||
const resumptionValue = yield NormalCompletion(iterNextObj);
|
||||
// 9. Return NormalCompletion(iterNextObj).
|
||||
return resumptionValue;
|
||||
// 10. NOTE: this returns to the evaluation of the operation that had most previously resumed evaluation of genContext.
|
||||
}
|
||||
@@ -1,332 +0,0 @@
|
||||
import { ExecutionContext, HostEnsureCanCompileStrings, surroundingAgent } from '../engine.mjs';
|
||||
import { Type, Value } from '../value.mjs';
|
||||
import { InstantiateFunctionObject } from '../runtime-semantics/all.mjs';
|
||||
import {
|
||||
IsStrict,
|
||||
VarDeclaredNames,
|
||||
VarScopedDeclarations,
|
||||
LexicallyScopedDeclarations,
|
||||
BoundNames,
|
||||
IsConstantDeclaration,
|
||||
} from '../static-semantics/all.mjs';
|
||||
import {
|
||||
Completion,
|
||||
AbruptCompletion,
|
||||
NormalCompletion,
|
||||
EnsureCompletion,
|
||||
Q, X,
|
||||
} from '../completion.mjs';
|
||||
import { wrappedParse } from '../parse.mjs';
|
||||
import {
|
||||
NewDeclarativeEnvironment,
|
||||
FunctionEnvironmentRecord,
|
||||
GlobalEnvironmentRecord,
|
||||
ObjectEnvironmentRecord,
|
||||
} from '../environment.mjs';
|
||||
import { Evaluate } from '../evaluator.mjs';
|
||||
import { unwind, ValueSet } from '../helpers.mjs';
|
||||
import { Assert, GetThisEnvironment } from './all.mjs';
|
||||
|
||||
// This file covers abstract operations defined in
|
||||
// 18 #sec-global-object
|
||||
|
||||
// 2.1.1 #sec-performeval
|
||||
export function PerformEval(x, callerRealm, strictCaller, direct) {
|
||||
// 1. Assert: If direct is false, then strictCaller is also false.
|
||||
if (direct === false) {
|
||||
Assert(strictCaller === false);
|
||||
}
|
||||
// 2. If Type(x) is not String, return x.
|
||||
if (Type(x) !== 'String') {
|
||||
return x;
|
||||
}
|
||||
// 3. Let evalRealm be the current Realm Record.
|
||||
const evalRealm = surroundingAgent.currentRealmRecord;
|
||||
// 4. Perform ? HostEnsureCanCompileStrings(callerRealm, evalRealm).
|
||||
Q(HostEnsureCanCompileStrings(callerRealm, evalRealm));
|
||||
// 5. Let inFunction be false.
|
||||
let inFunction = false;
|
||||
// 6. Let inMethod be false.
|
||||
let inMethod = false;
|
||||
// 7. Let inDerivedConstructor be false.
|
||||
let inDerivedConstructor = false;
|
||||
// 8. If direct is true, then
|
||||
if (direct === true) {
|
||||
// a. Let thisEnv be ! GetThisEnvironment().
|
||||
const thisEnv = X(GetThisEnvironment());
|
||||
// b. If thisEnv is a function Environment Record, then
|
||||
if (thisEnv instanceof FunctionEnvironmentRecord) {
|
||||
// i. Let F be thisEnv.[[FunctionObject]].
|
||||
const F = thisEnv.FunctionObject;
|
||||
// ii. Let inFunction be true.
|
||||
inFunction = true;
|
||||
// iii. Let inMethod be thisEnv.HasSuperBinding().
|
||||
inMethod = thisEnv.HasSuperBinding() === Value.true;
|
||||
// iv. If F.[[ConstructorKind]] is derived, set inDerivedConstructor to true.
|
||||
if (F.ConstructorKind === 'derived') {
|
||||
inDerivedConstructor = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 9. Perform the following substeps in an implementation-dependent order, possibly interleaving parsing and error detection:
|
||||
// a. Let script be the ECMAScript code that is the result of parsing ! UTF16DecodeString(x), for the goal symbol Script.
|
||||
// b. If script Contains ScriptBody is false, return undefined.
|
||||
// c. Let body be the ScriptBody of script.
|
||||
// d. If inFunction is false, and body Contains NewTarget, throw a SyntaxError exception.
|
||||
// e. If inMethod is false, and body Contains SuperProperty, throw a SyntaxError exception.
|
||||
// f. If inDerivedConstructor is false, and body Contains SuperCall, throw a SyntaxError exception.
|
||||
const script = wrappedParse({ source: x.stringValue() }, (parser) => parser.scope.with({
|
||||
strict: strictCaller,
|
||||
newTarget: inFunction,
|
||||
superProperty: inMethod,
|
||||
superCall: inDerivedConstructor,
|
||||
}, () => parser.parseScript()));
|
||||
if (Array.isArray(script)) {
|
||||
return surroundingAgent.Throw(script[0]);
|
||||
}
|
||||
if (!script.ScriptBody) {
|
||||
return Value.undefined;
|
||||
}
|
||||
const body = script.ScriptBody;
|
||||
// 10. If strictCaller is true, let strictEval be true.
|
||||
// 11. Else, let strictEval be IsStrict of script.
|
||||
let strictEval;
|
||||
if (strictCaller === true) {
|
||||
strictEval = true;
|
||||
} else {
|
||||
strictEval = IsStrict(script);
|
||||
}
|
||||
// 12. Let runningContext be the running execution context.
|
||||
const runningContext = surroundingAgent.runningExecutionContext;
|
||||
let lexEnv;
|
||||
let varEnv;
|
||||
// 13. NOTE: If direct is true, runningContext will be the execution context that performed the direct eval.
|
||||
// If direct is false, runningContext will be the execution context for the invocation of the eval function.
|
||||
// 14. If direct is true, then
|
||||
if (direct === true) {
|
||||
// a. Let lexEnv be NewDeclarativeEnvironment(runningContext's LexicalEnvironment).
|
||||
lexEnv = NewDeclarativeEnvironment(runningContext.LexicalEnvironment);
|
||||
// b. Let varEnv be runningContext's VariableEnvironment.
|
||||
varEnv = runningContext.VariableEnvironment;
|
||||
} else { // 15. Else,
|
||||
// a. Let lexEnv be NewDeclarativeEnvironment(evalRealm.[[GlobalEnv]]).
|
||||
lexEnv = NewDeclarativeEnvironment(evalRealm.GlobalEnv);
|
||||
// b. Let varEnv be evalRealm.[[GlobalEnv]].
|
||||
varEnv = evalRealm.GlobalEnv;
|
||||
}
|
||||
// 16. If strictEval is true, set varEnv to lexEnv.
|
||||
if (strictEval === true) {
|
||||
varEnv = lexEnv;
|
||||
}
|
||||
// 17. If runningContext is not already suspended, suspend runningContext.
|
||||
// 18. Let evalContext be a new ECMAScript code execution context.
|
||||
const evalContext = new ExecutionContext();
|
||||
// 19. Set evalContext's Function to null.
|
||||
evalContext.Function = Value.null;
|
||||
// 20. Set evalContext's Realm to evalRealm.
|
||||
evalContext.Realm = evalRealm;
|
||||
// 21. Set evalContext's ScriptOrModule to runningContext's ScriptOrModule.
|
||||
evalContext.ScriptOrModule = runningContext.ScriptOrModule;
|
||||
// 22. Set evalContext's VariableEnvironment to varEnv.
|
||||
evalContext.VariableEnvironment = varEnv;
|
||||
// 23. Set evalContext's LexicalEnvironment to lexEnv.
|
||||
evalContext.LexicalEnvironment = lexEnv;
|
||||
// 24. Push evalContext onto the execution context stack.
|
||||
surroundingAgent.executionContextStack.push(evalContext);
|
||||
// 25. Let result be EvalDeclarationInstantiation(body, varEnv, lexEnv, strictEval).
|
||||
let result = EnsureCompletion(EvalDeclarationInstantiation(body, varEnv, lexEnv, strictEval));
|
||||
// 26. If result.[[Type]] is normal, then
|
||||
if (result.Type === 'normal') {
|
||||
// a. Set result to the result of evaluating body.
|
||||
result = EnsureCompletion(unwind(Evaluate(body)));
|
||||
}
|
||||
// 27. If result.[[Type]] is normal and result.[[Value]] is empty, then
|
||||
if (result.Type === 'normal' && result.Value === undefined) {
|
||||
// a. Set result to NormalCompletion(undefined).
|
||||
result = NormalCompletion(Value.undefined);
|
||||
}
|
||||
// 28. Suspend evalContext and remove it from the execution context stack.
|
||||
// 29. Resume the context that is now on the top of the execution context stack as the running execution context.
|
||||
surroundingAgent.executionContextStack.pop(evalContext);
|
||||
// 30. Return Completion(result).
|
||||
return Completion(result);
|
||||
}
|
||||
|
||||
// 18.2.1.3 #sec-evaldeclarationinstantiation
|
||||
function EvalDeclarationInstantiation(body, varEnv, lexEnv, strict) {
|
||||
// 1. Let varNames be the VarDeclaredNames of body.
|
||||
const varNames = VarDeclaredNames(body);
|
||||
// 2. Let varDeclarations be the VarScopedDeclarations of body.
|
||||
const varDeclarations = VarScopedDeclarations(body);
|
||||
// 3. If strict is false, then
|
||||
if (strict === false) {
|
||||
// a. If varEnv is a global Environment Record, then
|
||||
if (varEnv instanceof GlobalEnvironmentRecord) {
|
||||
// i. For each name in varNames, do
|
||||
for (const name of varNames) {
|
||||
// 1. If varEnv.HasLexicalDeclaration(name) is true, throw a SyntaxError exception.
|
||||
if (varEnv.HasLexicalDeclaration(name) === Value.true) {
|
||||
return surroundingAgent.Throw('SyntaxError', 'AlreadyDeclared', name);
|
||||
}
|
||||
// 2. NOTE: eval will not create a global var declaration that would be shadowed by a global lexical declaration.
|
||||
}
|
||||
}
|
||||
// b. Let thisLex be lexEnv.
|
||||
let thisEnv = lexEnv;
|
||||
// c. Assert: The following loop will terminate.
|
||||
// d. Repeat, while thisEnv is not the same as varEnv,
|
||||
while (thisEnv !== varEnv) {
|
||||
// i. If thisEnv is not an object Environment Record, then
|
||||
if (!(thisEnv instanceof ObjectEnvironmentRecord)) {
|
||||
// 1. NOTE: The environment of with statements cannot contain any lexical declaration so it doesn't need to be checked for var/let hoisting conflicts.
|
||||
// 2. For each name in varNames, do
|
||||
for (const name of varNames) {
|
||||
// a. If thisEnv.HasBinding(name) is true, then
|
||||
if (thisEnv.HasBinding(name) === Value.true) {
|
||||
// i. Throw a SyntaxError exception.
|
||||
return surroundingAgent.Throw('SyntaxError', 'AlreadyDeclared', name);
|
||||
// ii. NOTE: Annex B.3.5 defines alternate semantics for the above step.
|
||||
}
|
||||
// b. NOTE: A direct eval will not hoist var declaration over a like-named lexical declaration
|
||||
}
|
||||
}
|
||||
// ii. Set thisEnv to thisEnv.[[OuterEnv]].
|
||||
thisEnv = thisEnv.OuterEnv;
|
||||
}
|
||||
}
|
||||
// 4. Let functionsToInitialize be a new empty List.
|
||||
const functionsToInitialize = [];
|
||||
// 5. Let declaredFunctionNames be a new empty List.
|
||||
const declaredFunctionNames = new ValueSet();
|
||||
// 6. For each d in varDeclarations, in reverse list order, do
|
||||
for (const d of [...varDeclarations].reverse()) {
|
||||
// a. If d is neither a VariableDeclaration nor a ForBinding nor a BindingIdentifier, then
|
||||
if (d.type !== 'VariableDeclaration'
|
||||
&& d.type !== 'ForBinding'
|
||||
&& d.type !== 'BindingIdentifier') {
|
||||
// i. Assert: d is either a FunctionDeclaration, a GeneratorDeclaration, an AsyncFunctionDeclaration, or an AsyncGeneratorDeclaration.
|
||||
Assert(d.type === 'FunctionDeclaration'
|
||||
|| d.type === 'GeneratorDeclaration'
|
||||
|| d.type === 'AsyncFunctionDeclaration'
|
||||
|| d.type === 'AsyncGeneratorDeclaration');
|
||||
// ii. NOTE: If there are multiple function declarations for the same name, the last declaration is used.
|
||||
// iii. Let fn be the sole element of the BoundNames of d.
|
||||
const fn = BoundNames(d)[0];
|
||||
// iv. If fn is not an element of declaredFunctionNames, then
|
||||
if (!declaredFunctionNames.has(fn)) {
|
||||
// 1. If varEnv is a global Environment Record, then
|
||||
if (varEnv instanceof GlobalEnvironmentRecord) {
|
||||
// a. Let fnDefinable be ? varEnv.CanDeclareGlobalFunction(fn).
|
||||
const fnDefinable = Q(varEnv.CanDeclareGlobalFunction(fn));
|
||||
// b. Let fnDefinable be ? varEnv.CanDeclareGlobalFunction(fn).
|
||||
if (fnDefinable === Value.false) {
|
||||
return surroundingAgent.Throw('TypeError', 'AlreadyDeclared', fn);
|
||||
}
|
||||
}
|
||||
// 2. Append fn to declaredFunctionNames.
|
||||
declaredFunctionNames.add(fn);
|
||||
// 3. Insert d as the first element of functionsToInitialize.
|
||||
functionsToInitialize.unshift(d);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 7. NOTE: Annex B.3.3.3 adds additional steps at this point.
|
||||
// 8. Let declaredVarNames be a new empty List.
|
||||
const declaredVarNames = new ValueSet();
|
||||
// 9. For each d in varDeclarations, do
|
||||
for (const d of varDeclarations) {
|
||||
// a. If d is a VariableDeclaration, a ForBinding, or a BindingIdentifier, then
|
||||
if (d.type === 'VariableDeclaration'
|
||||
|| d.type === 'ForBinding'
|
||||
|| d.type === 'BindingIdentifier') {
|
||||
// i. For each String vn in the BoundNames of d, do
|
||||
for (const vn of BoundNames(d)) {
|
||||
// 1. If vn is not an element of declaredFunctionNames, then
|
||||
if (!declaredFunctionNames.has(vn)) {
|
||||
// a. If varEnv is a global Environment Record, then
|
||||
if (varEnv instanceof GlobalEnvironmentRecord) {
|
||||
// i. Let vnDefinable be ? varEnv.CanDeclareGlobalVar(vn).
|
||||
const vnDefinable = Q(varEnv.CanDeclareGlobalVar(vn));
|
||||
// ii. If vnDefinable is false, throw a TypeError exception.
|
||||
if (vnDefinable === Value.false) {
|
||||
return surroundingAgent.Throw('TypeError', 'AlreadyDeclared', vn);
|
||||
}
|
||||
}
|
||||
// b. If vn is not an element of declaredVarNames, then
|
||||
if (!declaredVarNames.has(vn)) {
|
||||
// i. Append vn to declaredVarNames.
|
||||
declaredVarNames.add(vn);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 10. NOTE: No abnormal terminations occur after this algorithm step unless
|
||||
// varEnv is a global Environment Record and the global object is a Proxy exotic object.
|
||||
// 11. Let lexDeclarations be the LexicallyScopedDeclarations of body.
|
||||
const lexDeclarations = LexicallyScopedDeclarations(body);
|
||||
// 12. For each element d in lexDeclarations, do
|
||||
for (const d of lexDeclarations) {
|
||||
// a. NOTE: Lexically declared names are only instantiated here but not initialized.
|
||||
// b. For each element dn of the BoundNames of d, do
|
||||
for (const dn of BoundNames(d)) {
|
||||
// i. If IsConstantDeclaration of d is true, then
|
||||
if (IsConstantDeclaration(d)) {
|
||||
// 1. Perform ? lexEnv.CreateImmutableBinding(dn, true).
|
||||
Q(lexEnv.CreateImmutableBinding(dn, Value.true));
|
||||
} else { // ii. Else,
|
||||
// 1. Perform ? lexEnv.CreateMutableBinding(dn, false).
|
||||
Q(lexEnv.CreateMutableBinding(dn, Value.false));
|
||||
}
|
||||
}
|
||||
}
|
||||
// 13. For each Parse Node f in functionsToInitialize, do
|
||||
for (const f of functionsToInitialize) {
|
||||
// a. Let fn be the sole element of the BoundNames of f.
|
||||
const fn = BoundNames(f)[0];
|
||||
// b. Let fn be the sole element of the BoundNames of f.
|
||||
const fo = InstantiateFunctionObject(f, lexEnv);
|
||||
// c. If varEnv is a global Environment Record, then
|
||||
if (varEnv instanceof GlobalEnvironmentRecord) {
|
||||
// i. Perform ? varEnv.CreateGlobalFunctionBinding(fn, fo, true).
|
||||
Q(varEnv.CreateGlobalFunctionBinding(fn, fo, Value.true));
|
||||
} else { // d. Else,
|
||||
// i. Let bindingExists be varEnv.HasBinding(fn).
|
||||
const bindingExists = varEnv.HasBinding(fn);
|
||||
// ii. If bindingExists is false, then
|
||||
if (bindingExists === Value.false) {
|
||||
// 1. Let status be ! varEnv.CreateMutableBinding(fn, true).
|
||||
const status = X(varEnv.CreateMutableBinding(fn, Value.true));
|
||||
// 2. Assert: status is not an abrupt completion because of validation preceding step 12.
|
||||
Assert(!(status instanceof AbruptCompletion));
|
||||
// 3. Perform ! varEnv.InitializeBinding(fn, fo).
|
||||
X(varEnv.InitializeBinding(fn, fo));
|
||||
} else { // iii. Else,
|
||||
// 1. Perform ! varEnv.SetMutableBinding(fn, fo, false).
|
||||
X(varEnv.SetMutableBinding(fn, fo, Value.false));
|
||||
}
|
||||
}
|
||||
}
|
||||
// 14. For each String vn in declaredVarNames, in list order, do
|
||||
for (const vn of declaredVarNames) {
|
||||
// a. If varEnv is a global Environment Record, then
|
||||
if (varEnv instanceof GlobalEnvironmentRecord) {
|
||||
// i. Perform ? varEnv.CreateGlobalVarBinding(vn, true).
|
||||
Q(varEnv.CreateGlobalVarBinding(vn, Value.true));
|
||||
} else { // b. Else,
|
||||
// i. Let bindingExists be varEnv.HasBinding(vn).
|
||||
const bindingExists = varEnv.HasBinding(vn);
|
||||
// ii. If bindingExists is false, then
|
||||
if (bindingExists === Value.false) {
|
||||
// 1. Let status be ! varEnv.CreateMutableBinding(vn, true).
|
||||
const status = X(varEnv.CreateMutableBinding(vn, Value.true));
|
||||
// 2. Assert: status is not an abrupt completion because of validation preceding step 12.
|
||||
Assert(!(status instanceof AbruptCompletion));
|
||||
// 3. Perform ! varEnv.InitializeBinding(vn, undefined).
|
||||
X(varEnv.InitializeBinding(vn, Value.undefined));
|
||||
}
|
||||
}
|
||||
}
|
||||
// 15. Return NormalCompletion(empty).
|
||||
return NormalCompletion(undefined);
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
import { Type, Value } from '../value.mjs';
|
||||
import { Q } from '../completion.mjs';
|
||||
import { Assert, SameValue } from './all.mjs';
|
||||
|
||||
// #sec-set-immutable-prototype
|
||||
export function SetImmutablePrototype(O, V) {
|
||||
// 1. Assert: Either Type(V) is Object or Type(V) is Null.
|
||||
Assert(Type(V) === 'Object' || Type(V) === 'Null');
|
||||
// 2. Let current be ? O.[[GetPrototypeOf]]().
|
||||
const current = Q(O.GetPrototypeOf());
|
||||
// 3. If SameValue(V, current) is true, return true.
|
||||
if (SameValue(V, current) === Value.true) {
|
||||
return Value.true;
|
||||
}
|
||||
// 4. Return false.
|
||||
return Value.false;
|
||||
}
|
||||
@@ -1,314 +0,0 @@
|
||||
import { surroundingAgent } from '../engine.mjs';
|
||||
import { Value, Type, Descriptor } from '../value.mjs';
|
||||
import { Q, X } from '../completion.mjs';
|
||||
import {
|
||||
Assert,
|
||||
CanonicalNumericIndexString,
|
||||
IsAccessorDescriptor,
|
||||
IsDetachedBuffer,
|
||||
IsPropertyKey,
|
||||
IsValidIntegerIndex,
|
||||
MakeBasicObject,
|
||||
OrdinaryGetOwnProperty,
|
||||
OrdinaryHasProperty,
|
||||
OrdinaryDefineOwnProperty,
|
||||
OrdinaryGet,
|
||||
OrdinarySet,
|
||||
GetValueFromBuffer,
|
||||
SetValueInBuffer,
|
||||
ToString,
|
||||
ToNumber,
|
||||
ToBigInt,
|
||||
isIntegerIndex,
|
||||
typedArrayInfoByName,
|
||||
} from './all.mjs';
|
||||
|
||||
export function isIntegerIndexedExoticObject(O) {
|
||||
return O.GetOwnProperty === IntegerIndexedGetOwnProperty;
|
||||
}
|
||||
|
||||
// 9.4.5.1 #sec-integer-indexed-exotic-objects-getownproperty-p
|
||||
export function IntegerIndexedGetOwnProperty(P) {
|
||||
const O = this;
|
||||
// 1. Assert: IsPropertyKey(P) is true.
|
||||
Assert(IsPropertyKey(P));
|
||||
// 2. Assert: O is an Integer-Indexed exotic object.
|
||||
Assert(isIntegerIndexedExoticObject(O));
|
||||
// 3. If Type(P) is String, then
|
||||
if (Type(P) === 'String') {
|
||||
// a. Let numericIndex be ! CanonicalNumericIndexString(P).
|
||||
const numericIndex = X(CanonicalNumericIndexString(P));
|
||||
// b. If numericIndex is not undefined, then
|
||||
if (numericIndex !== Value.undefined) {
|
||||
// i. Let value be ? IntegerIndexedElementGet(O, numericIndex).
|
||||
const value = Q(IntegerIndexedElementGet(O, numericIndex));
|
||||
// ii. If value is undefined, return undefined.
|
||||
if (value === Value.undefined) {
|
||||
return Value.undefined;
|
||||
}
|
||||
// iii. Return the PropertyDescriptor { [[Value]]: value, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: false }.
|
||||
return Descriptor({
|
||||
Value: value,
|
||||
Writable: Value.true,
|
||||
Enumerable: Value.true,
|
||||
Configurable: Value.false,
|
||||
});
|
||||
}
|
||||
}
|
||||
// 4. Return OrdinaryGetOwnProperty(O, P).
|
||||
return OrdinaryGetOwnProperty(O, P);
|
||||
}
|
||||
|
||||
// 9.4.5.2 #sec-integer-indexed-exotic-objects-hasproperty-p
|
||||
export function IntegerIndexedHasProperty(P) {
|
||||
const O = this;
|
||||
// 1. Assert: IsPropertyKey(P) is true.
|
||||
Assert(IsPropertyKey(P));
|
||||
// 2. Assert: O is an Integer-Indexed exotic object.
|
||||
Assert(isIntegerIndexedExoticObject(O));
|
||||
// 3. If Type(P) is String, then
|
||||
if (Type(P) === 'String') {
|
||||
// a. Let numericIndex be ! CanonicalNumericIndexString(P).
|
||||
const numericIndex = X(CanonicalNumericIndexString(P));
|
||||
// b. If numericIndex is not undefined, then
|
||||
if (numericIndex !== Value.undefined) {
|
||||
// i. Let buffer be O.[[ViewedArrayBuffer]].
|
||||
const buffer = O.ViewedArrayBuffer;
|
||||
// ii. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
|
||||
if (IsDetachedBuffer(buffer) === Value.true) {
|
||||
return surroundingAgent.Throw('TypeError', 'ArrayBufferDetached');
|
||||
}
|
||||
// iii. If ! IsValidIntegerIndex(O, numericIndex) is false, return false.
|
||||
if (IsValidIntegerIndex(O, numericIndex) === Value.false) {
|
||||
return Value.false;
|
||||
}
|
||||
// iv. Return true.
|
||||
return Value.true;
|
||||
}
|
||||
}
|
||||
// 4. Return ? OrdinaryHasProperty(O, P)
|
||||
return Q(OrdinaryHasProperty(O, P));
|
||||
}
|
||||
|
||||
// #sec-integer-indexed-exotic-objects-defineownproperty-p-desc
|
||||
export function IntegerIndexedDefineOwnProperty(P, Desc) {
|
||||
const O = this;
|
||||
// 1. Assert: IsPropertyKey(P) is true.
|
||||
Assert(IsPropertyKey(P));
|
||||
// 2. Assert: O is an Integer-Indexed exotic object.
|
||||
Assert(isIntegerIndexedExoticObject(O));
|
||||
// 3. If Type(P) is String, then
|
||||
if (Type(P) === 'String') {
|
||||
// a. Let numericIndex be ! CanonicalNumericIndexString(P).
|
||||
const numericIndex = X(CanonicalNumericIndexString(P));
|
||||
// b. If numericIndex is not undefined, then
|
||||
if (numericIndex !== Value.undefined) {
|
||||
// i. If ! IsValidIntegerIndex(O, numericIndex) is false, return false.
|
||||
if (IsValidIntegerIndex(O, numericIndex) === Value.false) {
|
||||
return Value.false;
|
||||
}
|
||||
// ii. If IsAccessorDescriptor(Desc) is true, return false.
|
||||
if (IsAccessorDescriptor(Desc)) {
|
||||
return Value.false;
|
||||
}
|
||||
// iii. If Desc has a [[Configurable]] field and if Desc.[[Configurable]] is true, return false.
|
||||
if (Desc.Configurable === Value.true) {
|
||||
return Value.false;
|
||||
}
|
||||
// iv. If Desc has an [[Enumerable]] field and if Desc.[[Enumerable]] is false, return false.
|
||||
if (Desc.Enumerable === Value.false) {
|
||||
return Value.false;
|
||||
}
|
||||
// v. If Desc has a [[Writable]] field and if Desc.[[Writable]] is false, return false.
|
||||
if (Desc.Writable === Value.false) {
|
||||
return Value.false;
|
||||
}
|
||||
// vi. If Desc has a [[Value]] field, then
|
||||
if (Desc.Value !== undefined) {
|
||||
// 1. Let value be Desc.[[Value]].
|
||||
const value = Desc.Value;
|
||||
// 2. Return ? IntegerIndexedElementSet(O, numericIndex, value).
|
||||
return Q(IntegerIndexedElementSet(O, numericIndex, value));
|
||||
}
|
||||
// vii. Return true.
|
||||
return Value.true;
|
||||
}
|
||||
}
|
||||
// 4. Return ! OrdinaryDefineOwnProperty(O, P, Desc).
|
||||
return Q(OrdinaryDefineOwnProperty(O, P, Desc));
|
||||
}
|
||||
|
||||
// 9.4.5.4 #sec-integer-indexed-exotic-objects-get-p-receiver
|
||||
export function IntegerIndexedGet(P, Receiver) {
|
||||
const O = this;
|
||||
// 1. Assert: IsPropertykey(P) is true.
|
||||
Assert(IsPropertyKey(P));
|
||||
// 2. If Type(P) is String, then
|
||||
if (Type(P) === 'String') {
|
||||
// a. Let numericIndex be ! CanonicalNumericIndexString(P).
|
||||
const numericIndex = X(CanonicalNumericIndexString(P));
|
||||
// b. If numericIndex is not undefined, then
|
||||
if (numericIndex !== Value.undefined) {
|
||||
// i. Return ? IntegerIndexedElementGet(O, numericIndex).
|
||||
return Q(IntegerIndexedElementGet(O, numericIndex));
|
||||
}
|
||||
}
|
||||
// 3. Return ? OrdinaryGet(O, P, Receiver).
|
||||
return Q(OrdinaryGet(O, P, Receiver));
|
||||
}
|
||||
|
||||
// 9.4.5.5 #sec-integer-indexed-exotic-objects-set-p-v-receiver
|
||||
export function IntegerIndexedSet(P, V, Receiver) {
|
||||
const O = this;
|
||||
// 1. Assert: IsPropertyKey(P) is true.
|
||||
Assert(IsPropertyKey(P));
|
||||
// 2. If Type(P) is String, then
|
||||
if (Type(P) === 'String') {
|
||||
// a. Let numericIndex be ! CanonicalNumericIndexString(P).
|
||||
const numericIndex = X(CanonicalNumericIndexString(P));
|
||||
// b. If numericIndex is not undefined, then
|
||||
if (numericIndex !== Value.undefined) {
|
||||
// i. Return ? IntegerIndexedElementSet(O, numericIndex, V).
|
||||
return Q(IntegerIndexedElementSet(O, numericIndex, V));
|
||||
}
|
||||
}
|
||||
// 3. Return ? OrdinarySet(O, P, V, Receiver).
|
||||
return Q(OrdinarySet(O, P, V, Receiver));
|
||||
}
|
||||
|
||||
// 9.4.5.6 #sec-integer-indexed-exotic-objects-ownpropertykeys
|
||||
export function IntegerIndexedOwnPropertyKeys() {
|
||||
const O = this;
|
||||
// 1. Let keys be a new empty List.
|
||||
const keys = [];
|
||||
// 2. Assert: O is an Integer-Indexed exotic object.
|
||||
Assert(isIntegerIndexedExoticObject(O));
|
||||
// 3. Let len be O.[[ArrayLength]].
|
||||
const len = O.ArrayLength.numberValue();
|
||||
// 4. For each integer i starting with 0 such that i < len, in ascending order, do
|
||||
for (let i = 0; i < len; i += 1) {
|
||||
// a. Add ! ToString(i) as the last element of keys.
|
||||
keys.push(X(ToString(new Value(i))));
|
||||
}
|
||||
// 5. For each own property key P of O such that Type(P) is String and P is not an integer index, in ascending chronological order of property creation, do
|
||||
for (const P of O.properties.keys()) {
|
||||
if (Type(P) === 'String') {
|
||||
if (!isIntegerIndex(P)) {
|
||||
// a. Add P as the last element of keys.
|
||||
keys.push(P);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 6. For each own property key P of O such that Type(P) is Symbol, in ascending chronological order of property creation, do
|
||||
for (const P of O.properties.keys()) {
|
||||
if (Type(P) === 'Symbol') {
|
||||
// a. Add P as the last element of keys.
|
||||
keys.push(P);
|
||||
}
|
||||
}
|
||||
// 7. Return keys.
|
||||
return keys;
|
||||
}
|
||||
|
||||
// #sec-integerindexedelementget
|
||||
export function IntegerIndexedElementGet(O, index) {
|
||||
// 1. Assert: O is an Integer-Indexed exotic object.
|
||||
Assert(isIntegerIndexedExoticObject(O));
|
||||
// 2. Assert: Type(index) is Number.
|
||||
Assert(Type(index) === 'Number');
|
||||
// 3. Let buffer be O.[[ViewedArrayBuffer]].
|
||||
const buffer = O.ViewedArrayBuffer;
|
||||
// 4. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
|
||||
if (IsDetachedBuffer(buffer) === Value.true) {
|
||||
return surroundingAgent.Throw('TypeError', 'ArrayBufferDetached');
|
||||
}
|
||||
// 5. If ! IsValidIntegerIndex(O, index) is false, return undefined.
|
||||
if (IsValidIntegerIndex(O, index) === Value.false) {
|
||||
return Value.undefined;
|
||||
}
|
||||
// 6. Let offset be O.[[ByteOffset]].
|
||||
const offset = O.ByteOffset;
|
||||
// 7. Let arrayTypeName be the String value of O.[[TypedArrayName]].
|
||||
const arrayTypeName = O.TypedArrayName.stringValue();
|
||||
// 8. Let elementSize be the Element Size value specified in Table 61 for arrayTypeName.
|
||||
const elementSize = typedArrayInfoByName[arrayTypeName].ElementSize;
|
||||
// 9. Let indexedPosition be (index × elementSize) + offset.
|
||||
const indexedPosition = new Value((index.numberValue() * elementSize) + offset.numberValue());
|
||||
// 10. Let elementType be the Element Type value in Table 61 for arrayTypeName.
|
||||
const elementType = typedArrayInfoByName[arrayTypeName].ElementType;
|
||||
// 11. Return GetValueFromBuffer(buffer, indexedPosition, elementType, true, Unordered).
|
||||
return GetValueFromBuffer(buffer, indexedPosition, elementType, Value.true, 'Unordered');
|
||||
}
|
||||
|
||||
// #sec-integerindexedelementset
|
||||
export function IntegerIndexedElementSet(O, index, value) {
|
||||
// 1. Assert: O is an Integer-Indexed exotic object.
|
||||
Assert(isIntegerIndexedExoticObject(O));
|
||||
// 2. Assert: Type(index) is Number.
|
||||
Assert(Type(index) === 'Number');
|
||||
// 3. If O.[[ContentType]] is BigInt, let numValue be ? ToBigInt(value).
|
||||
// 4. Otherwise, let numValue be ? ToNumber(value).
|
||||
let numValue;
|
||||
if (O.ContentType === 'BigInt') {
|
||||
numValue = Q(ToBigInt(value));
|
||||
} else {
|
||||
numValue = Q(ToNumber(value));
|
||||
}
|
||||
// 5. Let buffer be O.[[ViewedArrayBuffer]].
|
||||
const buffer = O.ViewedArrayBuffer;
|
||||
// 6. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
|
||||
if (IsDetachedBuffer(buffer) === Value.true) {
|
||||
return surroundingAgent.Throw('TypeError', 'ArrayBufferDetached');
|
||||
}
|
||||
// 7. If ! IsValidIntegerIndex(O, index) is false, return false.
|
||||
if (IsValidIntegerIndex(O, index) === Value.false) {
|
||||
return Value.false;
|
||||
}
|
||||
// 8. Let offset be O.[[ByteOffset]].
|
||||
const offset = O.ByteOffset;
|
||||
// 9. Let arrayTypeName be the String value of O.[[TypedArrayName]].
|
||||
const arrayTypeName = O.TypedArrayName.stringValue();
|
||||
// 10. Let elementSize be the Element Size value specified in Table 61 for arrayTypeName.
|
||||
const elementSize = typedArrayInfoByName[arrayTypeName].ElementSize;
|
||||
// 11. Let indexedPosition be (index × elementSize) + offset.
|
||||
const indexedPosition = new Value((index.numberValue() * elementSize) + offset.numberValue());
|
||||
// 12. Let elementType be the Element Type value in Table 61 for arrayTypeName.
|
||||
const elementType = typedArrayInfoByName[arrayTypeName].ElementType;
|
||||
// 13. Perform SetValueInBuffer(buffer, indexedPosition, elementType, numValue, true, Unordered).
|
||||
X(SetValueInBuffer(buffer, indexedPosition, elementType, numValue, Value.true, 'Unordered'));
|
||||
// 14. Return true.
|
||||
return Value.true;
|
||||
}
|
||||
|
||||
// #sec-integerindexedobjectcreate
|
||||
export function IntegerIndexedObjectCreate(prototype) {
|
||||
// 1. Let internalSlotsList be « [[Prototype]], [[Extensible]], [[ViewedArrayBuffer]], [[TypedArrayName]], [[ContentType]], [[ByteLength]], [[ByteOffset]], [[ArrayLength]] ».
|
||||
const internalSlotsList = [
|
||||
'Prototype',
|
||||
'Extensible',
|
||||
'ViewedArrayBuffer',
|
||||
'TypedArrayName',
|
||||
'ContentType',
|
||||
'ByteLength',
|
||||
'ByteOffset',
|
||||
'ArrayLength',
|
||||
];
|
||||
// 2. Let A be ! MakeBasicObject(internalSlotsList).
|
||||
const A = X(MakeBasicObject(internalSlotsList));
|
||||
// 3. Set A.[[GetOwnProperty]] as specified in 9.4.5.1.
|
||||
A.GetOwnProperty = IntegerIndexedGetOwnProperty;
|
||||
// 4. Set A.[[HasProperty]] as specified in 9.4.5.2.
|
||||
A.HasProperty = IntegerIndexedHasProperty;
|
||||
// 5. Set A.[[DefineOwnProperty]] as specified in 9.4.5.3.
|
||||
A.DefineOwnProperty = IntegerIndexedDefineOwnProperty;
|
||||
// 6. Set A.[[Get]] as specified in 9.4.5.4.
|
||||
A.Get = IntegerIndexedGet;
|
||||
// 7. Set A.[[Set]] as specified in 9.4.5.5.
|
||||
A.Set = IntegerIndexedSet;
|
||||
// 8. Set A.[[OwnPropertyKeys]] as specified in 9.4.5.6.
|
||||
A.OwnPropertyKeys = IntegerIndexedOwnPropertyKeys;
|
||||
// 9. Set A.[[Prototype]] to prototype.
|
||||
A.Prototype = prototype;
|
||||
// 10. Return A.
|
||||
return A;
|
||||
}
|
||||
@@ -1,255 +0,0 @@
|
||||
import { surroundingAgent } from '../engine.mjs';
|
||||
import {
|
||||
Type,
|
||||
Value,
|
||||
wellKnownSymbols,
|
||||
} from '../value.mjs';
|
||||
import {
|
||||
Completion,
|
||||
EnsureCompletion,
|
||||
IfAbruptRejectPromise,
|
||||
Q, X,
|
||||
Await,
|
||||
} from '../completion.mjs';
|
||||
import {
|
||||
Assert,
|
||||
Call,
|
||||
CreateBuiltinFunction,
|
||||
CreateDataProperty,
|
||||
Get,
|
||||
GetMethod,
|
||||
GetV,
|
||||
PromiseResolve,
|
||||
OrdinaryObjectCreate,
|
||||
PerformPromiseThen,
|
||||
ToBoolean,
|
||||
} from './all.mjs';
|
||||
|
||||
// This file covers abstract operations defined in
|
||||
// 7.4 #sec-operations-on-iterator-objects
|
||||
// and
|
||||
// 25.1 #sec-iteration
|
||||
|
||||
// 7.4.1 #sec-getiterator
|
||||
export function GetIterator(obj, hint, method) {
|
||||
if (!hint) {
|
||||
hint = 'sync';
|
||||
}
|
||||
Assert(hint === 'sync' || hint === 'async');
|
||||
if (!method) {
|
||||
if (hint === 'async') {
|
||||
method = Q(GetMethod(obj, wellKnownSymbols.asyncIterator));
|
||||
if (method === Value.undefined) {
|
||||
const syncMethod = Q(GetMethod(obj, wellKnownSymbols.iterator));
|
||||
const syncIteratorRecord = Q(GetIterator(obj, 'sync', syncMethod));
|
||||
return Q(CreateAsyncFromSyncIterator(syncIteratorRecord));
|
||||
}
|
||||
} else {
|
||||
method = Q(GetMethod(obj, wellKnownSymbols.iterator));
|
||||
}
|
||||
}
|
||||
const iterator = Q(Call(method, obj));
|
||||
if (Type(iterator) !== 'Object') {
|
||||
return surroundingAgent.Throw('TypeError', 'NotAnObject', iterator);
|
||||
}
|
||||
const nextMethod = Q(GetV(iterator, new Value('next')));
|
||||
const iteratorRecord = {
|
||||
Iterator: iterator,
|
||||
NextMethod: nextMethod,
|
||||
Done: Value.false,
|
||||
};
|
||||
return EnsureCompletion(iteratorRecord);
|
||||
}
|
||||
|
||||
// 7.4.2 #sec-iteratornext
|
||||
export function IteratorNext(iteratorRecord, value) {
|
||||
let result;
|
||||
if (!value) {
|
||||
result = Q(Call(iteratorRecord.NextMethod, iteratorRecord.Iterator));
|
||||
} else {
|
||||
result = Q(Call(iteratorRecord.NextMethod, iteratorRecord.Iterator, [value]));
|
||||
}
|
||||
if (Type(result) !== 'Object') {
|
||||
return surroundingAgent.Throw('TypeError', 'NotAnObject', result);
|
||||
}
|
||||
return EnsureCompletion(result);
|
||||
}
|
||||
|
||||
// 7.4.3 #sec-iteratorcomplete
|
||||
export function IteratorComplete(iterResult) {
|
||||
Assert(Type(iterResult) === 'Object');
|
||||
return EnsureCompletion(ToBoolean(Q(Get(iterResult, new Value('done')))));
|
||||
}
|
||||
|
||||
// 7.4.4 #sec-iteratorvalue
|
||||
export function IteratorValue(iterResult) {
|
||||
Assert(Type(iterResult) === 'Object');
|
||||
return EnsureCompletion(Q(Get(iterResult, new Value('value'))));
|
||||
}
|
||||
|
||||
// 7.4.5 #sec-iteratorstep
|
||||
export function IteratorStep(iteratorRecord) {
|
||||
const result = Q(IteratorNext(iteratorRecord));
|
||||
const done = Q(IteratorComplete(result));
|
||||
if (done === Value.true) {
|
||||
return EnsureCompletion(Value.false);
|
||||
}
|
||||
return EnsureCompletion(result);
|
||||
}
|
||||
|
||||
// #sec-iteratorclose
|
||||
export function IteratorClose(iteratorRecord, completion) {
|
||||
// 1. Assert: Type(iteratorRecord.[[Iterator]]) is Object.
|
||||
Assert(Type(iteratorRecord.Iterator) === 'Object');
|
||||
// 2. Assert: completion is a Completion Record.
|
||||
// TODO: completion should be a Completion Record so this should not be necessary
|
||||
completion = EnsureCompletion(completion);
|
||||
Assert(completion instanceof Completion);
|
||||
// 3. Let iterator be iteratorRecord.[[Iterator]].
|
||||
const iterator = iteratorRecord.Iterator;
|
||||
// 4. Let innerResult be GetMethod(iterator, "return").
|
||||
let innerResult = EnsureCompletion(GetMethod(iterator, new Value('return')));
|
||||
// 5. If innerResult.[[Type]] is normal, then
|
||||
if (innerResult.Type === 'normal') {
|
||||
// a. Let return be innerResult.[[Value]].
|
||||
const ret = innerResult.Value;
|
||||
// b. If return is undefined, return Completion(completion).
|
||||
if (ret === Value.undefined) {
|
||||
return Completion(completion);
|
||||
}
|
||||
// c. Set innerResult to Call(return, iterator).
|
||||
innerResult = Call(ret, iterator);
|
||||
}
|
||||
// 6. If completion.[[Type]] is throw, return Completion(completion).
|
||||
if (completion.Type === 'throw') {
|
||||
return Completion(completion);
|
||||
}
|
||||
// 7. If innerResult.[[Type]] is throw, return Completion(innerResult).
|
||||
if (innerResult.Type === 'throw') {
|
||||
return Completion(innerResult);
|
||||
}
|
||||
// 8. If Type(innerResult.[[Value]]) is not Object, throw a TypeError exception.
|
||||
if (Type(innerResult.Value) !== 'Object') {
|
||||
return surroundingAgent.Throw('TypeError', 'NotAnObject', innerResult.Value);
|
||||
}
|
||||
// 9. Return Completion(completion).
|
||||
return Completion(completion);
|
||||
}
|
||||
|
||||
// #sec-asynciteratorclose
|
||||
export function* AsyncIteratorClose(iteratorRecord, completion) {
|
||||
// 1. Assert: Type(iteratorRecord.[[Iterator]]) is Object.
|
||||
Assert(Type(iteratorRecord.Iterator) === 'Object');
|
||||
// 2. Assert: completion is a Completion Record.
|
||||
Assert(completion instanceof Completion);
|
||||
// 3. Let iterator be iteratorRecord.[[Iterator]].
|
||||
const iterator = iteratorRecord.Iterator;
|
||||
// 4. Let innerResult be GetMethod(iterator, "return").
|
||||
let innerResult = EnsureCompletion(GetMethod(iterator, new Value('return')));
|
||||
// 5. If innerResult.[[Type]] is normal, then
|
||||
if (innerResult.Type === 'normal') {
|
||||
// a. Let return be innerResult.[[Value]].
|
||||
const ret = innerResult.Value;
|
||||
// b. If return is undefined, return Completion(completion).
|
||||
if (ret === Value.undefined) {
|
||||
return Completion(completion);
|
||||
}
|
||||
// c. Set innerResult to Call(return, iterator).
|
||||
innerResult = Call(ret, iterator);
|
||||
// d. If innerResult.[[Type]] is normal, set innerResult to Await(innerResult.[[Value]]).
|
||||
if (innerResult.Type === 'normal') {
|
||||
innerResult = EnsureCompletion(yield* Await(innerResult.Value));
|
||||
}
|
||||
}
|
||||
// 6. If completion.[[Type]] is throw, return Completion(completion).
|
||||
if (completion.Type === 'throw') {
|
||||
return Completion(completion);
|
||||
}
|
||||
// 7. If innerResult.[[Type]] is throw, return Completion(innerResult).
|
||||
if (innerResult.Type === 'throw') {
|
||||
return Completion(innerResult);
|
||||
}
|
||||
// 8. If Type(innerResult.[[Value]]) is not Object, throw a TypeError exception.
|
||||
if (Type(innerResult.Value) !== 'Object') {
|
||||
return surroundingAgent.Throw('TypeError', 'NotAnObject', innerResult.Value);
|
||||
}
|
||||
// 9. Return Completion(completion).
|
||||
return Completion(completion);
|
||||
}
|
||||
|
||||
// 7.4.8 #sec-createiterresultobject
|
||||
export function CreateIterResultObject(value, done) {
|
||||
Assert(Type(done) === 'Boolean');
|
||||
const obj = OrdinaryObjectCreate(surroundingAgent.intrinsic('%Object.prototype%'));
|
||||
X(CreateDataProperty(obj, new Value('value'), value));
|
||||
X(CreateDataProperty(obj, new Value('done'), done));
|
||||
return obj;
|
||||
}
|
||||
|
||||
// 7.4.9 #sec-createlistiteratorRecord
|
||||
export function CreateListIteratorRecord(list) {
|
||||
const iterator = OrdinaryObjectCreate(surroundingAgent.intrinsic('%IteratorPrototype%'), [
|
||||
'IteratedList',
|
||||
'ListNextIndex',
|
||||
]);
|
||||
iterator.IteratedList = list;
|
||||
iterator.ListNextIndex = 0;
|
||||
const steps = ListIteratorNextSteps;
|
||||
const next = X(CreateBuiltinFunction(steps, []));
|
||||
return {
|
||||
Iterator: iterator,
|
||||
NextMethod: next,
|
||||
Done: Value.false,
|
||||
};
|
||||
}
|
||||
|
||||
// 7.4.9.1 #sec-listiterator-next
|
||||
function ListIteratorNextSteps(args, { thisValue }) {
|
||||
const O = thisValue;
|
||||
Assert(Type(O) === 'Object');
|
||||
Assert('IteratedList' in O);
|
||||
const list = O.IteratedList;
|
||||
const index = O.ListNextIndex;
|
||||
const len = list.length;
|
||||
if (index >= len) {
|
||||
return CreateIterResultObject(Value.undefined, Value.true);
|
||||
}
|
||||
O.ListNextIndex += 1;
|
||||
return CreateIterResultObject(list[index], Value.false);
|
||||
}
|
||||
|
||||
// 25.1.4.1 #sec-createasyncfromsynciterator
|
||||
export function CreateAsyncFromSyncIterator(syncIteratorRecord) {
|
||||
const asyncIterator = X(OrdinaryObjectCreate(surroundingAgent.intrinsic('%AsyncFromSyncIteratorPrototype%'), [
|
||||
'SyncIteratorRecord',
|
||||
]));
|
||||
asyncIterator.SyncIteratorRecord = syncIteratorRecord;
|
||||
const nextMethod = X(Get(asyncIterator, new Value('next')));
|
||||
return {
|
||||
Iterator: asyncIterator,
|
||||
NextMethod: nextMethod,
|
||||
Done: Value.false,
|
||||
};
|
||||
}
|
||||
|
||||
// 25.1.4.2.4 #sec-async-from-sync-iterator-value-unwrap-functions
|
||||
function AsyncFromSyncIteratorValueUnwrapFunctions([value = Value.undefined]) {
|
||||
const F = this;
|
||||
|
||||
return X(CreateIterResultObject(value, F.Done));
|
||||
}
|
||||
|
||||
// 25.1.4.4 #sec-asyncfromsynciteratorcontinuation
|
||||
export function AsyncFromSyncIteratorContinuation(result, promiseCapability) {
|
||||
const done = IteratorComplete(result);
|
||||
IfAbruptRejectPromise(done, promiseCapability);
|
||||
const value = IteratorValue(result);
|
||||
IfAbruptRejectPromise(value, promiseCapability);
|
||||
const valueWrapper = PromiseResolve(surroundingAgent.intrinsic('%Promise%'), value);
|
||||
IfAbruptRejectPromise(valueWrapper, promiseCapability);
|
||||
const steps = AsyncFromSyncIteratorValueUnwrapFunctions;
|
||||
const onFulfilled = X(CreateBuiltinFunction(steps, ['Done']));
|
||||
onFulfilled.Done = done;
|
||||
X(PerformPromiseThen(valueWrapper, onFulfilled, Value.undefined, promiseCapability));
|
||||
return promiseCapability.Promise;
|
||||
}
|
||||
@@ -1,218 +0,0 @@
|
||||
import { surroundingAgent } from '../engine.mjs';
|
||||
import { Q, X } from '../completion.mjs';
|
||||
import { AbstractModuleRecord, ResolvedBindingRecord } from '../modules.mjs';
|
||||
import {
|
||||
Type,
|
||||
Value,
|
||||
Descriptor,
|
||||
wellKnownSymbols,
|
||||
} from '../value.mjs';
|
||||
import { ValueSet } from '../helpers.mjs';
|
||||
import {
|
||||
Assert,
|
||||
SortCompare,
|
||||
SameValue,
|
||||
MakeBasicObject,
|
||||
IsPropertyKey,
|
||||
IsAccessorDescriptor,
|
||||
SetImmutablePrototype,
|
||||
OrdinaryGetOwnProperty,
|
||||
OrdinaryDefineOwnProperty,
|
||||
OrdinaryHasProperty,
|
||||
OrdinaryGet,
|
||||
OrdinaryDelete,
|
||||
OrdinaryOwnPropertyKeys,
|
||||
GetModuleNamespace,
|
||||
} from './all.mjs';
|
||||
|
||||
|
||||
function ModuleNamespaceSetPrototypeOf(V) {
|
||||
const O = this;
|
||||
|
||||
return Q(SetImmutablePrototype(O, V));
|
||||
}
|
||||
|
||||
function ModuleNamespaceIsExtensible() {
|
||||
return Value.false;
|
||||
}
|
||||
|
||||
function ModuleNamespacePreventExtensions() {
|
||||
return Value.true;
|
||||
}
|
||||
|
||||
function ModuleNamespaceGetOwnProperty(P) {
|
||||
const O = this;
|
||||
|
||||
if (Type(P) === 'Symbol') {
|
||||
return OrdinaryGetOwnProperty(O, P);
|
||||
}
|
||||
const exports = O.Exports;
|
||||
if (!exports.has(P)) {
|
||||
return Value.undefined;
|
||||
}
|
||||
const value = Q(O.Get(P, O));
|
||||
return Descriptor({
|
||||
Value: value,
|
||||
Writable: Value.true,
|
||||
Enumerable: Value.true,
|
||||
Configurable: Value.false,
|
||||
});
|
||||
}
|
||||
|
||||
function ModuleNamespaceDefineOwnProperty(P, Desc) {
|
||||
const O = this;
|
||||
|
||||
if (Type(P) === 'Symbol') {
|
||||
return OrdinaryDefineOwnProperty(O, P, Desc);
|
||||
}
|
||||
|
||||
const current = Q(O.GetOwnProperty(P));
|
||||
if (current === Value.undefined) {
|
||||
return Value.false;
|
||||
}
|
||||
if (IsAccessorDescriptor(Desc)) {
|
||||
return Value.false;
|
||||
}
|
||||
if (Desc.Writable !== undefined && Desc.Writable === Value.false) {
|
||||
return Value.false;
|
||||
}
|
||||
if (Desc.Enumerable !== undefined && Desc.Enumerable === Value.false) {
|
||||
return Value.false;
|
||||
}
|
||||
if (Desc.Configurable !== undefined && Desc.Configurable === Value.true) {
|
||||
return Value.false;
|
||||
}
|
||||
if (Desc.Value !== undefined) {
|
||||
return SameValue(Desc.Value, current.Value);
|
||||
}
|
||||
return Value.true;
|
||||
}
|
||||
|
||||
function ModuleNamespaceHasProperty(P) {
|
||||
const O = this;
|
||||
|
||||
if (Type(P) === 'Symbol') {
|
||||
return OrdinaryHasProperty(O, P);
|
||||
}
|
||||
const exports = O.Exports;
|
||||
if (exports.has(P)) {
|
||||
return Value.true;
|
||||
}
|
||||
return Value.false;
|
||||
}
|
||||
|
||||
// #sec-module-namespace-exotic-objects-get-p-receiver
|
||||
function ModuleNamespaceGet(P, Receiver) {
|
||||
const O = this;
|
||||
|
||||
// 1. Assert: IsPropertyKey(P) is true.
|
||||
Assert(IsPropertyKey(P));
|
||||
// 2. If Type(P) is Symbol, then
|
||||
if (Type(P) === 'Symbol') {
|
||||
// a. Return ? OrdinaryGet(O, P, Receiver).
|
||||
return OrdinaryGet(O, P, Receiver);
|
||||
}
|
||||
// 3. Let exports be O.[[Exports]].
|
||||
const exports = O.Exports;
|
||||
// 4. If P is not an element of exports, return undefined.
|
||||
if (!exports.has(P)) {
|
||||
return Value.undefined;
|
||||
}
|
||||
// 5. Let m be O.[[Module]].
|
||||
const m = O.Module;
|
||||
// 6. Let binding be ! m.ResolveExport(P).
|
||||
const binding = m.ResolveExport(P);
|
||||
// 7. Assert: binding is a ResolvedBinding Record.
|
||||
Assert(binding instanceof ResolvedBindingRecord);
|
||||
// 8. Let targetModule be binding.[[Module]].
|
||||
const targetModule = binding.Module;
|
||||
// 9. Assert: targetModule is not undefined.
|
||||
Assert(targetModule !== Value.undefined);
|
||||
// 10. If binding.[[BindingName]] is ~namespace~, then
|
||||
if (binding.BindingName === 'namespace') {
|
||||
// a. Return ? GetModuleNamespace(targetModule).
|
||||
return Q(GetModuleNamespace(targetModule));
|
||||
}
|
||||
// 11. Let targetEnv be targetModule.[[Environment]].
|
||||
const targetEnv = targetModule.Environment;
|
||||
// 12. If targetEnv is undefined, throw a ReferenceError exception.
|
||||
if (targetEnv === Value.undefined) {
|
||||
return surroundingAgent.Throw('ReferenceError', 'NotDefined', P);
|
||||
}
|
||||
// 13. Return ? targetEnv.GetBindingValue(binding.[[BindingName]], true).
|
||||
return Q(targetEnv.GetBindingValue(binding.BindingName, Value.true));
|
||||
}
|
||||
|
||||
function ModuleNamespaceSet() {
|
||||
return Value.false;
|
||||
}
|
||||
|
||||
function ModuleNamespaceDelete(P) {
|
||||
const O = this;
|
||||
|
||||
Assert(IsPropertyKey(P));
|
||||
if (Type(P) === 'Symbol') {
|
||||
return Q(OrdinaryDelete(O, P));
|
||||
}
|
||||
const exports = O.Exports;
|
||||
if (exports.has(P)) {
|
||||
return Value.false;
|
||||
}
|
||||
return Value.true;
|
||||
}
|
||||
|
||||
function ModuleNamespaceOwnPropertyKeys() {
|
||||
const O = this;
|
||||
|
||||
const exports = [...O.Exports];
|
||||
const symbolKeys = X(OrdinaryOwnPropertyKeys(O));
|
||||
exports.push(...symbolKeys);
|
||||
return exports;
|
||||
}
|
||||
|
||||
// 9.4.6.11 #sec-modulenamespacecreate
|
||||
export function ModuleNamespaceCreate(module, exports) {
|
||||
// 1. Assert: module is a Module Record.
|
||||
Assert(module instanceof AbstractModuleRecord);
|
||||
// 2. Assert: module.[[Namespace]] is undefined.
|
||||
Assert(module.Namespace === Value.undefined);
|
||||
// 3. Assert: exports is a List of String values.
|
||||
Assert(Array.isArray(exports));
|
||||
// 4. Let internalSlotsList be the internal slots listed in Table 31.
|
||||
const internalSlotsList = ['Module', 'Exports', 'Prototype'];
|
||||
// 5. Let M be ! MakeBasicObject(internalSlotsList).
|
||||
const M = X(MakeBasicObject(internalSlotsList));
|
||||
// 6. Set M's essential internal methods to the definitions specified in #sec-module-namespace-exotic-objects
|
||||
M.SetPrototypeOf = ModuleNamespaceSetPrototypeOf;
|
||||
M.IsExtensible = ModuleNamespaceIsExtensible;
|
||||
M.PreventExtensions = ModuleNamespacePreventExtensions;
|
||||
M.GetOwnProperty = ModuleNamespaceGetOwnProperty;
|
||||
M.DefineOwnProperty = ModuleNamespaceDefineOwnProperty;
|
||||
M.HasProperty = ModuleNamespaceHasProperty;
|
||||
M.Get = ModuleNamespaceGet;
|
||||
M.Set = ModuleNamespaceSet;
|
||||
M.Delete = ModuleNamespaceDelete;
|
||||
M.OwnPropertyKeys = ModuleNamespaceOwnPropertyKeys;
|
||||
// 7. Set M.[[Prototype]] to null.
|
||||
M.Prototype = Value.null;
|
||||
// 8. Set M.[[Module]] to module.
|
||||
M.Module = module;
|
||||
// 9. Let sortedExports be a new List containing the same values as the list exports where the values are ordered as if an Array of the same values had been sorted using Array.prototype.sort using undefined as comparefn.
|
||||
const sortedExports = [...exports].sort((x, y) => {
|
||||
const result = X(SortCompare(x, y, Value.undefined));
|
||||
return result.numberValue();
|
||||
});
|
||||
// 10. Set M.[[Exports]] to sortedExports.
|
||||
M.Exports = new ValueSet(sortedExports);
|
||||
// 11. Create own properties of M corresponding to the definitions in 26.3.
|
||||
M.properties.set(wellKnownSymbols.toStringTag, Descriptor({
|
||||
Writable: Value.false,
|
||||
Enumerable: Value.false,
|
||||
Configurable: Value.false,
|
||||
Value: new Value('Module'),
|
||||
}));
|
||||
// 12. Set module.[[Namespace]] to M.
|
||||
module.Namespace = M;
|
||||
// 13. Return M;
|
||||
return M;
|
||||
}
|
||||
@@ -1,263 +0,0 @@
|
||||
import { surroundingAgent, HostResolveImportedModule } from '../engine.mjs';
|
||||
import {
|
||||
AbstractModuleRecord,
|
||||
CyclicModuleRecord,
|
||||
ResolvedBindingRecord,
|
||||
} from '../modules.mjs';
|
||||
import { Value } from '../value.mjs';
|
||||
import {
|
||||
Q, X, NormalCompletion, ThrowCompletion,
|
||||
} from '../completion.mjs';
|
||||
import {
|
||||
Assert,
|
||||
ModuleNamespaceCreate,
|
||||
NewPromiseCapability,
|
||||
PerformPromiseThen,
|
||||
CreateBuiltinFunction,
|
||||
Call,
|
||||
} from './all.mjs';
|
||||
|
||||
// 15.2.1.16.1.1 #sec-InnerModuleLinking
|
||||
export function InnerModuleLinking(module, stack, index) {
|
||||
if (!(module instanceof CyclicModuleRecord)) {
|
||||
Q(module.Link());
|
||||
return index;
|
||||
}
|
||||
if (module.Status === 'linking' || module.Status === 'linked' || module.Status === 'evaluated') {
|
||||
return index;
|
||||
}
|
||||
Assert(module.Status === 'unlinked');
|
||||
module.Status = 'linking';
|
||||
module.DFSIndex = index;
|
||||
module.DFSAncestorIndex = index;
|
||||
index += 1;
|
||||
stack.push(module);
|
||||
for (const required of module.RequestedModules) {
|
||||
const requiredModule = Q(HostResolveImportedModule(module, required));
|
||||
index = Q(InnerModuleLinking(requiredModule, stack, index));
|
||||
if (requiredModule instanceof CyclicModuleRecord) {
|
||||
Assert(requiredModule.Status === 'linking' || requiredModule.Status === 'linked' || requiredModule.Status === 'evaluated');
|
||||
Assert((requiredModule.Status === 'linking') === stack.includes(requiredModule));
|
||||
if (requiredModule.Status === 'linking') {
|
||||
module.DFSAncestorIndex = Math.min(module.DFSAncestorIndex, requiredModule.DFSAncestorIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
Q(module.InitializeEnvironment());
|
||||
Assert(stack.indexOf(module) === stack.lastIndexOf(module));
|
||||
Assert(module.DFSAncestorIndex <= module.DFSIndex);
|
||||
if (module.DFSAncestorIndex === module.DFSIndex) {
|
||||
let done = false;
|
||||
while (done === false) {
|
||||
const requiredModule = stack.pop();
|
||||
Assert(requiredModule instanceof CyclicModuleRecord);
|
||||
requiredModule.Status = 'linked';
|
||||
if (requiredModule === module) {
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
// 15.2.1.16.2.1 #sec-innermoduleevaluation
|
||||
export function InnerModuleEvaluation(module, stack, index) {
|
||||
if (!(module instanceof CyclicModuleRecord)) {
|
||||
Q(module.Evaluate());
|
||||
return index;
|
||||
}
|
||||
if (module.Status === 'evaluated') {
|
||||
if (module.EvaluationError === Value.undefined) {
|
||||
return index;
|
||||
} else {
|
||||
return module.EvaluationError;
|
||||
}
|
||||
}
|
||||
if (module.Status === 'evaluating') {
|
||||
return index;
|
||||
}
|
||||
Assert(module.Status === 'linked');
|
||||
module.Status = 'evaluating';
|
||||
module.DFSIndex = index;
|
||||
module.DFSAncestorIndex = index;
|
||||
module.PendingAsyncDependencies = 0;
|
||||
module.AsyncParentModules = [];
|
||||
index += 1;
|
||||
stack.push(module);
|
||||
for (const required of module.RequestedModules) {
|
||||
let requiredModule = X(HostResolveImportedModule(module, required));
|
||||
index = Q(InnerModuleEvaluation(requiredModule, stack, index));
|
||||
if (requiredModule instanceof CyclicModuleRecord) {
|
||||
Assert(requiredModule.Status === 'evaluating' || requiredModule.Status === 'evaluated');
|
||||
if (stack.includes(requiredModule)) {
|
||||
Assert(requiredModule.Status === 'evaluating');
|
||||
}
|
||||
if (requiredModule.Status === 'evaluating') {
|
||||
module.DFSAncestorIndex = Math.min(module.DFSAncestorIndex, requiredModule.DFSAncestorIndex);
|
||||
} else {
|
||||
requiredModule = GetAsyncCycleRoot(requiredModule);
|
||||
Assert(requiredModule.Status === 'evaluated');
|
||||
if (requiredModule.EvaluationError !== Value.undefined) {
|
||||
return module.EvaluationError;
|
||||
}
|
||||
}
|
||||
if (requiredModule.AsyncEvaluating === Value.true) {
|
||||
module.PendingAsyncDependencies += 1;
|
||||
requiredModule.AsyncParentModules.push(module);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (module.PendingAsyncDependencies > 0) {
|
||||
module.AsyncEvaluating = Value.true;
|
||||
} else if (module.Async === Value.true) {
|
||||
X(ExecuteAsyncModule(module));
|
||||
} else {
|
||||
Q(module.ExecuteModule());
|
||||
}
|
||||
Assert(stack.indexOf(module) === stack.lastIndexOf(module));
|
||||
Assert(module.DFSAncestorIndex <= module.DFSIndex);
|
||||
if (module.DFSAncestorIndex === module.DFSIndex) {
|
||||
let done = false;
|
||||
while (done === false) {
|
||||
const requiredModule = stack.pop();
|
||||
Assert(requiredModule instanceof CyclicModuleRecord);
|
||||
requiredModule.Status = 'evaluated';
|
||||
if (requiredModule === module) {
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
// https://tc39.es/proposal-top-level-await/#sec-execute-async-module
|
||||
function ExecuteAsyncModule(module) {
|
||||
Assert(module.Status === 'evaluating' || module.Status === 'evaluated');
|
||||
Assert(module.Async === Value.true);
|
||||
module.AsyncEvaluating = Value.true;
|
||||
const capability = X(NewPromiseCapability(surroundingAgent.intrinsic('%Promise%')));
|
||||
const stepsFulfilled = CallAsyncModuleFulfilled;
|
||||
const onFulfilled = CreateBuiltinFunction(stepsFulfilled, ['Module']);
|
||||
onFulfilled.Module = module;
|
||||
const stepsRejected = CallAsyncModuleRejected;
|
||||
const onRejected = CreateBuiltinFunction(stepsRejected, ['Module']);
|
||||
onRejected.Module = module;
|
||||
X(PerformPromiseThen(capability.Promise, onFulfilled, onRejected));
|
||||
X(module.ExecuteModule(capability));
|
||||
return Value.undefined;
|
||||
}
|
||||
|
||||
// https://tc39.es/proposal-top-level-await/#sec-execute-async-module
|
||||
function CallAsyncModuleFulfilled() {
|
||||
const f = surroundingAgent.activeFunctionObject;
|
||||
const module = f.Module;
|
||||
X(AsyncModuleExecutionFulfilled(module));
|
||||
return Value.undefined;
|
||||
}
|
||||
|
||||
// https://tc39.es/proposal-top-level-await/#sec-execute-async-module
|
||||
function CallAsyncModuleRejected([error = Value.undefined]) {
|
||||
const f = surroundingAgent.activeFunctionObject;
|
||||
const module = f.Module;
|
||||
X(AsyncModuleExecutionRejected(module, error));
|
||||
return Value.undefined;
|
||||
}
|
||||
|
||||
// https://tc39.es/proposal-top-level-await/#sec-getcycleroot
|
||||
export function GetAsyncCycleRoot(module) {
|
||||
Assert(module.Status === 'evaluated');
|
||||
if (module.AsyncParentModules.length === 0) {
|
||||
return module;
|
||||
}
|
||||
while (module.DFSIndex > module.DFSAncestorIndex) {
|
||||
Assert(module.AsyncParentModules.length > 0);
|
||||
const nextCycleModule = module.AsyncParentModules[0];
|
||||
Assert(nextCycleModule.DFSAncestorIndex === module.DFSAncestorIndex);
|
||||
module = nextCycleModule;
|
||||
}
|
||||
Assert(module.DFSIndex === module.DFSAncestorIndex);
|
||||
return module;
|
||||
}
|
||||
|
||||
// https://tc39.es/proposal-top-level-await/#sec-asyncmodulexecutionfulfilled
|
||||
function AsyncModuleExecutionFulfilled(module) {
|
||||
Assert(module.Status === 'evaluated');
|
||||
if (module.AsyncEvaluating === Value.false) {
|
||||
Assert(module.EvaluationError !== Value.undefined);
|
||||
return Value.undefined;
|
||||
}
|
||||
Assert(module.EvaluationError === Value.undefined);
|
||||
module.AsyncEvaluating = Value.false;
|
||||
for (const m of module.AsyncParentModules) {
|
||||
if (module.DFSIndex !== module.DFSAncestorIndex) {
|
||||
Assert(m.DFSAncestorIndex === module.DFSAncestorIndex);
|
||||
}
|
||||
m.PendingAsyncDependencies -= 1;
|
||||
if (m.PendingAsyncDependencies === 0 && m.EvaluationError === Value.undefined) {
|
||||
Assert(m.AsyncEvaluating === Value.true);
|
||||
const cycleRoot = X(GetAsyncCycleRoot(m));
|
||||
if (cycleRoot.EvaluationError !== Value.undefined) {
|
||||
return Value.undefined;
|
||||
}
|
||||
if (m.Async === Value.true) {
|
||||
X(ExecuteAsyncModule(m));
|
||||
} else {
|
||||
const result = m.ExecuteModule();
|
||||
if (result instanceof NormalCompletion) {
|
||||
X(AsyncModuleExecutionFulfilled(m));
|
||||
} else {
|
||||
X(AsyncModuleExecutionRejected(m, result.Value));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (module.TopLevelCapability !== Value.undefined) {
|
||||
Assert(module.DFSIndex === module.DFSAncestorIndex);
|
||||
X(Call(module.TopLevelCapability.Resolve, Value.undefined, [Value.undefined]));
|
||||
}
|
||||
return Value.undefined;
|
||||
}
|
||||
|
||||
// https://tc39.es/proposal-top-level-await/#sec-AsyncModuleExecutionRejected
|
||||
function AsyncModuleExecutionRejected(module, error) {
|
||||
Assert(module.Status === 'evaluated');
|
||||
if (module.AsyncEvaluating === Value.false) {
|
||||
Assert(module.EvaluationError !== Value.undefined);
|
||||
return Value.undefined;
|
||||
}
|
||||
Assert(module.EvaluationError === Value.undefined);
|
||||
module.EvaluationError = ThrowCompletion(error);
|
||||
module.AsyncEvaluating = Value.false;
|
||||
for (const m of module.AsyncParentModules) {
|
||||
if (module.DFSIndex !== module.DFSAncestorIndex) {
|
||||
Assert(m.DFSAncestorIndex === module.DFSAncestorIndex);
|
||||
}
|
||||
X(AsyncModuleExecutionRejected(m, error));
|
||||
}
|
||||
if (module.TopLevelCapability !== Value.undefined) {
|
||||
Assert(module.DFSIndex === module.DFSAncestorIndex);
|
||||
X(Call(module.TopLevelCapability.Reject, Value.undefined, [error]));
|
||||
}
|
||||
return Value.undefined;
|
||||
}
|
||||
|
||||
// 15.2.1.21 #sec-getmodulenamespace
|
||||
export function GetModuleNamespace(module) {
|
||||
Assert(module instanceof AbstractModuleRecord);
|
||||
if (module instanceof CyclicModuleRecord) {
|
||||
Assert(module.Status !== 'unlinked');
|
||||
}
|
||||
let namespace = module.Namespace;
|
||||
if (namespace === Value.undefined) {
|
||||
const exportedNames = Q(module.GetExportedNames());
|
||||
const unambiguousNames = [];
|
||||
for (const name of exportedNames) {
|
||||
const resolution = Q(module.ResolveExport(name));
|
||||
if (resolution instanceof ResolvedBindingRecord) {
|
||||
unambiguousNames.push(name);
|
||||
}
|
||||
}
|
||||
namespace = ModuleNamespaceCreate(module, unambiguousNames);
|
||||
}
|
||||
return namespace;
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
import { surroundingAgent } from '../engine.mjs';
|
||||
import { Type } from '../value.mjs';
|
||||
|
||||
export function Assert(invariant, source) {
|
||||
/* istanbul ignore next */
|
||||
if (!invariant) {
|
||||
throw new TypeError(`Assert failed${source ? `: ${source}` : ''}`.trim());
|
||||
}
|
||||
}
|
||||
|
||||
// 9.1.15 #sec-requireinternalslot
|
||||
export function RequireInternalSlot(O, internalSlot) {
|
||||
if (Type(O) !== 'Object') {
|
||||
return surroundingAgent.Throw('TypeError', 'NotAnObject', O);
|
||||
}
|
||||
if (!(internalSlot in O)) {
|
||||
return surroundingAgent.Throw('TypeError', 'InternalSlotMissing', O, internalSlot);
|
||||
}
|
||||
}
|
||||
|
||||
export function sourceTextMatchedBy(node) {
|
||||
return node.sourceText();
|
||||
}
|
||||
|
||||
// An ECMAScript Script syntactic unit may be processed using either unrestricted or strict mode syntax and semantics.
|
||||
// Code is interpreted as strict mode code in the following situations:
|
||||
//
|
||||
// - Global code is strict mode code if it begins with a Directive Prologue that contains a Use Strict Directive.
|
||||
//
|
||||
// - Module code is always strict mode code.
|
||||
//
|
||||
// - All parts of a ClassDeclaration or a ClassExpression are strict mode code.
|
||||
//
|
||||
// - Eval code is strict mode code if it begins with a Directive Prologue that contains a Use Strict Directive or
|
||||
// if the call to eval is a direct eval that is contained in strict mode code.
|
||||
//
|
||||
// - Function code is strict mode code if the associated FunctionDeclaration, FunctionExpression, GeneratorDeclaration,
|
||||
// GeneratorExpression, AsyncFunctionDeclaration, AsyncFunctionExpression, AsyncGeneratorDeclaration,
|
||||
// AsyncGeneratorExpression, MethodDefinition, ArrowFunction, or AsyncArrowFunction is contained in strict mode code
|
||||
// or if the code that produces the value of the function's [[ECMAScriptCode]] internal slot begins with a Directive
|
||||
// Prologue that contains a Use Strict Directive.
|
||||
//
|
||||
// - Function code that is supplied as the arguments to the built-in Function, Generator, AsyncFunction, and
|
||||
// AsyncGenerator constructors is strict mode code if the last argument is a String that when processed is a
|
||||
// FunctionBody that begins with a Directive Prologue that contains a Use Strict Directive.
|
||||
export function isStrictModeCode(node) {
|
||||
return node.strict;
|
||||
}
|
||||
@@ -1,433 +0,0 @@
|
||||
import {
|
||||
Descriptor,
|
||||
Type,
|
||||
Value,
|
||||
ObjectValue,
|
||||
wellKnownSymbols,
|
||||
} from '../value.mjs';
|
||||
import {
|
||||
surroundingAgent,
|
||||
} from '../engine.mjs';
|
||||
import { InstanceofOperator } from '../runtime-semantics/all.mjs';
|
||||
import {
|
||||
NormalCompletion,
|
||||
EnsureCompletion,
|
||||
Q, X,
|
||||
} from '../completion.mjs';
|
||||
import {
|
||||
ArrayCreate,
|
||||
Assert,
|
||||
IsAccessorDescriptor,
|
||||
IsCallable,
|
||||
IsConstructor,
|
||||
IsDataDescriptor,
|
||||
IsExtensible,
|
||||
IsPropertyKey,
|
||||
SameValue,
|
||||
ToLength,
|
||||
ToObject,
|
||||
ToString,
|
||||
isProxyExoticObject,
|
||||
} from './all.mjs';
|
||||
|
||||
|
||||
// This file covers abstract operations defined in
|
||||
// 7.3 #sec-operations-on-objects
|
||||
|
||||
// #sec-makebasicobject
|
||||
export function MakeBasicObject(internalSlotsList) {
|
||||
// 1. Assert: internalSlotsList is a List of internal slot names.
|
||||
Assert(Array.isArray(internalSlotsList));
|
||||
// 2. Let obj be a newly created object with an internal slot for each name in internalSlotsList.
|
||||
// 3. Set obj's essential internal methods to the default ordinary object definitions specified in 9.1.
|
||||
const obj = new ObjectValue(internalSlotsList);
|
||||
internalSlotsList.forEach((s) => {
|
||||
obj[s] = Value.undefined;
|
||||
});
|
||||
// 4. Assert: If the caller will not be overriding both obj's [[GetPrototypeOf]] and [[SetPrototypeOf]] essential internal methods, then internalSlotsList contains [[Prototype]].
|
||||
// 5. Assert: If the caller will not be overriding all of obj's [[SetPrototypeOf]], [[IsExtensible]], and [[PreventExtensions]] essential internal methods, then internalSlotsList contains [[Extensible]].
|
||||
// 6. If internalSlotsList contains [[Extensible]], then set obj.[[Extensible]] to true.
|
||||
if (internalSlotsList.includes('Extensible')) {
|
||||
obj.Extensible = Value.true;
|
||||
}
|
||||
// 7. Return obj.
|
||||
return obj;
|
||||
}
|
||||
|
||||
// 7.3.1 #sec-get-o-p
|
||||
export function Get(O, P) {
|
||||
Assert(Type(O) === 'Object');
|
||||
Assert(IsPropertyKey(P));
|
||||
// TODO: This should just return Q(O.Get(P, O))
|
||||
return NormalCompletion(Q(O.Get(P, O)));
|
||||
}
|
||||
|
||||
// 7.3.2 #sec-getv
|
||||
export function GetV(V, P) {
|
||||
Assert(IsPropertyKey(P));
|
||||
const O = Q(ToObject(V));
|
||||
return Q(O.Get(P, V));
|
||||
}
|
||||
|
||||
// 7.3.3 #sec-set-o-p-v-throw
|
||||
export function Set(O, P, V, Throw) {
|
||||
Assert(Type(O) === 'Object');
|
||||
Assert(IsPropertyKey(P));
|
||||
Assert(Type(Throw) === 'Boolean');
|
||||
const success = Q(O.Set(P, V, O));
|
||||
if (success === Value.false && Throw === Value.true) {
|
||||
return surroundingAgent.Throw('TypeError', 'CannotSetProperty', P, O);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
// 7.3.4 #sec-createdataproperty
|
||||
export function CreateDataProperty(O, P, V) {
|
||||
Assert(Type(O) === 'Object');
|
||||
Assert(IsPropertyKey(P));
|
||||
|
||||
const newDesc = Descriptor({
|
||||
Value: V,
|
||||
Writable: Value.true,
|
||||
Enumerable: Value.true,
|
||||
Configurable: Value.true,
|
||||
});
|
||||
return Q(O.DefineOwnProperty(P, newDesc));
|
||||
}
|
||||
|
||||
// 7.3.5 #sec-createmethodproperty
|
||||
export function CreateMethodProperty(O, P, V) {
|
||||
Assert(Type(O) === 'Object');
|
||||
Assert(IsPropertyKey(P));
|
||||
|
||||
const newDesc = Descriptor({
|
||||
Value: V,
|
||||
Writable: Value.true,
|
||||
Enumerable: Value.false,
|
||||
Configurable: Value.true,
|
||||
});
|
||||
return Q(O.DefineOwnProperty(P, newDesc));
|
||||
}
|
||||
|
||||
// 7.3.6 #sec-createdatapropertyorthrow
|
||||
export function CreateDataPropertyOrThrow(O, P, V) {
|
||||
Assert(Type(O) === 'Object');
|
||||
Assert(IsPropertyKey(P));
|
||||
const success = Q(CreateDataProperty(O, P, V));
|
||||
if (success === Value.false) {
|
||||
return surroundingAgent.Throw('TypeError', 'CannotDefineProperty', P);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
// 7.3.7 #sec-definepropertyorthrow
|
||||
export function DefinePropertyOrThrow(O, P, desc) {
|
||||
Assert(Type(O) === 'Object');
|
||||
Assert(IsPropertyKey(P));
|
||||
const success = Q(O.DefineOwnProperty(P, desc));
|
||||
if (success === Value.false) {
|
||||
return surroundingAgent.Throw('TypeError', 'CannotDefineProperty', P);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
// 7.3.8 #sec-deletepropertyorthrow
|
||||
export function DeletePropertyOrThrow(O, P) {
|
||||
Assert(Type(O) === 'Object');
|
||||
Assert(IsPropertyKey(P));
|
||||
const success = Q(O.Delete(P));
|
||||
if (success === Value.false) {
|
||||
return surroundingAgent.Throw('TypeError', 'CannotDeleteProperty', P);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
// 7.3.9 #sec-getmethod
|
||||
export function GetMethod(V, P) {
|
||||
Assert(IsPropertyKey(P));
|
||||
const func = Q(GetV(V, P));
|
||||
if (func === Value.null || func === Value.undefined) {
|
||||
return Value.undefined;
|
||||
}
|
||||
if (IsCallable(func) === Value.false) {
|
||||
return surroundingAgent.Throw('TypeError', 'NotAFunction', func);
|
||||
}
|
||||
return func;
|
||||
}
|
||||
|
||||
// 7.3.10 #sec-hasproperty
|
||||
export function HasProperty(O, P) {
|
||||
Assert(Type(O) === 'Object');
|
||||
Assert(IsPropertyKey(P));
|
||||
return Q(O.HasProperty(P));
|
||||
}
|
||||
|
||||
// 7.3.11 #sec-hasownproperty
|
||||
export function HasOwnProperty(O, P) {
|
||||
Assert(Type(O) === 'Object');
|
||||
Assert(IsPropertyKey(P));
|
||||
const desc = Q(O.GetOwnProperty(P));
|
||||
if (desc === Value.undefined) {
|
||||
return Value.false;
|
||||
}
|
||||
return Value.true;
|
||||
}
|
||||
|
||||
// 7.3.12 #sec-call
|
||||
export function Call(F, V, argumentsList) {
|
||||
if (!argumentsList) {
|
||||
argumentsList = [];
|
||||
}
|
||||
Assert(argumentsList.every((a) => a instanceof Value));
|
||||
|
||||
if (IsCallable(F) === Value.false) {
|
||||
return surroundingAgent.Throw('TypeError', 'NotAFunction', F);
|
||||
}
|
||||
|
||||
return EnsureCompletion(Q(F.Call(V, argumentsList)));
|
||||
}
|
||||
|
||||
// 7.3.13 #sec-construct
|
||||
export function Construct(F, argumentsList, newTarget) {
|
||||
if (!newTarget) {
|
||||
newTarget = F;
|
||||
}
|
||||
if (!argumentsList) {
|
||||
argumentsList = [];
|
||||
}
|
||||
Assert(IsConstructor(F) === Value.true);
|
||||
Assert(IsConstructor(newTarget) === Value.true);
|
||||
return Q(F.Construct(argumentsList, newTarget));
|
||||
}
|
||||
|
||||
// 7.3.14 #sec-setintegritylevel
|
||||
export function SetIntegrityLevel(O, level) {
|
||||
Assert(Type(O) === 'Object');
|
||||
Assert(level === 'sealed' || level === 'frozen');
|
||||
const status = Q(O.PreventExtensions());
|
||||
if (status === Value.false) {
|
||||
return Value.false;
|
||||
}
|
||||
const keys = Q(O.OwnPropertyKeys());
|
||||
if (level === 'sealed') {
|
||||
for (const k of keys) {
|
||||
Q(DefinePropertyOrThrow(O, k, Descriptor({ Configurable: Value.false })));
|
||||
}
|
||||
} else if (level === 'frozen') {
|
||||
for (const k of keys) {
|
||||
const currentDesc = Q(O.GetOwnProperty(k));
|
||||
if (currentDesc !== Value.undefined) {
|
||||
let desc;
|
||||
if (IsAccessorDescriptor(currentDesc) === true) {
|
||||
desc = Descriptor({ Configurable: Value.false });
|
||||
} else {
|
||||
desc = Descriptor({ Configurable: Value.false, Writable: Value.false });
|
||||
}
|
||||
Q(DefinePropertyOrThrow(O, k, desc));
|
||||
}
|
||||
}
|
||||
}
|
||||
return Value.true;
|
||||
}
|
||||
|
||||
// 7.3.15 #sec-testintegritylevel
|
||||
export function TestIntegrityLevel(O, level) {
|
||||
Assert(Type(O) === 'Object');
|
||||
Assert(level === 'sealed' || level === 'frozen');
|
||||
const extensible = Q(IsExtensible(O));
|
||||
if (extensible === Value.true) {
|
||||
return Value.false;
|
||||
}
|
||||
const keys = Q(O.OwnPropertyKeys());
|
||||
for (const k of keys) {
|
||||
const currentDesc = Q(O.GetOwnProperty(k));
|
||||
if (currentDesc !== Value.undefined) {
|
||||
if (currentDesc.Configurable === Value.true) {
|
||||
return Value.false;
|
||||
}
|
||||
if (level === 'frozen' && IsDataDescriptor(currentDesc)) {
|
||||
if (currentDesc.Writable === Value.true) {
|
||||
return Value.false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return Value.true;
|
||||
}
|
||||
|
||||
// 7.3.16 #sec-createarrayfromlist
|
||||
export function CreateArrayFromList(elements) {
|
||||
Assert(elements.every((e) => e instanceof Value));
|
||||
const array = X(ArrayCreate(new Value(0)));
|
||||
let n = 0;
|
||||
for (const e of elements) {
|
||||
const nStr = X(ToString(new Value(n)));
|
||||
const status = X(CreateDataProperty(array, nStr, e));
|
||||
Assert(status === Value.true);
|
||||
n += 1;
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
// 7.3.17 #sec-lengthofarraylike
|
||||
export function LengthOfArrayLike(obj) {
|
||||
Assert(Type(obj) === 'Object');
|
||||
return Q(ToLength(Q(Get(obj, new Value('length')))));
|
||||
}
|
||||
|
||||
// 7.3.17 #sec-createlistfromarraylike
|
||||
export function CreateListFromArrayLike(obj, elementTypes) {
|
||||
if (!elementTypes) {
|
||||
elementTypes = ['Undefined', 'Null', 'Boolean', 'String', 'Symbol', 'Number', 'Object'];
|
||||
}
|
||||
if (Type(obj) !== 'Object') {
|
||||
return surroundingAgent.Throw('TypeError', 'NotAnObject', obj);
|
||||
}
|
||||
const len = Q(LengthOfArrayLike(obj)).numberValue();
|
||||
const list = [];
|
||||
let index = 0;
|
||||
while (index < len) {
|
||||
const indexName = X(ToString(new Value(index)));
|
||||
const next = Q(Get(obj, indexName));
|
||||
if (!elementTypes.includes(Type(next))) {
|
||||
return surroundingAgent.Throw('TypeError', 'NotPropertyName', next);
|
||||
}
|
||||
list.push(next);
|
||||
index += 1;
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
// 7.3.18 #sec-invoke
|
||||
export function Invoke(V, P, argumentsList) {
|
||||
Assert(IsPropertyKey(P));
|
||||
if (!argumentsList) {
|
||||
argumentsList = [];
|
||||
}
|
||||
const func = Q(GetV(V, P));
|
||||
return Q(Call(func, V, argumentsList));
|
||||
}
|
||||
|
||||
// 7.3.19 #sec-ordinaryhasinstance
|
||||
export function OrdinaryHasInstance(C, O) {
|
||||
if (IsCallable(C) === Value.false) {
|
||||
return Value.false;
|
||||
}
|
||||
if ('BoundTargetFunction' in C) {
|
||||
const BC = C.BoundTargetFunction;
|
||||
return Q(InstanceofOperator(O, BC));
|
||||
}
|
||||
if (Type(O) !== 'Object') {
|
||||
return Value.false;
|
||||
}
|
||||
const P = Q(Get(C, new Value('prototype')));
|
||||
if (Type(P) !== 'Object') {
|
||||
return surroundingAgent.Throw('TypeError', 'NotAnObject', P);
|
||||
}
|
||||
while (true) {
|
||||
O = Q(O.GetPrototypeOf());
|
||||
if (O === Value.null) {
|
||||
return Value.false;
|
||||
}
|
||||
if (SameValue(P, O) === Value.true) {
|
||||
return Value.true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 7.3.20 #sec-speciesconstructor
|
||||
export function SpeciesConstructor(O, defaultConstructor) {
|
||||
Assert(Type(O) === 'Object');
|
||||
const C = Q(Get(O, new Value('constructor')));
|
||||
if (C === Value.undefined) {
|
||||
return defaultConstructor;
|
||||
}
|
||||
if (Type(C) !== 'Object') {
|
||||
return surroundingAgent.Throw('TypeError', 'NotAnObject', C);
|
||||
}
|
||||
const S = Q(Get(C, wellKnownSymbols.species));
|
||||
if (S === Value.undefined || S === Value.null) {
|
||||
return defaultConstructor;
|
||||
}
|
||||
if (IsConstructor(S) === Value.true) {
|
||||
return S;
|
||||
}
|
||||
return surroundingAgent.Throw('TypeError', 'SpeciesNotConstructor');
|
||||
}
|
||||
|
||||
// 7.3.21 #sec-enumerableownpropertynames
|
||||
export function EnumerableOwnPropertyNames(O, kind) {
|
||||
Assert(Type(O) === 'Object');
|
||||
const ownKeys = Q(O.OwnPropertyKeys());
|
||||
const properties = [];
|
||||
for (const key of ownKeys) {
|
||||
if (Type(key) === 'String') {
|
||||
const desc = Q(O.GetOwnProperty(key));
|
||||
if (desc !== Value.undefined && desc.Enumerable === Value.true) {
|
||||
if (kind === 'key') {
|
||||
properties.push(key);
|
||||
} else {
|
||||
const value = Q(Get(O, key));
|
||||
if (kind === 'value') {
|
||||
properties.push(value);
|
||||
} else {
|
||||
Assert(kind === 'key+value');
|
||||
const entry = X(CreateArrayFromList([key, value]));
|
||||
properties.push(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
||||
// 7.3.22 #sec-getfunctionrealm
|
||||
export function GetFunctionRealm(obj) {
|
||||
Assert(X(IsCallable(obj)) === Value.true);
|
||||
if ('Realm' in obj) {
|
||||
return obj.Realm;
|
||||
}
|
||||
|
||||
if ('BoundTargetFunction' in obj) {
|
||||
const target = obj.BoundTargetFunction;
|
||||
return Q(GetFunctionRealm(target));
|
||||
}
|
||||
|
||||
if (isProxyExoticObject(obj)) {
|
||||
if (obj.ProxyHandler === Value.null) {
|
||||
return surroundingAgent.Throw('TypeError', 'ProxyRevoked', 'GetFunctionRealm');
|
||||
}
|
||||
const proxyTarget = obj.ProxyTarget;
|
||||
return Q(GetFunctionRealm(proxyTarget));
|
||||
}
|
||||
|
||||
return surroundingAgent.currentRealmRecord;
|
||||
}
|
||||
|
||||
// 7.3.23 #sec-copydataproperties
|
||||
export function CopyDataProperties(target, source, excludedItems) {
|
||||
Assert(Type(target) === 'Object');
|
||||
Assert(excludedItems.every((i) => IsPropertyKey(i)));
|
||||
if (source === Value.undefined || source === Value.null) {
|
||||
return target;
|
||||
}
|
||||
const from = X(ToObject(source));
|
||||
const keys = Q(from.OwnPropertyKeys());
|
||||
for (const nextKey of keys) {
|
||||
let excluded = false;
|
||||
for (const e of excludedItems) {
|
||||
if (SameValue(e, nextKey) === Value.true) {
|
||||
excluded = true;
|
||||
}
|
||||
}
|
||||
if (excluded === false) {
|
||||
const desc = Q(from.GetOwnProperty(nextKey));
|
||||
if (desc !== Value.undefined && desc.Enumerable === Value.true) {
|
||||
const propValue = Q(Get(from, nextKey));
|
||||
X(CreateDataProperty(target, nextKey, propValue));
|
||||
}
|
||||
}
|
||||
}
|
||||
return target;
|
||||
}
|
||||
@@ -1,405 +0,0 @@
|
||||
import {
|
||||
Descriptor,
|
||||
ObjectValue,
|
||||
Type,
|
||||
Value,
|
||||
} from '../value.mjs';
|
||||
import { Q, X } from '../completion.mjs';
|
||||
import {
|
||||
Assert,
|
||||
Call,
|
||||
CreateDataProperty,
|
||||
Get,
|
||||
GetFunctionRealm,
|
||||
IsAccessorDescriptor,
|
||||
IsCallable,
|
||||
IsDataDescriptor,
|
||||
IsExtensible,
|
||||
IsGenericDescriptor,
|
||||
IsPropertyKey,
|
||||
SameValue,
|
||||
MakeBasicObject,
|
||||
isArrayIndex,
|
||||
} from './all.mjs';
|
||||
|
||||
// 9.1.1.1 OrdinaryGetPrototypeOf
|
||||
export function OrdinaryGetPrototypeOf(O) {
|
||||
return O.Prototype;
|
||||
}
|
||||
|
||||
// 9.1.2.1 OrdinarySetPrototypeOf
|
||||
export function OrdinarySetPrototypeOf(O, V) {
|
||||
Assert(Type(V) === 'Object' || Type(V) === 'Null');
|
||||
|
||||
const current = O.Prototype;
|
||||
if (SameValue(V, current) === Value.true) {
|
||||
return Value.true;
|
||||
}
|
||||
const extensible = O.Extensible;
|
||||
if (extensible === Value.false) {
|
||||
return Value.false;
|
||||
}
|
||||
let p = V;
|
||||
let done = false;
|
||||
while (done === false) {
|
||||
if (p === Value.null) {
|
||||
done = true;
|
||||
} else if (SameValue(p, O) === Value.true) {
|
||||
return Value.false;
|
||||
} else if (p.GetPrototypeOf !== ObjectValue.prototype.GetPrototypeOf) {
|
||||
done = true;
|
||||
} else {
|
||||
p = p.Prototype;
|
||||
}
|
||||
}
|
||||
O.Prototype = V;
|
||||
return Value.true;
|
||||
}
|
||||
|
||||
// 9.1.3.1 OrdinaryIsExtensible
|
||||
export function OrdinaryIsExtensible(O) {
|
||||
return O.Extensible;
|
||||
}
|
||||
|
||||
// 9.1.4.1 OrdinaryPreventExtensions
|
||||
export function OrdinaryPreventExtensions(O) {
|
||||
O.Extensible = Value.false;
|
||||
return Value.true;
|
||||
}
|
||||
|
||||
// 9.1.5.1 OrdinaryGetOwnProperty
|
||||
export function OrdinaryGetOwnProperty(O, P) {
|
||||
Assert(IsPropertyKey(P));
|
||||
|
||||
if (!O.properties.has(P)) {
|
||||
return Value.undefined;
|
||||
}
|
||||
|
||||
const D = Descriptor({});
|
||||
|
||||
const x = O.properties.get(P);
|
||||
|
||||
if (IsDataDescriptor(x)) {
|
||||
D.Value = x.Value;
|
||||
D.Writable = x.Writable;
|
||||
} else if (IsAccessorDescriptor(x)) {
|
||||
D.Get = x.Get;
|
||||
D.Set = x.Set;
|
||||
}
|
||||
D.Enumerable = x.Enumerable;
|
||||
D.Configurable = x.Configurable;
|
||||
|
||||
return D;
|
||||
}
|
||||
|
||||
// 9.1.6.1 OrdinaryDefineOwnProperty
|
||||
export function OrdinaryDefineOwnProperty(O, P, Desc) {
|
||||
const current = Q(O.GetOwnProperty(P));
|
||||
const extensible = Q(IsExtensible(O));
|
||||
return ValidateAndApplyPropertyDescriptor(O, P, extensible, Desc, current);
|
||||
}
|
||||
|
||||
// 9.1.6.2 #sec-iscompatiblepropertydescriptor
|
||||
export function IsCompatiblePropertyDescriptor(Extensible, Desc, Current) {
|
||||
return ValidateAndApplyPropertyDescriptor(
|
||||
Value.undefined, Value.undefined, Extensible, Desc, Current,
|
||||
);
|
||||
}
|
||||
|
||||
// 9.1.6.3 ValidateAndApplyPropertyDescriptor
|
||||
export function ValidateAndApplyPropertyDescriptor(O, P, extensible, Desc, current) {
|
||||
Assert(O === Value.undefined || IsPropertyKey(P));
|
||||
|
||||
if (current === Value.undefined) {
|
||||
if (extensible === Value.false) {
|
||||
return Value.false;
|
||||
}
|
||||
|
||||
Assert(extensible === Value.true);
|
||||
|
||||
if (IsGenericDescriptor(Desc) || IsDataDescriptor(Desc)) {
|
||||
if (Type(O) !== 'Undefined') {
|
||||
O.properties.set(P, Descriptor({
|
||||
Value: Desc.Value === undefined ? Value.undefined : Desc.Value,
|
||||
Writable: Desc.Writable === undefined ? Value.false : Desc.Writable,
|
||||
Enumerable: Desc.Enumerable === undefined ? Value.false : Desc.Enumerable,
|
||||
Configurable: Desc.Configurable === undefined ? Value.false : Desc.Configurable,
|
||||
}));
|
||||
}
|
||||
} else {
|
||||
Assert(IsAccessorDescriptor(Desc));
|
||||
if (Type(O) !== 'Undefined') {
|
||||
O.properties.set(P, Descriptor({
|
||||
Get: Desc.Get === undefined ? Value.undefined : Desc.Get,
|
||||
Set: Desc.Set === undefined ? Value.undefined : Desc.Set,
|
||||
Enumerable: Desc.Enumerable === undefined ? Value.false : Desc.Enumerable,
|
||||
Configurable: Desc.Configurable === undefined ? Value.false : Desc.Configurable,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
return Value.true;
|
||||
}
|
||||
|
||||
if (Desc.everyFieldIsAbsent()) {
|
||||
return Value.true;
|
||||
}
|
||||
|
||||
if (current.Configurable === Value.false) {
|
||||
if (Desc.Configurable !== undefined && Desc.Configurable === Value.true) {
|
||||
return Value.false;
|
||||
}
|
||||
|
||||
if (Desc.Enumerable !== undefined && Desc.Enumerable !== current.Enumerable) {
|
||||
return Value.false;
|
||||
}
|
||||
}
|
||||
|
||||
if (IsGenericDescriptor(Desc)) {
|
||||
// No further validation is required.
|
||||
} else if (IsDataDescriptor(current) !== IsDataDescriptor(Desc)) {
|
||||
if (current.Configurable === Value.false) {
|
||||
return Value.false;
|
||||
}
|
||||
if (IsDataDescriptor(current)) {
|
||||
if (Type(O) !== 'Undefined') {
|
||||
const entry = O.properties.get(P);
|
||||
entry.Value = undefined;
|
||||
entry.Writable = undefined;
|
||||
entry.Get = Value.undefined;
|
||||
entry.Set = Value.undefined;
|
||||
}
|
||||
} else {
|
||||
if (Type(O) !== 'Undefined') {
|
||||
const entry = O.properties.get(P);
|
||||
entry.Get = undefined;
|
||||
entry.Set = undefined;
|
||||
entry.Value = Value.undefined;
|
||||
entry.Writable = Value.false;
|
||||
}
|
||||
}
|
||||
} else if (IsDataDescriptor(current) && IsDataDescriptor(Desc)) {
|
||||
if (current.Configurable === Value.false && current.Writable === Value.false) {
|
||||
if (Desc.Writable !== undefined && Desc.Writable === Value.true) {
|
||||
return Value.false;
|
||||
}
|
||||
if (Desc.Value !== undefined && SameValue(Desc.Value, current.Value) === Value.false) {
|
||||
return Value.false;
|
||||
}
|
||||
return Value.true;
|
||||
}
|
||||
} else {
|
||||
Assert(IsAccessorDescriptor(current) && IsAccessorDescriptor(Desc));
|
||||
if (current.Configurable === Value.false) {
|
||||
if (Desc.Set !== undefined && SameValue(Desc.Set, current.Set) === Value.false) {
|
||||
return Value.false;
|
||||
}
|
||||
if (Desc.Get !== undefined && SameValue(Desc.Get, current.Get) === Value.false) {
|
||||
return Value.false;
|
||||
}
|
||||
return Value.true;
|
||||
}
|
||||
}
|
||||
|
||||
if (Type(O) !== 'Undefined') {
|
||||
const target = O.properties.get(P);
|
||||
if (Desc.Value !== undefined) {
|
||||
target.Value = Desc.Value;
|
||||
}
|
||||
if (Desc.Writable !== undefined) {
|
||||
target.Writable = Desc.Writable;
|
||||
}
|
||||
if (Desc.Get !== undefined) {
|
||||
target.Get = Desc.Get;
|
||||
}
|
||||
if (Desc.Set !== undefined) {
|
||||
target.Set = Desc.Set;
|
||||
}
|
||||
if (Desc.Enumerable !== undefined) {
|
||||
target.Enumerable = Desc.Enumerable;
|
||||
}
|
||||
if (Desc.Configurable !== undefined) {
|
||||
target.Configurable = Desc.Configurable;
|
||||
}
|
||||
}
|
||||
|
||||
return Value.true;
|
||||
}
|
||||
|
||||
// 9.1.7.1 OrdinaryHasProperty
|
||||
export function OrdinaryHasProperty(O, P) {
|
||||
Assert(IsPropertyKey(P));
|
||||
|
||||
const hasOwn = Q(O.GetOwnProperty(P));
|
||||
if (Type(hasOwn) !== 'Undefined') {
|
||||
return Value.true;
|
||||
}
|
||||
const parent = Q(O.GetPrototypeOf());
|
||||
if (Type(parent) !== 'Null') {
|
||||
return Q(parent.HasProperty(P));
|
||||
}
|
||||
return Value.false;
|
||||
}
|
||||
|
||||
// 9.1.8.1
|
||||
export function OrdinaryGet(O, P, Receiver) {
|
||||
Assert(IsPropertyKey(P));
|
||||
|
||||
const desc = Q(O.GetOwnProperty(P));
|
||||
if (Type(desc) === 'Undefined') {
|
||||
const parent = Q(O.GetPrototypeOf());
|
||||
if (Type(parent) === 'Null') {
|
||||
return Value.undefined;
|
||||
}
|
||||
return Q(parent.Get(P, Receiver));
|
||||
}
|
||||
if (IsDataDescriptor(desc)) {
|
||||
return desc.Value;
|
||||
}
|
||||
Assert(IsAccessorDescriptor(desc));
|
||||
const getter = desc.Get;
|
||||
if (Type(getter) === 'Undefined') {
|
||||
return Value.undefined;
|
||||
}
|
||||
return Q(Call(getter, Receiver));
|
||||
}
|
||||
|
||||
// 9.1.9.1 OrdinarySet
|
||||
export function OrdinarySet(O, P, V, Receiver) {
|
||||
Assert(IsPropertyKey(P));
|
||||
const ownDesc = Q(O.GetOwnProperty(P));
|
||||
return OrdinarySetWithOwnDescriptor(O, P, V, Receiver, ownDesc);
|
||||
}
|
||||
|
||||
// 9.1.9.2 OrdinarySetWithOwnDescriptor
|
||||
export function OrdinarySetWithOwnDescriptor(O, P, V, Receiver, ownDesc) {
|
||||
Assert(IsPropertyKey(P));
|
||||
|
||||
if (Type(ownDesc) === 'Undefined') {
|
||||
const parent = Q(O.GetPrototypeOf());
|
||||
if (Type(parent) !== 'Null') {
|
||||
return Q(parent.Set(P, V, Receiver));
|
||||
}
|
||||
ownDesc = Descriptor({
|
||||
Value: Value.undefined,
|
||||
Writable: Value.true,
|
||||
Enumerable: Value.true,
|
||||
Configurable: Value.true,
|
||||
});
|
||||
}
|
||||
|
||||
if (IsDataDescriptor(ownDesc)) {
|
||||
if (ownDesc.Writable !== undefined && ownDesc.Writable === Value.false) {
|
||||
return Value.false;
|
||||
}
|
||||
if (Type(Receiver) !== 'Object') {
|
||||
return Value.false;
|
||||
}
|
||||
|
||||
const existingDescriptor = Q(Receiver.GetOwnProperty(P));
|
||||
if (Type(existingDescriptor) !== 'Undefined') {
|
||||
if (IsAccessorDescriptor(existingDescriptor)) {
|
||||
return Value.false;
|
||||
}
|
||||
if (existingDescriptor.Writable === Value.false) {
|
||||
return Value.false;
|
||||
}
|
||||
const valueDesc = Descriptor({ Value: V });
|
||||
return Q(Receiver.DefineOwnProperty(P, valueDesc));
|
||||
}
|
||||
return CreateDataProperty(Receiver, P, V);
|
||||
}
|
||||
|
||||
Assert(IsAccessorDescriptor(ownDesc));
|
||||
const setter = ownDesc.Set;
|
||||
if (setter === undefined || Type(setter) === 'Undefined') {
|
||||
return Value.false;
|
||||
}
|
||||
Q(Call(setter, Receiver, [V]));
|
||||
return Value.true;
|
||||
}
|
||||
|
||||
// 9.1.10.1 OrdinaryDelete
|
||||
export function OrdinaryDelete(O, P) {
|
||||
Assert(IsPropertyKey(P));
|
||||
const desc = Q(O.GetOwnProperty(P));
|
||||
if (Type(desc) === 'Undefined') {
|
||||
return Value.true;
|
||||
}
|
||||
if (desc.Configurable === Value.true) {
|
||||
O.properties.delete(P);
|
||||
return Value.true;
|
||||
}
|
||||
return Value.false;
|
||||
}
|
||||
|
||||
// 9.1.11.1
|
||||
export function OrdinaryOwnPropertyKeys(O) {
|
||||
const keys = [];
|
||||
|
||||
// For each own property key P of O that is an array index, in ascending numeric index order, do
|
||||
// Add P as the last element of keys.
|
||||
for (const P of O.properties.keys()) {
|
||||
if (isArrayIndex(P)) {
|
||||
keys.push(P);
|
||||
}
|
||||
}
|
||||
keys.sort((a, b) => Number.parseInt(a.stringValue(), 10) - Number.parseInt(b.stringValue(), 10));
|
||||
|
||||
// For each own property key P of O such that Type(P) is String and
|
||||
// P is not an array index, in ascending chronological order of property creation, do
|
||||
// Add P as the last element of keys.
|
||||
for (const P of O.properties.keys()) {
|
||||
if (Type(P) === 'String' && isArrayIndex(P) === false) {
|
||||
keys.push(P);
|
||||
}
|
||||
}
|
||||
|
||||
// For each own property key P of O such that Type(P) is Symbol,
|
||||
// in ascending chronological order of property creation, do
|
||||
// Add P as the last element of keys.
|
||||
for (const P of O.properties.keys()) {
|
||||
if (Type(P) === 'Symbol') {
|
||||
keys.push(P);
|
||||
}
|
||||
}
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
// #sec-ordinaryobjectcreate
|
||||
export function OrdinaryObjectCreate(proto, additionalInternalSlotsList) {
|
||||
// 1. Let internalSlotsList be « [[Prototype]], [[Extensible]] ».
|
||||
const internalSlotsList = ['Prototype', 'Extensible'];
|
||||
// 2. If additionalInternalSlotsList is present, append each of its elements to internalSlotsList.
|
||||
if (additionalInternalSlotsList !== undefined) {
|
||||
internalSlotsList.push(...additionalInternalSlotsList);
|
||||
}
|
||||
// 3. Let O be ! MakeBasicObject(internalSlotsList).
|
||||
const O = X(MakeBasicObject(internalSlotsList));
|
||||
// 4. Set O.[[Prototype]] to proto.
|
||||
O.Prototype = proto;
|
||||
// 5. Return O.
|
||||
return O;
|
||||
}
|
||||
|
||||
// 9.1.13 OrdinaryCreateFromConstructor
|
||||
export function OrdinaryCreateFromConstructor(constructor, intrinsicDefaultProto, internalSlotsList) {
|
||||
// Assert: intrinsicDefaultProto is a String value that is this specification's name of an intrinsic object.
|
||||
const proto = Q(GetPrototypeFromConstructor(constructor, intrinsicDefaultProto));
|
||||
return OrdinaryObjectCreate(proto, internalSlotsList);
|
||||
}
|
||||
|
||||
// 9.1.14 GetPrototypeFromConstructor
|
||||
export function GetPrototypeFromConstructor(constructor, intrinsicDefaultProto) {
|
||||
// Assert: intrinsicDefaultProto is a String value that
|
||||
// is this specification's name of an intrinsic object.
|
||||
Assert(IsCallable(constructor) === Value.true);
|
||||
let proto = Q(Get(constructor, new Value('prototype')));
|
||||
if (Type(proto) !== 'Object') {
|
||||
const realm = Q(GetFunctionRealm(constructor));
|
||||
proto = realm.Intrinsics[intrinsicDefaultProto];
|
||||
}
|
||||
return proto;
|
||||
}
|
||||
|
||||
@@ -1,423 +0,0 @@
|
||||
import {
|
||||
HostMakeJobCallback,
|
||||
HostCallJobCallback,
|
||||
HostEnqueuePromiseJob,
|
||||
HostPromiseRejectionTracker,
|
||||
surroundingAgent,
|
||||
} from '../engine.mjs';
|
||||
import { Type, Value } from '../value.mjs';
|
||||
import {
|
||||
Completion,
|
||||
AbruptCompletion,
|
||||
NormalCompletion,
|
||||
Q,
|
||||
X,
|
||||
ThrowCompletion,
|
||||
} from '../completion.mjs';
|
||||
import {
|
||||
Assert,
|
||||
Call,
|
||||
Construct,
|
||||
CreateBuiltinFunction,
|
||||
Get,
|
||||
IsCallable,
|
||||
IsConstructor,
|
||||
SameValue,
|
||||
SetFunctionLength,
|
||||
SetFunctionName,
|
||||
GetFunctionRealm,
|
||||
isFunctionObject,
|
||||
} from './all.mjs';
|
||||
|
||||
// This file covers abstract operations defined in
|
||||
// 25.6 #sec-promise-objects
|
||||
|
||||
// 25.6.1.1 #sec-promisecapability-records
|
||||
export class PromiseCapabilityRecord {
|
||||
constructor() {
|
||||
this.Promise = Value.undefined;
|
||||
this.Resolve = Value.undefined;
|
||||
this.Reject = Value.undefined;
|
||||
}
|
||||
}
|
||||
|
||||
// 25.6.1.2 #sec-promisereaction-records
|
||||
export class PromiseReactionRecord {
|
||||
constructor(O) {
|
||||
Assert(O.Capability instanceof PromiseCapabilityRecord
|
||||
|| O.Capability === Value.undefined);
|
||||
Assert(O.Type === 'Fulfill' || O.Type === 'Reject');
|
||||
Assert(O.Handler === undefined
|
||||
|| isFunctionObject(O.Handler.Callback));
|
||||
this.Capability = O.Capability;
|
||||
this.Type = O.Type;
|
||||
this.Handler = O.Handler;
|
||||
}
|
||||
}
|
||||
|
||||
// 25.6.1.3 #sec-createresolvingfunctions
|
||||
export function CreateResolvingFunctions(promise) {
|
||||
const alreadyResolved = { Value: false };
|
||||
const stepsResolve = PromiseResolveFunctions;
|
||||
const resolve = X(CreateBuiltinFunction(stepsResolve, ['Promise', 'AlreadyResolved']));
|
||||
SetFunctionLength(resolve, new Value(1));
|
||||
SetFunctionName(resolve, new Value(''));
|
||||
resolve.Promise = promise;
|
||||
resolve.AlreadyResolved = alreadyResolved;
|
||||
const stepsReject = PromiseRejectFunctions;
|
||||
const reject = X(CreateBuiltinFunction(stepsReject, ['Promise', 'AlreadyResolved']));
|
||||
SetFunctionLength(reject, new Value(1));
|
||||
SetFunctionName(reject, new Value(''));
|
||||
reject.Promise = promise;
|
||||
reject.AlreadyResolved = alreadyResolved;
|
||||
return {
|
||||
Resolve: resolve,
|
||||
Reject: reject,
|
||||
};
|
||||
}
|
||||
|
||||
// 25.6.1.3.1 #sec-promise-reject-functions
|
||||
function PromiseRejectFunctions([reason = Value.undefined]) {
|
||||
const F = this;
|
||||
|
||||
Assert('Promise' in F && Type(F.Promise) === 'Object');
|
||||
const promise = F.Promise;
|
||||
const alreadyResolved = F.AlreadyResolved;
|
||||
if (alreadyResolved.Value === true) {
|
||||
return Value.undefined;
|
||||
}
|
||||
alreadyResolved.Value = true;
|
||||
return RejectPromise(promise, reason);
|
||||
}
|
||||
|
||||
// #sec-newpromiseresolvethenablejob
|
||||
function NewPromiseResolveThenableJob(promiseToResolve, thenable, then) {
|
||||
// 1. Let job be a new Job abstract closure with no parameters that captures
|
||||
// promiseToResolve, thenable, and then and performs the following steps when called:
|
||||
const job = () => {
|
||||
// a. Let resolvingFunctions be CreateResolvingFunctions(promiseToResolve).
|
||||
const resolvingFunctions = CreateResolvingFunctions(promiseToResolve);
|
||||
// b. Let thenCallResult be HostCallJobCallback(then, thenable, « resolvingFunctions.[[Resolve]], resolvingFunctions.[[Reject]] »).
|
||||
const thenCallResult = HostCallJobCallback(then, thenable, [resolvingFunctions.Resolve, resolvingFunctions.Reject]);
|
||||
// c. If thenCallResult is an abrupt completion, then
|
||||
if (thenCallResult instanceof AbruptCompletion) {
|
||||
// i .Let status be Call(resolvingFunctions.[[Reject]], undefined, « thenCallResult.[[Value]] »).
|
||||
const status = Call(resolvingFunctions.Reject, Value.undefined, [thenCallResult.Value]);
|
||||
// ii. Return Completion(status).
|
||||
return Completion(status);
|
||||
}
|
||||
// d. Return Completion(thenCallResult).
|
||||
return Completion(thenCallResult);
|
||||
};
|
||||
// 2. Let getThenRealmResult be GetFunctionRealm(then.[[Callback]]).
|
||||
const getThenRealmResult = GetFunctionRealm(then.Callback);
|
||||
// 3. If getThenRealmResult is a normal completion, then let thenRealm be getThenRealmResult.[[Value]].
|
||||
let thenRealm;
|
||||
if (getThenRealmResult instanceof NormalCompletion) {
|
||||
thenRealm = getThenRealmResult.Value;
|
||||
} else {
|
||||
// 4. Else, let _thenRealm_ be the current Realm Record.
|
||||
thenRealm = surroundingAgent.currentRealmRecord;
|
||||
}
|
||||
// 5. NOTE: _thenRealm_ is never *null*. When _then_.[[Callback]] is a revoked Proxy and no code runs, _thenRealm_ is used to create error objects.
|
||||
// 6. Return { [[Job]]: job, [[Realm]]: thenRealm }.
|
||||
return { Job: job, Realm: thenRealm };
|
||||
}
|
||||
|
||||
// 25.6.1.3.2 #sec-promise-resolve-functions
|
||||
function PromiseResolveFunctions([resolution = Value.undefined]) {
|
||||
// 1. Let F be the active function object.
|
||||
const F = this;
|
||||
// 2. Assert: F has a [[Promise]] internal slot whose value is an Object.
|
||||
Assert('Promise' in F && Type(F.Promise) === 'Object');
|
||||
// 3. Let promise be F.[[Promise]].
|
||||
const promise = F.Promise;
|
||||
// 4. Let alreadyResolved be F.[[AlreadyResolved]].
|
||||
const alreadyResolved = F.AlreadyResolved;
|
||||
// 5. If alreadyResolved.[[Value]] is true, return undefined.
|
||||
if (alreadyResolved.Value === true) {
|
||||
return Value.undefined;
|
||||
}
|
||||
// 6. Set alreadyResolved.[[Value]] to true.
|
||||
alreadyResolved.Value = true;
|
||||
// 7. If SameValue(resolution, promise) is true, then
|
||||
if (SameValue(resolution, promise) === Value.true) {
|
||||
// a. Let selfResolutionError be a newly created TypeError object.
|
||||
const selfResolutionError = surroundingAgent.Throw('TypeError', 'CannotResolvePromiseWithItself').Value;
|
||||
// b. Return RejectPromise(promise, selfResolutionError).
|
||||
return RejectPromise(promise, selfResolutionError);
|
||||
}
|
||||
// 8. If Type(resolution) is not Object, then
|
||||
if (Type(resolution) !== 'Object') {
|
||||
// a. Return FulfillPromise(promise, resolution).
|
||||
return FulfillPromise(promise, resolution);
|
||||
}
|
||||
// 9. Let then be Get(resolution, "then").
|
||||
const then = Get(resolution, new Value('then'));
|
||||
// 10. If then is an abrupt completion, then
|
||||
if (then instanceof AbruptCompletion) {
|
||||
// a. Return RejectPromise(promise, then.[[Value]]).
|
||||
return RejectPromise(promise, then.Value);
|
||||
}
|
||||
// 11. Let thenAction be then.[[Value]].
|
||||
const thenAction = then.Value;
|
||||
// 12. If IsCallable(thenAction) is false, then
|
||||
if (IsCallable(thenAction) === Value.false) {
|
||||
// a. Return FulfillPromise(promise, resolution).
|
||||
return FulfillPromise(promise, resolution);
|
||||
}
|
||||
// 13. Let thenJobCallback be HostMakeJobCallback(thenAction).
|
||||
const thenJobCallback = HostMakeJobCallback(thenAction);
|
||||
// 14. Let job be NewPromiseResolveThenableJob(promise, resolution, thenJobCallback).
|
||||
const job = NewPromiseResolveThenableJob(promise, resolution, thenJobCallback);
|
||||
// 15. Perform HostEnqueuePromiseJob(job.[[Job]], job.[[Realm]]).
|
||||
HostEnqueuePromiseJob(job.Job, job.Realm);
|
||||
// 16. Return undefined.
|
||||
return Value.undefined;
|
||||
}
|
||||
|
||||
// 25.6.1.4 #sec-fulfillpromise
|
||||
function FulfillPromise(promise, value) {
|
||||
Assert(promise.PromiseState === 'pending');
|
||||
const reactions = promise.PromiseFulfillReactions;
|
||||
promise.PromiseResult = value;
|
||||
promise.PromiseFulfillReactions = undefined;
|
||||
promise.PromiseRejectReactions = undefined;
|
||||
promise.PromiseState = 'fulfilled';
|
||||
return TriggerPromiseReactions(reactions, value);
|
||||
}
|
||||
|
||||
// 25.6.1.5 #sec-newpromisecapability
|
||||
export function NewPromiseCapability(C) {
|
||||
if (IsConstructor(C) === Value.false) {
|
||||
return surroundingAgent.Throw('TypeError', 'NotAConstructor', C);
|
||||
}
|
||||
const promiseCapability = new PromiseCapabilityRecord();
|
||||
const steps = GetCapabilitiesExecutorFunctions;
|
||||
const executor = X(CreateBuiltinFunction(steps, ['Capability']));
|
||||
SetFunctionLength(executor, new Value(2));
|
||||
SetFunctionName(executor, new Value(''));
|
||||
executor.Capability = promiseCapability;
|
||||
const promise = Q(Construct(C, [executor]));
|
||||
if (IsCallable(promiseCapability.Resolve) === Value.false) {
|
||||
return surroundingAgent.Throw('TypeError', 'PromiseResolveFunction', promiseCapability.Resolve);
|
||||
}
|
||||
if (IsCallable(promiseCapability.Reject) === Value.false) {
|
||||
return surroundingAgent.Throw('TypeError', 'PromiseRejectFunction', promiseCapability.Reject);
|
||||
}
|
||||
promiseCapability.Promise = promise;
|
||||
return promiseCapability;
|
||||
}
|
||||
|
||||
// 25.6.1.5.1 #sec-getcapabilitiesexecutor-functions
|
||||
function GetCapabilitiesExecutorFunctions([resolve = Value.undefined, reject = Value.undefined]) {
|
||||
const F = this;
|
||||
|
||||
const promiseCapability = F.Capability;
|
||||
if (Type(promiseCapability.Resolve) !== 'Undefined') {
|
||||
return surroundingAgent.Throw('TypeError', 'PromiseCapabilityFunctionAlreadySet', 'resolve');
|
||||
}
|
||||
if (Type(promiseCapability.Reject) !== 'Undefined') {
|
||||
return surroundingAgent.Throw('TypeError', 'PromiseCapabilityFunctionAlreadySet', 'reject');
|
||||
}
|
||||
promiseCapability.Resolve = resolve;
|
||||
promiseCapability.Reject = reject;
|
||||
return Value.undefined;
|
||||
}
|
||||
|
||||
// 25.6.1.6 #sec-ispromise
|
||||
export function IsPromise(x) {
|
||||
if (Type(x) !== 'Object') {
|
||||
return Value.false;
|
||||
}
|
||||
if (!('PromiseState' in x)) {
|
||||
return Value.false;
|
||||
}
|
||||
return Value.true;
|
||||
}
|
||||
|
||||
// 25.6.1.7 #sec-rejectpromise
|
||||
function RejectPromise(promise, reason) {
|
||||
Assert(promise.PromiseState === 'pending');
|
||||
const reactions = promise.PromiseRejectReactions;
|
||||
promise.PromiseResult = reason;
|
||||
promise.PromiseFulfillReactions = undefined;
|
||||
promise.PromiseRejectReactions = undefined;
|
||||
promise.PromiseState = 'rejected';
|
||||
if (promise.PromiseIsHandled === Value.false) {
|
||||
HostPromiseRejectionTracker(promise, 'reject');
|
||||
}
|
||||
return TriggerPromiseReactions(reactions, reason);
|
||||
}
|
||||
|
||||
// #sec-triggerpromisereactions
|
||||
function TriggerPromiseReactions(reactions, argument) {
|
||||
// 1. For each reaction in reactions, in original insertion order, do
|
||||
reactions.forEach((reaction) => {
|
||||
// a. Let job be NewPromiseReactionJob(reaction, argument).
|
||||
const job = NewPromiseReactionJob(reaction, argument);
|
||||
// b. Perform HostEnqueuePromiseJob(job.[[Job]], job.[[Realm]]).
|
||||
HostEnqueuePromiseJob(job.Job, job.Realm);
|
||||
});
|
||||
// 2. Return undefined.
|
||||
return Value.undefined;
|
||||
}
|
||||
|
||||
// 25.6.4.5.1 #sec-promise-resolve
|
||||
export function PromiseResolve(C, x) {
|
||||
Assert(Type(C) === 'Object');
|
||||
if (IsPromise(x) === Value.true) {
|
||||
const xConstructor = Q(Get(x, new Value('constructor')));
|
||||
if (SameValue(xConstructor, C) === Value.true) {
|
||||
return x;
|
||||
}
|
||||
}
|
||||
const promiseCapability = Q(NewPromiseCapability(C));
|
||||
Q(Call(promiseCapability.Resolve, Value.undefined, [x]));
|
||||
return promiseCapability.Promise;
|
||||
}
|
||||
|
||||
// #sec-newpromisereactionjob
|
||||
function NewPromiseReactionJob(reaction, argument) {
|
||||
// 1. Let job be a new Job abstract closure with no parameters that captures
|
||||
// reaction and argument and performs the following steps when called:
|
||||
const job = () => {
|
||||
// a. Assert: reaction is a PromiseReaction Record.
|
||||
Assert(reaction instanceof PromiseReactionRecord);
|
||||
// b. Let promiseCapability be reaction.[[Capability]].
|
||||
const promiseCapability = reaction.Capability;
|
||||
// c. Let type be reaction.[[Type]].
|
||||
const type = reaction.Type;
|
||||
// d. Let handler be reaction.[[Handler]].
|
||||
const handler = reaction.Handler;
|
||||
let handlerResult;
|
||||
// e. If handler is empty, then
|
||||
if (handler === undefined) {
|
||||
// i. If type is Fulfill, let handlerResult be NormalCompletion(argument).
|
||||
if (type === 'Fulfill') {
|
||||
handlerResult = NormalCompletion(argument);
|
||||
} else {
|
||||
// 1. Assert: type is Reject.
|
||||
Assert(type === 'Reject');
|
||||
// 2. Let handlerResult be ThrowCompletion(argument).
|
||||
handlerResult = ThrowCompletion(argument);
|
||||
}
|
||||
} else {
|
||||
// f. Else, let handlerResult be HostCallJobCallback(handler, undefined, « argument »).
|
||||
handlerResult = HostCallJobCallback(handler, Value.undefined, [argument]);
|
||||
}
|
||||
// g. If promiseCapability is undefined, then
|
||||
if (promiseCapability === Value.undefined) {
|
||||
// i. Assert: handlerResult is not an abrupt completion.
|
||||
Assert(!(handlerResult instanceof AbruptCompletion));
|
||||
// ii. Return NormalCompletion(empty).
|
||||
return NormalCompletion(undefined);
|
||||
}
|
||||
let status;
|
||||
// h. If handlerResult is an abrupt completion, then
|
||||
if (handlerResult instanceof AbruptCompletion) {
|
||||
// i. Let status be Call(promiseCapability.[[Reject]], undefined, « handlerResult.[[Value]] »).
|
||||
status = Call(promiseCapability.Reject, Value.undefined, [handlerResult.Value]);
|
||||
} else {
|
||||
// ii. Let status be Call(promiseCapability.[[Resolve]], undefined, « handlerResult.[[Value]] »).
|
||||
status = Call(promiseCapability.Resolve, Value.undefined, [handlerResult.Value]);
|
||||
}
|
||||
// j. Return Completion(status).
|
||||
return Completion(status);
|
||||
};
|
||||
// 2. Let handlerRealm be null.
|
||||
let handlerRealm = Value.null;
|
||||
// 3. If reaction.[[Handler]] is not empty, then
|
||||
if (reaction.Handler !== undefined) {
|
||||
// a. Let getHandlerRealmResult be GetFunctionRealm(reaction.[[Handler]].[[Callback]]).
|
||||
const getHandlerRealmResult = GetFunctionRealm(reaction.Handler.Callback);
|
||||
// b. If getHandlerRealmResult is a normal completion, then set handlerRealm to getHandlerRealmResult.[[Value]].
|
||||
if (getHandlerRealmResult instanceof NormalCompletion) {
|
||||
handlerRealm = getHandlerRealmResult.Value;
|
||||
} else {
|
||||
// c. Else, set _handlerRealm_ to the current Realm Record.
|
||||
handlerRealm = surroundingAgent.currentRealmRecord;
|
||||
}
|
||||
// d. NOTE: _handlerRealm_ is never *null* unless the handler is *undefined*. When the handler
|
||||
// is a revoked Proxy and no ECMAScript code runs, _handlerRealm_ is used to create error objects.
|
||||
}
|
||||
// 4. Return { [[Job]]: job, [[Realm]]: handlerRealm }.
|
||||
return { Job: job, Realm: handlerRealm };
|
||||
}
|
||||
|
||||
// 25.6.5.4.1 #sec-performpromisethen
|
||||
export function PerformPromiseThen(promise, onFulfilled, onRejected, resultCapability) {
|
||||
// 1. Assert: IsPromise(promise) is true.
|
||||
Assert(IsPromise(promise) === Value.true);
|
||||
// 2. If resultCapability is not present, then
|
||||
if (resultCapability === undefined) {
|
||||
// a. Set resultCapability to undefined.
|
||||
resultCapability = Value.undefined;
|
||||
}
|
||||
let onFulfilledJobCallback;
|
||||
// 3. If IsCallable(onFulfilled) is false, then
|
||||
if (IsCallable(onFulfilled) === Value.false) {
|
||||
// a. Let onFulfilledJobCallback be empty.
|
||||
onFulfilledJobCallback = undefined;
|
||||
} else { // 4. Else,
|
||||
// a. Let onFulfilledJobCallback be HostMakeJobCallback(onFulfilled).
|
||||
onFulfilledJobCallback = HostMakeJobCallback(onFulfilled);
|
||||
}
|
||||
let onRejectedJobCallback;
|
||||
// 5. If IsCallable(onRejected) is false, then
|
||||
if (IsCallable(onRejected) === Value.false) {
|
||||
// a. Let onRejectedJobCallback be empty.
|
||||
onRejectedJobCallback = undefined;
|
||||
} else { // 6. Else,
|
||||
onRejectedJobCallback = HostMakeJobCallback(onRejected);
|
||||
}
|
||||
// 7. Let fulfillReaction be the PromiseReaction { [[Capability]]: resultCapability, [[Type]]: Fulfill, [[Handler]]: onFulfilled }.
|
||||
const fulfillReaction = new PromiseReactionRecord({
|
||||
Capability: resultCapability,
|
||||
Type: 'Fulfill',
|
||||
Handler: onFulfilledJobCallback,
|
||||
});
|
||||
// 8. Let rejectReaction be the PromiseReaction { [[Capability]]: resultCapability, [[Type]]: Reject, [[Handler]]: onRejected }.
|
||||
const rejectReaction = new PromiseReactionRecord({
|
||||
Capability: resultCapability,
|
||||
Type: 'Reject',
|
||||
Handler: onRejectedJobCallback,
|
||||
});
|
||||
// 9. If promise.[[PromiseState]] is pending, then
|
||||
if (promise.PromiseState === 'pending') {
|
||||
// a. Append fulfillReaction as the last element of the List that is promise.[[PromiseFulfillReactions]].
|
||||
promise.PromiseFulfillReactions.push(fulfillReaction);
|
||||
// b. Append rejectReaction as the last element of the List that is promise.[[PromiseRejectReactions]].
|
||||
promise.PromiseRejectReactions.push(rejectReaction);
|
||||
} else if (promise.PromiseState === 'fulfilled') {
|
||||
// a. Let value be promise.[[PromiseResult]].
|
||||
const value = promise.PromiseResult;
|
||||
// b. Let fulfillJob be NewPromiseReactionJob(fulfillReaction, value).
|
||||
const fulfillJob = NewPromiseReactionJob(fulfillReaction, value);
|
||||
// c. Perform HostEnqueuePromiseJob(fulfillJob.[[Job]], fulfillJob.[[Realm]]).
|
||||
HostEnqueuePromiseJob(fulfillJob.Job, fulfillJob.Realm);
|
||||
} else {
|
||||
// a. Assert: The value of promise.[[PromiseState]] is rejected.
|
||||
Assert(promise.PromiseState === 'rejected');
|
||||
// b. Let reason be promise.[[PromiseResult]].
|
||||
const reason = promise.PromiseResult;
|
||||
// c. If promise.[[PromiseIsHandled]] is false, perform HostPromiseRejectionTracker(promise, "handle").
|
||||
if (promise.PromiseIsHandled === Value.false) {
|
||||
HostPromiseRejectionTracker(promise, 'handle');
|
||||
}
|
||||
// d. Let rejectJob be NewPromiseReactionJob(rejectReaction, reason).
|
||||
const rejectJob = NewPromiseReactionJob(rejectReaction, reason);
|
||||
// e. Perform HostEnqueuePromiseJob(rejectJob.[[Job]], rejectJob.[[Realm]]).
|
||||
HostEnqueuePromiseJob(rejectJob.Job, rejectJob.Realm);
|
||||
}
|
||||
// 12. Set promise.[[PromiseIsHandled]] to true.
|
||||
promise.PromiseIsHandled = Value.true;
|
||||
// 13. If resultCapability is undefined, then
|
||||
if (resultCapability === Value.undefined) {
|
||||
// a. Return undefined.
|
||||
return Value.undefined;
|
||||
} else { // 14. Else,
|
||||
// a. Return resultCapability.[[Promise]].
|
||||
return resultCapability.Promise;
|
||||
}
|
||||
}
|
||||
@@ -1,579 +0,0 @@
|
||||
import { surroundingAgent } from '../engine.mjs';
|
||||
import { Type, Value } from '../value.mjs';
|
||||
import { Q, X } from '../completion.mjs';
|
||||
import { ValueSet } from '../helpers.mjs';
|
||||
import {
|
||||
Assert,
|
||||
MakeBasicObject,
|
||||
IsConstructor,
|
||||
IsCallable,
|
||||
Call,
|
||||
Construct,
|
||||
GetMethod,
|
||||
CreateArrayFromList,
|
||||
CreateListFromArrayLike,
|
||||
IsExtensible,
|
||||
IsPropertyKey,
|
||||
SameValue,
|
||||
ToBoolean,
|
||||
ToPropertyDescriptor,
|
||||
FromPropertyDescriptor,
|
||||
CompletePropertyDescriptor,
|
||||
IsCompatiblePropertyDescriptor,
|
||||
IsDataDescriptor,
|
||||
IsAccessorDescriptor,
|
||||
} from './all.mjs';
|
||||
|
||||
// #sec-proxy-object-internal-methods-and-internal-slots-getprototypeof
|
||||
function ProxyGetPrototypeOf() {
|
||||
const O = this;
|
||||
|
||||
const handler = O.ProxyHandler;
|
||||
if (handler === Value.null) {
|
||||
return surroundingAgent.Throw('TypeError', 'ProxyRevoked', 'getPrototypeOf');
|
||||
}
|
||||
Assert(Type(handler) === 'Object');
|
||||
const target = O.ProxyTarget;
|
||||
const trap = Q(GetMethod(handler, new Value('getPrototypeOf')));
|
||||
if (trap === Value.undefined) {
|
||||
return Q(target.GetPrototypeOf());
|
||||
}
|
||||
const handlerProto = Q(Call(trap, handler, [target]));
|
||||
if (Type(handlerProto) !== 'Object' && Type(handlerProto) !== 'Null') {
|
||||
return surroundingAgent.Throw('TypeError', 'ProxyGetPrototypeOfInvalid');
|
||||
}
|
||||
const extensibleTarget = Q(IsExtensible(target));
|
||||
if (extensibleTarget === Value.true) {
|
||||
return handlerProto;
|
||||
}
|
||||
const targetProto = Q(target.GetPrototypeOf());
|
||||
if (SameValue(handlerProto, targetProto) === Value.false) {
|
||||
return surroundingAgent.Throw('TypeError', 'ProxyGetPrototypeOfNonExtensible');
|
||||
}
|
||||
return handlerProto;
|
||||
}
|
||||
|
||||
// #sec-proxy-object-internal-methods-and-internal-slots-setprototypeof-v
|
||||
function ProxySetPrototypeOf(V) {
|
||||
const O = this;
|
||||
|
||||
Assert(Type(V) === 'Object' || Type(V) === 'Null');
|
||||
const handler = O.ProxyHandler;
|
||||
if (handler === Value.null) {
|
||||
return surroundingAgent.Throw('TypeError', 'ProxyRevoked', 'setPrototypeOf');
|
||||
}
|
||||
Assert(Type(handler) === 'Object');
|
||||
const target = O.ProxyTarget;
|
||||
const trap = Q(GetMethod(handler, new Value('setPrototypeOf')));
|
||||
if (trap === Value.undefined) {
|
||||
return Q(target.SetPrototypeOf(V));
|
||||
}
|
||||
const booleanTrapResult = ToBoolean(Q(Call(trap, handler, [target, V])));
|
||||
if (booleanTrapResult === Value.false) {
|
||||
return Value.false;
|
||||
}
|
||||
const extensibleTarget = Q(IsExtensible(target));
|
||||
if (extensibleTarget === Value.true) {
|
||||
return Value.true;
|
||||
}
|
||||
const targetProto = Q(target.GetPrototypeOf());
|
||||
if (SameValue(V, targetProto) === Value.false) {
|
||||
return surroundingAgent.Throw('TypeError', 'ProxySetPrototypeOfNonExtensible');
|
||||
}
|
||||
return Value.true;
|
||||
}
|
||||
|
||||
// #sec-proxy-object-internal-methods-and-internal-slots-isextensible
|
||||
function ProxyIsExtensible() {
|
||||
const O = this;
|
||||
|
||||
const handler = O.ProxyHandler;
|
||||
if (handler === Value.null) {
|
||||
return surroundingAgent.Throw('TypeError', 'ProxyRevoked', 'isExtensible');
|
||||
}
|
||||
Assert(Type(handler) === 'Object');
|
||||
const target = O.ProxyTarget;
|
||||
const trap = Q(GetMethod(handler, new Value('isExtensible')));
|
||||
if (trap === Value.undefined) {
|
||||
return Q(IsExtensible(target));
|
||||
}
|
||||
const booleanTrapResult = ToBoolean(Q(Call(trap, handler, [target])));
|
||||
const targetResult = Q(IsExtensible(target));
|
||||
if (SameValue(booleanTrapResult, targetResult) === Value.false) {
|
||||
return surroundingAgent.Throw('TypeError', 'ProxyIsExtensibleInconsistent', targetResult);
|
||||
}
|
||||
return booleanTrapResult;
|
||||
}
|
||||
|
||||
// #sec-proxy-object-internal-methods-and-internal-slots-preventextensions
|
||||
function ProxyPreventExtensions() {
|
||||
const O = this;
|
||||
|
||||
const handler = O.ProxyHandler;
|
||||
if (handler === Value.null) {
|
||||
return surroundingAgent.Throw('TypeError', 'ProxyRevoked', 'preventExtensions');
|
||||
}
|
||||
Assert(Type(handler) === 'Object');
|
||||
const target = O.ProxyTarget;
|
||||
const trap = Q(GetMethod(handler, new Value('preventExtensions')));
|
||||
if (trap === Value.undefined) {
|
||||
return Q(target.PreventExtensions());
|
||||
}
|
||||
const booleanTrapResult = ToBoolean(Q(Call(trap, handler, [target])));
|
||||
if (booleanTrapResult === Value.true) {
|
||||
const extensibleTarget = Q(IsExtensible(target));
|
||||
if (extensibleTarget === Value.true) {
|
||||
return surroundingAgent.Throw('TypeError', 'ProxyPreventExtensionsExtensible');
|
||||
}
|
||||
}
|
||||
return booleanTrapResult;
|
||||
}
|
||||
|
||||
// #sec-proxy-object-internal-methods-and-internal-slots-getownproperty-p
|
||||
function ProxyGetOwnProperty(P) {
|
||||
const O = this;
|
||||
|
||||
// 1. Assert: IsPropertyKey(P) is true.
|
||||
Assert(IsPropertyKey(P));
|
||||
// 2. Let handler be O.[[ProxyHandler]].
|
||||
const handler = O.ProxyHandler;
|
||||
// 3. If handler is null, throw a TypeError exception.
|
||||
if (handler === Value.null) {
|
||||
return surroundingAgent.Throw('TypeError', 'ProxyRevoked', 'getOwnPropertyDescriptor');
|
||||
}
|
||||
// 4. Assert: Type(Handler) is Object.
|
||||
Assert(Type(handler) === 'Object');
|
||||
// 5. Let target be O.[[ProxyTarget]].
|
||||
const target = O.ProxyTarget;
|
||||
// 6. Let trap be ? Getmethod(handler, "getOwnPropertyDescriptor").
|
||||
const trap = Q(GetMethod(handler, new Value('getOwnPropertyDescriptor')));
|
||||
// 7. If trap is undefined, then
|
||||
if (trap === Value.undefined) {
|
||||
// a. Return ? target.[[GetOwnProperty]](P).
|
||||
return Q(target.GetOwnProperty(P));
|
||||
}
|
||||
// 8. Let trapResultObj be ? Call(trap, handler, « target, P »).
|
||||
const trapResultObj = Q(Call(trap, handler, [target, P]));
|
||||
// 9. If Type(trapResultObj) is neither Object nor Undefined, throw a TypeError exception.
|
||||
if (Type(trapResultObj) !== 'Object' && Type(trapResultObj) !== 'Undefined') {
|
||||
return surroundingAgent.Throw('TypeError', 'ProxyGetOwnPropertyDescriptorInvalid', P);
|
||||
}
|
||||
// 10. Let targetDesc be ? target.[[GetOwnProperty]](P).
|
||||
const targetDesc = Q(target.GetOwnProperty(P));
|
||||
// 11. If trapResultObj is undefined, then
|
||||
if (trapResultObj === Value.undefined) {
|
||||
// a. If targetDesc is undefined, return undefined.
|
||||
if (targetDesc === Value.undefined) {
|
||||
return Value.undefined;
|
||||
}
|
||||
// b. If targetDesc.[[Configurable]] is false, throw a TypeError exception.
|
||||
if (targetDesc.Configurable === Value.false) {
|
||||
return surroundingAgent.Throw('TypeError', 'ProxyGetOwnPropertyDescriptorUndefined', P);
|
||||
}
|
||||
// c. Let extensibleTarget be ? IsExtensible(target).
|
||||
const extensibleTarget = Q(IsExtensible(target));
|
||||
// d. If extensibleTarget is false, throw a TypeError exception.
|
||||
if (extensibleTarget === Value.false) {
|
||||
return surroundingAgent.Throw('TypeError', 'ProxyGetOwnPropertyDescriptorNonExtensible', P);
|
||||
}
|
||||
// e. Return undefined.
|
||||
return Value.undefined;
|
||||
}
|
||||
// 12. Let extensibleTarget be ? IsExtensible(target).
|
||||
const extensibleTarget = Q(IsExtensible(target));
|
||||
// 13. Let resultDesc be ? ToPropertyDescriptor(trapResultObj).
|
||||
const resultDesc = Q(ToPropertyDescriptor(trapResultObj));
|
||||
// 14. Call CompletePropertyDescriptor(resultDesc).
|
||||
CompletePropertyDescriptor(resultDesc);
|
||||
// 15. Let valid be IsCompatiblePropertyDescriptor(extensibleTarget, resultDesc, targetDesc).
|
||||
const valid = IsCompatiblePropertyDescriptor(extensibleTarget, resultDesc, targetDesc);
|
||||
// 16. If valid is false, throw a TypeError exception.
|
||||
if (valid === Value.false) {
|
||||
return surroundingAgent.Throw('TypeError', 'ProxyGetOwnPropertyDescriptorIncompatible', P);
|
||||
}
|
||||
// 17. If resultDesc.[[Configurable]] is false, then
|
||||
if (resultDesc.Configurable === Value.false) {
|
||||
// a. If targetDesc is undefined or targetDesc.[[Configurable]] is true, then
|
||||
if (targetDesc === Value.undefined || targetDesc.Configurable === Value.true) {
|
||||
// i. Throw a TypeError exception.
|
||||
return surroundingAgent.Throw('TypeError', 'ProxyGetOwnPropertyDescriptorNonConfigurable', P);
|
||||
}
|
||||
// b. If resultDesc has a [[Writable]] field and resultDesc.[[Writable]] is false, then
|
||||
if ('Writable' in resultDesc && resultDesc.Writable === Value.false) {
|
||||
// i. If targetDesc.[[Writable]] is true, throw a TypeError exception.
|
||||
if (targetDesc.Writable === Value.true) {
|
||||
return surroundingAgent.Throw('TypeError', 'ProxyGetOwnPropertyDescriptorNonConfigurableWritable', P);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 18. Return resultDesc.
|
||||
return resultDesc;
|
||||
}
|
||||
|
||||
// #sec-proxy-object-internal-methods-and-internal-slots-defineownproperty-p-desc
|
||||
function ProxyDefineOwnProperty(P, Desc) {
|
||||
const O = this;
|
||||
|
||||
// 1. Assert: IsPropertyKey(P) is true.
|
||||
Assert(IsPropertyKey(P));
|
||||
// 2. Let handler be O.[[ProxyHandler]].
|
||||
const handler = O.ProxyHandler;
|
||||
// 3. If handler is null, throw a TypeError exception.
|
||||
if (handler === Value.null) {
|
||||
return surroundingAgent.Throw('TypeError', 'ProxyRevoked', 'defineProperty');
|
||||
}
|
||||
// 4. Assert: Type(handler) is Object.
|
||||
Assert(Type(handler) === 'Object');
|
||||
// 5. Let target be O.[[ProxyTarget]].
|
||||
const target = O.ProxyTarget;
|
||||
// 6. Let trap be ? GetMethod(handler, "defineProperty").
|
||||
const trap = Q(GetMethod(handler, new Value('defineProperty')));
|
||||
// 7. If trap is undefined, then
|
||||
if (trap === Value.undefined) {
|
||||
// a. Return ? target.[[DefineOwnProperty]](P, Desc).
|
||||
return Q(target.DefineOwnProperty(P, Desc));
|
||||
}
|
||||
// 8. Let descObj be FromPropertyDescriptor(Desc).
|
||||
const descObj = FromPropertyDescriptor(Desc);
|
||||
// 9. Let booleanTrapResult be ! ToBoolean(? Call(trap, handler, « target, P, descObj »)).
|
||||
const booleanTrapResult = ToBoolean(Q(Call(trap, handler, [target, P, descObj])));
|
||||
// 10. If booleanTrapResult is false, return false.
|
||||
if (booleanTrapResult === Value.false) {
|
||||
return Value.false;
|
||||
}
|
||||
// 11. Let targetDesc be ? target.[[GetOwnProperty]](P).
|
||||
const targetDesc = Q(target.GetOwnProperty(P));
|
||||
// 12. Let extensibleTarget be ? IsExtensible(target).
|
||||
const extensibleTarget = Q(IsExtensible(target));
|
||||
let settingConfigFalse;
|
||||
// 13. If Desc has a [[Configurable]] field and if Desc.[[Configurable]] is false, then
|
||||
if (Desc.Configurable !== undefined && Desc.Configurable === Value.false) {
|
||||
// a. Let settingConfigFalse be true.
|
||||
settingConfigFalse = true;
|
||||
} else {
|
||||
// Else, let settingConfigFalse be false.
|
||||
settingConfigFalse = false;
|
||||
}
|
||||
// 15. If targetDesc is undefined, then
|
||||
if (targetDesc === Value.undefined) {
|
||||
// a. If extensibleTarget is false, throw a TypeError exception.
|
||||
if (extensibleTarget === Value.false) {
|
||||
return surroundingAgent.Throw('TypeError', 'ProxyDefinePropertyNonExtensible', P);
|
||||
}
|
||||
// b. If settingConfigFalse is true, throw a TypeError exception.
|
||||
if (settingConfigFalse === true) {
|
||||
return surroundingAgent.Throw('TypeError', 'ProxyDefinePropertyNonConfigurable', P);
|
||||
}
|
||||
} else {
|
||||
// a. If IsCompatiblePropertyDescriptor(extensibleTarget, Desc, targetDesc) is false, throw a TypeError exception.
|
||||
if (IsCompatiblePropertyDescriptor(extensibleTarget, Desc, targetDesc) === Value.false) {
|
||||
return surroundingAgent.Throw('TypeError', 'ProxyDefinePropertyIncompatible', P);
|
||||
}
|
||||
// b. If settingConfigFalse is true and targetDesc.[[Configurable]] is true, throw a TypeError exception.
|
||||
if (settingConfigFalse === true && targetDesc.Configurable === Value.true) {
|
||||
return surroundingAgent.Throw('TypeError', 'ProxyDefinePropertyNonConfigurable', P);
|
||||
}
|
||||
// c. If IsDataDescriptor(targetDesc) is true, targetDesc.[[Configurable]] is false, and targetDesc.[[Writable]] is true, then
|
||||
if (IsDataDescriptor(targetDesc)
|
||||
&& targetDesc.Configurable === Value.false
|
||||
&& targetDesc.Writable === Value.true) {
|
||||
// i. If Desc has a [[Writable]] field and Desc.[[Writable]] is false, throw a TypeError exception.
|
||||
if ('Writable' in Desc && Desc.Writable === Value.false) {
|
||||
return surroundingAgent.Throw('TypeError', 'ProxyDefinePropertyNonConfigurableWritable', P);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Value.true;
|
||||
}
|
||||
|
||||
// #sec-proxy-object-internal-methods-and-internal-slots-hasproperty-p
|
||||
function ProxyHasProperty(P) {
|
||||
const O = this;
|
||||
|
||||
Assert(IsPropertyKey(P));
|
||||
const handler = O.ProxyHandler;
|
||||
if (handler === Value.null) {
|
||||
return surroundingAgent.Throw('TypeError', 'ProxyRevoked', 'has');
|
||||
}
|
||||
Assert(Type(handler) === 'Object');
|
||||
const target = O.ProxyTarget;
|
||||
const trap = Q(GetMethod(handler, new Value('has')));
|
||||
if (trap === Value.undefined) {
|
||||
return Q(target.HasProperty(P));
|
||||
}
|
||||
const booleanTrapResult = ToBoolean(Q(Call(trap, handler, [target, P])));
|
||||
if (booleanTrapResult === Value.false) {
|
||||
const targetDesc = Q(target.GetOwnProperty(P));
|
||||
if (targetDesc !== Value.undefined) {
|
||||
if (targetDesc.Configurable === Value.false) {
|
||||
return surroundingAgent.Throw('TypeError', 'ProxyHasNonConfigurable', P);
|
||||
}
|
||||
const extensibleTarget = Q(IsExtensible(target));
|
||||
if (extensibleTarget === Value.false) {
|
||||
return surroundingAgent.Throw('TypeError', 'ProxyHasNonExtensible', P);
|
||||
}
|
||||
}
|
||||
}
|
||||
return booleanTrapResult;
|
||||
}
|
||||
|
||||
// #sec-proxy-object-internal-methods-and-internal-slots-get-p-receiver
|
||||
function ProxyGet(P, Receiver) {
|
||||
const O = this;
|
||||
|
||||
Assert(IsPropertyKey(P));
|
||||
const handler = O.ProxyHandler;
|
||||
if (handler === Value.null) {
|
||||
return surroundingAgent.Throw('TypeError', 'ProxyRevoked', 'get');
|
||||
}
|
||||
Assert(Type(handler) === 'Object');
|
||||
const target = O.ProxyTarget;
|
||||
const trap = Q(GetMethod(handler, new Value('get')));
|
||||
if (trap === Value.undefined) {
|
||||
return Q(target.Get(P, Receiver));
|
||||
}
|
||||
const trapResult = Q(Call(trap, handler, [target, P, Receiver]));
|
||||
const targetDesc = Q(target.GetOwnProperty(P));
|
||||
if (targetDesc !== Value.undefined && targetDesc.Configurable === Value.false) {
|
||||
if (IsDataDescriptor(targetDesc) === true && targetDesc.Writable === Value.false) {
|
||||
if (SameValue(trapResult, targetDesc.Value) === Value.false) {
|
||||
return surroundingAgent.Throw('TypeError', 'ProxyGetNonConfigurableData', P);
|
||||
}
|
||||
}
|
||||
if (IsAccessorDescriptor(targetDesc) === true && targetDesc.Get === Value.undefined) {
|
||||
if (trapResult !== Value.undefined) {
|
||||
return surroundingAgent.Throw('TypeError', 'ProxyGetNonConfigurableAccessor', P);
|
||||
}
|
||||
}
|
||||
}
|
||||
return trapResult;
|
||||
}
|
||||
|
||||
// #sec-proxy-object-internal-methods-and-internal-slots-set-p-v-receiver
|
||||
function ProxySet(P, V, Receiver) {
|
||||
const O = this;
|
||||
|
||||
Assert(IsPropertyKey(P));
|
||||
const handler = O.ProxyHandler;
|
||||
if (handler === Value.null) {
|
||||
return surroundingAgent.Throw('TypeError', 'ProxyRevoked', 'set');
|
||||
}
|
||||
Assert(Type(handler) === 'Object');
|
||||
const target = O.ProxyTarget;
|
||||
const trap = Q(GetMethod(handler, new Value('set')));
|
||||
if (trap === Value.undefined) {
|
||||
return Q(target.Set(P, V, Receiver));
|
||||
}
|
||||
const booleanTrapResult = ToBoolean(Q(Call(trap, handler, [target, P, V, Receiver])));
|
||||
if (booleanTrapResult === Value.false) {
|
||||
return Value.false;
|
||||
}
|
||||
const targetDesc = Q(target.GetOwnProperty(P));
|
||||
if (targetDesc !== Value.undefined && targetDesc.Configurable === Value.false) {
|
||||
if (IsDataDescriptor(targetDesc) === true && targetDesc.Writable === Value.false) {
|
||||
if (SameValue(V, targetDesc.Value) === Value.false) {
|
||||
return surroundingAgent.Throw('TypeError', 'ProxySetFrozenData', P);
|
||||
}
|
||||
}
|
||||
if (IsAccessorDescriptor(targetDesc) === true) {
|
||||
if (targetDesc.Set === Value.undefined) {
|
||||
return surroundingAgent.Throw('TypeError', 'ProxySetFrozenAccessor', P);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Value.true;
|
||||
}
|
||||
|
||||
// #sec-proxy-object-internal-methods-and-internal-slots-delete-p
|
||||
function ProxyDelete(P) {
|
||||
const O = this;
|
||||
|
||||
// 1. Assert: IsPropertyKey(P) is true.
|
||||
Assert(IsPropertyKey(P));
|
||||
// 2. Let handler be O.[[ProxyHandler]].
|
||||
const handler = O.ProxyHandler;
|
||||
// 3. If handler is null, throw a TypeError exception.
|
||||
if (handler === Value.null) {
|
||||
return surroundingAgent.Throw('TypeError', 'ProxyRevoked', 'deleteProperty');
|
||||
}
|
||||
// 4. Assert: Type(handler) is Object.
|
||||
Assert(Type(handler) === 'Object');
|
||||
// 5. Let target be O.[[ProxyTarget]].
|
||||
const target = O.ProxyTarget;
|
||||
// 6. Let trap be ? GetMethod(handler, "deleteProperty").
|
||||
const trap = Q(GetMethod(handler, new Value('deleteProperty')));
|
||||
// 7. If trap is undefined, then
|
||||
if (trap === Value.undefined) {
|
||||
// a. Return ? target.[[Delete]](P).
|
||||
return Q(target.Delete(P));
|
||||
}
|
||||
// 8. Let booleanTrapResult be ! ToBoolean(? Call(trap, handler, « target, P »)).
|
||||
const booleanTrapResult = ToBoolean(Q(Call(trap, handler, [target, P])));
|
||||
// 9. If booleanTrapResult is false, return false.
|
||||
if (booleanTrapResult === Value.false) {
|
||||
return Value.false;
|
||||
}
|
||||
// 10. Let targetDesc be ? target.[[GetOwnProperty]](P).
|
||||
const targetDesc = Q(target.GetOwnProperty(P));
|
||||
// 11. If targetDesc is undefined, return true.
|
||||
if (targetDesc === Value.undefined) {
|
||||
return Value.true;
|
||||
}
|
||||
// 12. If targetDesc.[[Configurable]] is false, throw a TypeError exception.
|
||||
if (targetDesc.Configurable === Value.false) {
|
||||
return surroundingAgent.Throw('TypeError', 'ProxyDeletePropertyNonConfigurable', P);
|
||||
}
|
||||
// 13. Let extensibleTarget be ? IsExtensible(target).
|
||||
const extensibleTarget = Q(IsExtensible(target));
|
||||
// 14. If extensibleTarget is false, throw a TypeError exception.
|
||||
if (extensibleTarget === Value.false) {
|
||||
return surroundingAgent.Throw('TypeError', 'ProxyDeletePropertyNonExtensible', P);
|
||||
}
|
||||
// 15. Return true.
|
||||
return Value.true;
|
||||
}
|
||||
|
||||
// #sec-proxy-object-internal-methods-and-internal-slots-ownpropertykeys
|
||||
function ProxyOwnPropertyKeys() {
|
||||
const O = this;
|
||||
|
||||
const handler = O.ProxyHandler;
|
||||
if (handler === Value.null) {
|
||||
return surroundingAgent.Throw('TypeError', 'ProxyRevoked', 'ownKeys');
|
||||
}
|
||||
Assert(Type(handler) === 'Object');
|
||||
const target = O.ProxyTarget;
|
||||
const trap = Q(GetMethod(handler, new Value('ownKeys')));
|
||||
if (trap === Value.undefined) {
|
||||
return Q(target.OwnPropertyKeys());
|
||||
}
|
||||
const trapResultArray = Q(Call(trap, handler, [target]));
|
||||
const trapResult = Q(CreateListFromArrayLike(trapResultArray, ['String', 'Symbol']));
|
||||
if (new ValueSet(trapResult).size !== trapResult.length) {
|
||||
return surroundingAgent.Throw('TypeError', 'ProxyOwnKeysDuplicateEntries');
|
||||
}
|
||||
const extensibleTarget = Q(IsExtensible(target));
|
||||
const targetKeys = Q(target.OwnPropertyKeys());
|
||||
// Assert: targetKeys is a List containing only String and Symbol values.
|
||||
// Assert: targetKeys contains no duplicate entries.
|
||||
const targetConfigurableKeys = [];
|
||||
const targetNonconfigurableKeys = [];
|
||||
for (const key of targetKeys) {
|
||||
const desc = Q(target.GetOwnProperty(key));
|
||||
if (desc !== Value.undefined && desc.Configurable === Value.false) {
|
||||
targetNonconfigurableKeys.push(key);
|
||||
} else {
|
||||
targetConfigurableKeys.push(key);
|
||||
}
|
||||
}
|
||||
if (extensibleTarget === Value.true && targetNonconfigurableKeys.length === 0) {
|
||||
return trapResult;
|
||||
}
|
||||
const uncheckedResultKeys = new ValueSet(trapResult);
|
||||
for (const key of targetNonconfigurableKeys) {
|
||||
if (!uncheckedResultKeys.has(key)) {
|
||||
return surroundingAgent.Throw('TypeError', 'ProxyOwnKeysMissing', 'non-configurable key');
|
||||
}
|
||||
uncheckedResultKeys.delete(key);
|
||||
}
|
||||
if (extensibleTarget === Value.true) {
|
||||
return trapResult;
|
||||
}
|
||||
for (const key of targetConfigurableKeys) {
|
||||
if (!uncheckedResultKeys.has(key)) {
|
||||
return surroundingAgent.Throw('TypeError', 'ProxyOwnKeysMissing', 'configurable key');
|
||||
}
|
||||
uncheckedResultKeys.delete(key);
|
||||
}
|
||||
if (uncheckedResultKeys.size > 0) {
|
||||
return surroundingAgent.Throw('TypeError', 'ProxyOwnKeysNonExtensible');
|
||||
}
|
||||
return trapResult;
|
||||
}
|
||||
|
||||
// #sec-proxy-object-internal-methods-and-internal-slots-call-thisargument-argumentslist
|
||||
function ProxyCall(thisArgument, argumentsList) {
|
||||
const O = this;
|
||||
|
||||
const handler = O.ProxyHandler;
|
||||
if (handler === Value.null) {
|
||||
return surroundingAgent.Throw('TypeError', 'ProxyRevoked', 'apply');
|
||||
}
|
||||
Assert(Type(handler) === 'Object');
|
||||
const target = O.ProxyTarget;
|
||||
const trap = Q(GetMethod(handler, new Value('apply')));
|
||||
if (trap === Value.undefined) {
|
||||
return Q(Call(target, thisArgument, argumentsList));
|
||||
}
|
||||
const argArray = X(CreateArrayFromList(argumentsList));
|
||||
return Q(Call(trap, handler, [target, thisArgument, argArray]));
|
||||
}
|
||||
|
||||
// #sec-proxy-object-internal-methods-and-internal-slots-construct-argumentslist-newtarget
|
||||
function ProxyConstruct(argumentsList, newTarget) {
|
||||
const O = this;
|
||||
|
||||
const handler = O.ProxyHandler;
|
||||
if (handler === Value.null) {
|
||||
return surroundingAgent.Throw('TypeError', 'ProxyRevoked', 'construct');
|
||||
}
|
||||
Assert(Type(handler) === 'Object');
|
||||
const target = O.ProxyTarget;
|
||||
Assert(IsConstructor(target) === Value.true);
|
||||
const trap = Q(GetMethod(handler, new Value('construct')));
|
||||
if (trap === Value.undefined) {
|
||||
return Q(Construct(target, argumentsList, newTarget));
|
||||
}
|
||||
const argArray = X(CreateArrayFromList(argumentsList));
|
||||
const newObj = Q(Call(trap, handler, [target, argArray, newTarget]));
|
||||
if (Type(newObj) !== 'Object') {
|
||||
return surroundingAgent.Throw('TypeError', 'NotAnObject', newObj);
|
||||
}
|
||||
return newObj;
|
||||
}
|
||||
|
||||
export function isProxyExoticObject(O) {
|
||||
return 'ProxyHandler' in O;
|
||||
}
|
||||
|
||||
// #sec-proxycreate
|
||||
export function ProxyCreate(target, handler) {
|
||||
// 1. If Type(target) is not Object, throw a TypeError exception.
|
||||
if (Type(target) !== 'Object') {
|
||||
return surroundingAgent.Throw('TypeError', 'CannotCreateProxyWith', 'non-object', 'target');
|
||||
}
|
||||
// 2. If Type(handler) is not Object, throw a TypeError exception.
|
||||
if (Type(handler) !== 'Object') {
|
||||
return surroundingAgent.Throw('TypeError', 'CannotCreateProxyWith', 'non-object', 'handler');
|
||||
}
|
||||
// 3. Let P be ! MakeBasicObject(« [[ProxyHandler]], [[ProxyTarget]] »).
|
||||
const P = X(MakeBasicObject(['ProxyHandler', 'ProxyTarget']));
|
||||
// 4. Set P's essential internal methods, except for [[Call]] and [[Construct]], to the definitions specified in 9.5.
|
||||
P.GetPrototypeOf = ProxyGetPrototypeOf;
|
||||
P.SetPrototypeOf = ProxySetPrototypeOf;
|
||||
P.IsExtensible = ProxyIsExtensible;
|
||||
P.PreventExtensions = ProxyPreventExtensions;
|
||||
P.GetOwnProperty = ProxyGetOwnProperty;
|
||||
P.DefineOwnProperty = ProxyDefineOwnProperty;
|
||||
P.HasProperty = ProxyHasProperty;
|
||||
P.Get = ProxyGet;
|
||||
P.Set = ProxySet;
|
||||
P.Delete = ProxyDelete;
|
||||
P.OwnPropertyKeys = ProxyOwnPropertyKeys;
|
||||
// 5. If IsCallable(target) is true, then
|
||||
if (IsCallable(target) === Value.true) {
|
||||
// a. Set P.[[Call]] as specified in #sec-proxy-object-internal-methods-and-internal-slots-call-thisargument-argumentslist.
|
||||
P.Call = ProxyCall;
|
||||
// b. If IsConstructor(target) is true, then
|
||||
if (IsConstructor(target) === Value.true) {
|
||||
// i. Set P.[[Construct]] as specified in #sec-proxy-object-internal-methods-and-internal-slots-construct-argumentslist-newtarget.
|
||||
P.Construct = ProxyConstruct;
|
||||
}
|
||||
}
|
||||
// 6. Set P.[[ProxyTarget]] to target.
|
||||
P.ProxyTarget = target;
|
||||
// 7. Set P.[[ProxyHandler]] to handler.
|
||||
P.ProxyHandler = handler;
|
||||
// 8. Return P.
|
||||
return P;
|
||||
}
|
||||
@@ -1,366 +0,0 @@
|
||||
import {
|
||||
Descriptor,
|
||||
Value,
|
||||
} from '../value.mjs';
|
||||
import { NewGlobalEnvironment } from '../environment.mjs';
|
||||
import { Q, X } from '../completion.mjs';
|
||||
import { BootstrapObjectPrototype } from '../intrinsics/ObjectPrototype.mjs';
|
||||
import { BootstrapObject } from '../intrinsics/Object.mjs';
|
||||
import { BootstrapArrayPrototype } from '../intrinsics/ArrayPrototype.mjs';
|
||||
import { BootstrapArray } from '../intrinsics/Array.mjs';
|
||||
import { BootstrapBigInt } from '../intrinsics/BigInt.mjs';
|
||||
import { BootstrapBigIntPrototype } from '../intrinsics/BigIntPrototype.mjs';
|
||||
import { BootstrapBooleanPrototype } from '../intrinsics/BooleanPrototype.mjs';
|
||||
import { BootstrapBoolean } from '../intrinsics/Boolean.mjs';
|
||||
import { BootstrapNumberPrototype } from '../intrinsics/NumberPrototype.mjs';
|
||||
import { BootstrapNumber } from '../intrinsics/Number.mjs';
|
||||
import { BootstrapFunctionPrototype } from '../intrinsics/FunctionPrototype.mjs';
|
||||
import { BootstrapFunction } from '../intrinsics/Function.mjs';
|
||||
import { BootstrapSymbolPrototype } from '../intrinsics/SymbolPrototype.mjs';
|
||||
import { BootstrapSymbol } from '../intrinsics/Symbol.mjs';
|
||||
import { BootstrapMath } from '../intrinsics/Math.mjs';
|
||||
import { BootstrapDatePrototype } from '../intrinsics/DatePrototype.mjs';
|
||||
import { BootstrapDate } from '../intrinsics/Date.mjs';
|
||||
import { BootstrapRegExpPrototype } from '../intrinsics/RegExpPrototype.mjs';
|
||||
import { BootstrapRegExp } from '../intrinsics/RegExp.mjs';
|
||||
import { BootstrapPromisePrototype } from '../intrinsics/PromisePrototype.mjs';
|
||||
import { BootstrapPromise } from '../intrinsics/Promise.mjs';
|
||||
import { BootstrapProxy } from '../intrinsics/Proxy.mjs';
|
||||
import { BootstrapReflect } from '../intrinsics/Reflect.mjs';
|
||||
import { BootstrapStringPrototype } from '../intrinsics/StringPrototype.mjs';
|
||||
import { BootstrapString } from '../intrinsics/String.mjs';
|
||||
import { BootstrapErrorPrototype } from '../intrinsics/ErrorPrototype.mjs';
|
||||
import { BootstrapError } from '../intrinsics/Error.mjs';
|
||||
import { BootstrapNativeError } from '../intrinsics/NativeError.mjs';
|
||||
import { BootstrapIteratorPrototype } from '../intrinsics/IteratorPrototype.mjs';
|
||||
import { BootstrapAsyncIteratorPrototype } from '../intrinsics/AsyncIteratorPrototype.mjs';
|
||||
import { BootstrapArrayIteratorPrototype } from '../intrinsics/ArrayIteratorPrototype.mjs';
|
||||
import { BootstrapMapIteratorPrototype } from '../intrinsics/MapIteratorPrototype.mjs';
|
||||
import { BootstrapSetIteratorPrototype } from '../intrinsics/SetIteratorPrototype.mjs';
|
||||
import { BootstrapStringIteratorPrototype } from '../intrinsics/StringIteratorPrototype.mjs';
|
||||
import { BootstrapRegExpStringIteratorPrototype } from '../intrinsics/RegExpStringIteratorPrototype.mjs';
|
||||
import { BootstrapForInIteratorPrototype } from '../intrinsics/ForInIteratorPrototype.mjs';
|
||||
import { BootstrapMapPrototype } from '../intrinsics/MapPrototype.mjs';
|
||||
import { BootstrapMap } from '../intrinsics/Map.mjs';
|
||||
import { BootstrapSetPrototype } from '../intrinsics/SetPrototype.mjs';
|
||||
import { BootstrapSet } from '../intrinsics/Set.mjs';
|
||||
import { BootstrapGenerator } from '../intrinsics/Generator.mjs';
|
||||
import { BootstrapGeneratorFunction } from '../intrinsics/GeneratorFunction.mjs';
|
||||
import { BootstrapGeneratorPrototype } from '../intrinsics/GeneratorPrototype.mjs';
|
||||
import { BootstrapAsyncFunctionPrototype } from '../intrinsics/AsyncFunctionPrototype.mjs';
|
||||
import { BootstrapAsyncFunction } from '../intrinsics/AsyncFunction.mjs';
|
||||
import { BootstrapAsyncGenerator } from '../intrinsics/AsyncGenerator.mjs';
|
||||
import { BootstrapAsyncGeneratorFunction } from '../intrinsics/AsyncGeneratorFunction.mjs';
|
||||
import { BootstrapAsyncGeneratorPrototype } from '../intrinsics/AsyncGeneratorPrototype.mjs';
|
||||
import { BootstrapAsyncFromSyncIteratorPrototype } from '../intrinsics/AsyncFromSyncIteratorPrototype.mjs';
|
||||
import { BootstrapArrayBuffer } from '../intrinsics/ArrayBuffer.mjs';
|
||||
import { BootstrapArrayBufferPrototype } from '../intrinsics/ArrayBufferPrototype.mjs';
|
||||
import { BootstrapJSON } from '../intrinsics/JSON.mjs';
|
||||
import { BootstrapEval } from '../intrinsics/eval.mjs';
|
||||
import { BootstrapIsFinite } from '../intrinsics/isFinite.mjs';
|
||||
import { BootstrapIsNaN } from '../intrinsics/isNaN.mjs';
|
||||
import { BootstrapParseFloat } from '../intrinsics/parseFloat.mjs';
|
||||
import { BootstrapParseInt } from '../intrinsics/parseInt.mjs';
|
||||
import { BootstrapURIHandling } from '../intrinsics/URIHandling.mjs';
|
||||
import { BootstrapThrowTypeError } from '../intrinsics/ThrowTypeError.mjs';
|
||||
import { BootstrapTypedArray } from '../intrinsics/TypedArray.mjs';
|
||||
import { BootstrapTypedArrayPrototype } from '../intrinsics/TypedArrayPrototype.mjs';
|
||||
import { BootstrapTypedArrayConstructors } from '../intrinsics/TypedArrayConstructors.mjs';
|
||||
import { BootstrapTypedArrayPrototypes } from '../intrinsics/TypedArrayPrototypes.mjs';
|
||||
import { BootstrapDataView } from '../intrinsics/DataView.mjs';
|
||||
import { BootstrapDataViewPrototype } from '../intrinsics/DataViewPrototype.mjs';
|
||||
import { BootstrapWeakMapPrototype } from '../intrinsics/WeakMapPrototype.mjs';
|
||||
import { BootstrapWeakMap } from '../intrinsics/WeakMap.mjs';
|
||||
import { BootstrapWeakSetPrototype } from '../intrinsics/WeakSetPrototype.mjs';
|
||||
import { BootstrapWeakSet } from '../intrinsics/WeakSet.mjs';
|
||||
import { BootstrapAggregateError } from '../intrinsics/AggregateError.mjs';
|
||||
import { BootstrapAggregateErrorPrototype } from '../intrinsics/AggregateErrorPrototype.mjs';
|
||||
import { BootstrapWeakRefPrototype } from '../intrinsics/WeakRefPrototype.mjs';
|
||||
import { BootstrapWeakRef } from '../intrinsics/WeakRef.mjs';
|
||||
import { BootstrapFinalizationRegistryPrototype } from '../intrinsics/FinalizationRegistryPrototype.mjs';
|
||||
import { BootstrapFinalizationRegistry } from '../intrinsics/FinalizationRegistry.mjs';
|
||||
import {
|
||||
Assert,
|
||||
DefinePropertyOrThrow,
|
||||
OrdinaryObjectCreate,
|
||||
} from './all.mjs';
|
||||
|
||||
// 8.2 #sec-code-realms
|
||||
export class Realm {
|
||||
constructor() {
|
||||
this.Intrinsics = undefined;
|
||||
this.GlobalObject = undefined;
|
||||
this.GlobalEnv = undefined;
|
||||
this.TemplateMap = undefined;
|
||||
this.HostDefined = undefined;
|
||||
|
||||
this.randomState = undefined;
|
||||
}
|
||||
|
||||
mark(m) {
|
||||
m(this.GlobalObject);
|
||||
m(this.GlobalEnv);
|
||||
for (const v of Object.values(this.Intrinsics)) {
|
||||
m(v);
|
||||
}
|
||||
for (const v of Object.values(this.TemplateMap)) {
|
||||
m(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 8.2.1 #sec-createrealm
|
||||
export function CreateRealm() {
|
||||
const realmRec = new Realm();
|
||||
CreateIntrinsics(realmRec);
|
||||
realmRec.GlobalObject = Value.undefined;
|
||||
realmRec.GlobalEnv = Value.undefined;
|
||||
realmRec.TemplateMap = [];
|
||||
return realmRec;
|
||||
}
|
||||
|
||||
function AddRestrictedFunctionProperties(F, realm) {
|
||||
Assert(realm.Intrinsics['%ThrowTypeError%']);
|
||||
const thrower = realm.Intrinsics['%ThrowTypeError%'];
|
||||
X(DefinePropertyOrThrow(F, new Value('caller'), Descriptor({
|
||||
Get: thrower,
|
||||
Set: thrower,
|
||||
Enumerable: Value.false,
|
||||
Configurable: Value.true,
|
||||
})));
|
||||
X(DefinePropertyOrThrow(F, new Value('arguments'), Descriptor({
|
||||
Get: thrower,
|
||||
Set: thrower,
|
||||
Enumerable: Value.false,
|
||||
Configurable: Value.true,
|
||||
})));
|
||||
}
|
||||
|
||||
// #sec-createintrinsics
|
||||
export function CreateIntrinsics(realmRec) {
|
||||
const intrinsics = Object.create(null);
|
||||
realmRec.Intrinsics = intrinsics;
|
||||
|
||||
intrinsics['%Object.prototype%'] = OrdinaryObjectCreate(Value.null);
|
||||
|
||||
BootstrapFunctionPrototype(realmRec);
|
||||
BootstrapObjectPrototype(realmRec);
|
||||
BootstrapThrowTypeError(realmRec);
|
||||
|
||||
BootstrapEval(realmRec);
|
||||
BootstrapIsFinite(realmRec);
|
||||
BootstrapIsNaN(realmRec);
|
||||
BootstrapParseFloat(realmRec);
|
||||
BootstrapParseInt(realmRec);
|
||||
BootstrapURIHandling(realmRec);
|
||||
|
||||
BootstrapObject(realmRec);
|
||||
|
||||
BootstrapErrorPrototype(realmRec);
|
||||
BootstrapError(realmRec);
|
||||
BootstrapNativeError(realmRec);
|
||||
BootstrapAggregateErrorPrototype(realmRec);
|
||||
BootstrapAggregateError(realmRec);
|
||||
|
||||
BootstrapFunction(realmRec);
|
||||
|
||||
BootstrapIteratorPrototype(realmRec);
|
||||
BootstrapAsyncIteratorPrototype(realmRec);
|
||||
BootstrapArrayIteratorPrototype(realmRec);
|
||||
BootstrapMapIteratorPrototype(realmRec);
|
||||
BootstrapSetIteratorPrototype(realmRec);
|
||||
BootstrapStringIteratorPrototype(realmRec);
|
||||
BootstrapRegExpStringIteratorPrototype(realmRec);
|
||||
BootstrapForInIteratorPrototype(realmRec);
|
||||
|
||||
BootstrapStringPrototype(realmRec);
|
||||
BootstrapString(realmRec);
|
||||
|
||||
BootstrapArrayPrototype(realmRec);
|
||||
BootstrapArray(realmRec);
|
||||
|
||||
BootstrapBooleanPrototype(realmRec);
|
||||
BootstrapBoolean(realmRec);
|
||||
|
||||
BootstrapNumberPrototype(realmRec);
|
||||
BootstrapNumber(realmRec);
|
||||
|
||||
BootstrapBigIntPrototype(realmRec);
|
||||
BootstrapBigInt(realmRec);
|
||||
|
||||
BootstrapSymbolPrototype(realmRec);
|
||||
BootstrapSymbol(realmRec);
|
||||
|
||||
BootstrapPromisePrototype(realmRec);
|
||||
BootstrapPromise(realmRec);
|
||||
|
||||
BootstrapProxy(realmRec);
|
||||
|
||||
BootstrapReflect(realmRec);
|
||||
|
||||
BootstrapMath(realmRec);
|
||||
|
||||
BootstrapDatePrototype(realmRec);
|
||||
BootstrapDate(realmRec);
|
||||
|
||||
BootstrapRegExpPrototype(realmRec);
|
||||
BootstrapRegExp(realmRec);
|
||||
|
||||
BootstrapSetPrototype(realmRec);
|
||||
BootstrapSet(realmRec);
|
||||
|
||||
BootstrapMapPrototype(realmRec);
|
||||
BootstrapMap(realmRec);
|
||||
|
||||
BootstrapGeneratorPrototype(realmRec);
|
||||
BootstrapGenerator(realmRec);
|
||||
BootstrapGeneratorFunction(realmRec);
|
||||
|
||||
BootstrapAsyncFunctionPrototype(realmRec);
|
||||
BootstrapAsyncFunction(realmRec);
|
||||
|
||||
BootstrapAsyncGeneratorPrototype(realmRec);
|
||||
BootstrapAsyncGenerator(realmRec);
|
||||
BootstrapAsyncGeneratorFunction(realmRec);
|
||||
|
||||
BootstrapAsyncFromSyncIteratorPrototype(realmRec);
|
||||
|
||||
BootstrapArrayBufferPrototype(realmRec);
|
||||
BootstrapArrayBuffer(realmRec);
|
||||
|
||||
BootstrapTypedArrayPrototype(realmRec);
|
||||
BootstrapTypedArray(realmRec);
|
||||
BootstrapTypedArrayPrototypes(realmRec);
|
||||
BootstrapTypedArrayConstructors(realmRec);
|
||||
|
||||
BootstrapDataViewPrototype(realmRec);
|
||||
BootstrapDataView(realmRec);
|
||||
|
||||
BootstrapJSON(realmRec);
|
||||
|
||||
BootstrapWeakMapPrototype(realmRec);
|
||||
BootstrapWeakMap(realmRec);
|
||||
BootstrapWeakSetPrototype(realmRec);
|
||||
BootstrapWeakSet(realmRec);
|
||||
|
||||
BootstrapWeakRefPrototype(realmRec);
|
||||
BootstrapWeakRef(realmRec);
|
||||
|
||||
BootstrapFinalizationRegistryPrototype(realmRec);
|
||||
BootstrapFinalizationRegistry(realmRec);
|
||||
|
||||
AddRestrictedFunctionProperties(intrinsics['%Function.prototype%'], realmRec);
|
||||
|
||||
return intrinsics;
|
||||
}
|
||||
|
||||
// 8.2.3 #sec-setrealmglobalobject
|
||||
export function SetRealmGlobalObject(realmRec, globalObj, thisValue) {
|
||||
const intrinsics = realmRec.Intrinsics;
|
||||
if (globalObj === Value.undefined) {
|
||||
globalObj = OrdinaryObjectCreate(intrinsics['%Object.prototype%']);
|
||||
}
|
||||
if (thisValue === Value.undefined) {
|
||||
thisValue = globalObj;
|
||||
}
|
||||
realmRec.GlobalObject = globalObj;
|
||||
const newGlobalEnv = NewGlobalEnvironment(globalObj, thisValue);
|
||||
realmRec.GlobalEnv = newGlobalEnv;
|
||||
return realmRec;
|
||||
}
|
||||
|
||||
// 8.2.4 #sec-setdefaultglobalbindings
|
||||
export function SetDefaultGlobalBindings(realmRec) {
|
||||
const global = realmRec.GlobalObject;
|
||||
|
||||
// Value Properties of the Global Object
|
||||
[
|
||||
['Infinity', new Value(Infinity)],
|
||||
['NaN', new Value(NaN)],
|
||||
['undefined', Value.undefined],
|
||||
].forEach(([name, value]) => {
|
||||
Q(DefinePropertyOrThrow(global, new Value(name), Descriptor({
|
||||
Value: value,
|
||||
Writable: Value.false,
|
||||
Enumerable: Value.false,
|
||||
Configurable: Value.false,
|
||||
})));
|
||||
});
|
||||
|
||||
Q(DefinePropertyOrThrow(global, new Value('globalThis'), Descriptor({
|
||||
Value: realmRec.GlobalEnv.GlobalThisValue,
|
||||
Writable: Value.true,
|
||||
Enumerable: Value.false,
|
||||
Configurable: Value.true,
|
||||
})));
|
||||
|
||||
[
|
||||
// Function Properties of the Global Object
|
||||
'eval',
|
||||
'isFinite',
|
||||
'isNaN',
|
||||
'parseFloat',
|
||||
'parseInt',
|
||||
'decodeURI',
|
||||
'decodeURIComponent',
|
||||
'encodeURI',
|
||||
'encodeURIComponent',
|
||||
|
||||
// Constructor Properties of the Global Object
|
||||
'AggregateError',
|
||||
'Array',
|
||||
'ArrayBuffer',
|
||||
'Boolean',
|
||||
'BigInt',
|
||||
'BigInt64Array',
|
||||
'BigUint64Array',
|
||||
'DataView',
|
||||
'Date',
|
||||
'Error',
|
||||
'EvalError',
|
||||
'FinalizationRegistry',
|
||||
'Float32Array',
|
||||
'Float64Array',
|
||||
'Function',
|
||||
'Int8Array',
|
||||
'Int16Array',
|
||||
'Int32Array',
|
||||
'Map',
|
||||
'Number',
|
||||
'Object',
|
||||
'Promise',
|
||||
'Proxy',
|
||||
'RangeError',
|
||||
'ReferenceError',
|
||||
'RegExp',
|
||||
'Set',
|
||||
// 'SharedArrayBuffer',
|
||||
'String',
|
||||
'Symbol',
|
||||
'SyntaxError',
|
||||
'TypeError',
|
||||
'Uint8Array',
|
||||
'Uint8ClampedArray',
|
||||
'Uint16Array',
|
||||
'Uint32Array',
|
||||
'URIError',
|
||||
'WeakMap',
|
||||
'WeakRef',
|
||||
'WeakSet',
|
||||
|
||||
// Other Properties of the Global Object
|
||||
// 'Atomics',
|
||||
'JSON',
|
||||
'Math',
|
||||
'Reflect',
|
||||
].forEach((name) => {
|
||||
Q(DefinePropertyOrThrow(global, new Value(name), Descriptor({
|
||||
Value: realmRec.Intrinsics[`%${name}%`],
|
||||
Writable: Value.true,
|
||||
Enumerable: Value.false,
|
||||
Configurable: Value.true,
|
||||
})));
|
||||
});
|
||||
|
||||
return global;
|
||||
}
|
||||
@@ -1,142 +0,0 @@
|
||||
import { surroundingAgent } from '../engine.mjs';
|
||||
import { PrimitiveValue, Type, Value } from '../value.mjs';
|
||||
import {
|
||||
NormalCompletion,
|
||||
Q,
|
||||
ReturnIfAbrupt,
|
||||
X,
|
||||
} from '../completion.mjs';
|
||||
import {
|
||||
Assert,
|
||||
GetGlobalObject,
|
||||
ToObject,
|
||||
Set,
|
||||
} from './all.mjs';
|
||||
|
||||
|
||||
// 6.2.4.1 #sec-getbase
|
||||
export function GetBase(V) {
|
||||
Assert(Type(V) === 'Reference');
|
||||
return V.BaseValue;
|
||||
}
|
||||
|
||||
// 6.2.4.2 #sec-getreferencedname
|
||||
export function GetReferencedName(V) {
|
||||
Assert(Type(V) === 'Reference');
|
||||
return V.ReferencedName;
|
||||
}
|
||||
|
||||
// 6.2.4.3 #sec-isstrictreference
|
||||
export function IsStrictReference(V) {
|
||||
Assert(Type(V) === 'Reference');
|
||||
return V.StrictReference;
|
||||
}
|
||||
|
||||
// 6.2.4.4 #sec-hasprimitivebase
|
||||
export function HasPrimitiveBase(V) {
|
||||
Assert(Type(V) === 'Reference');
|
||||
if (V.BaseValue instanceof PrimitiveValue) {
|
||||
return Value.true;
|
||||
}
|
||||
return Value.false;
|
||||
}
|
||||
|
||||
// 6.2.4.5 #sec-ispropertyreference
|
||||
export function IsPropertyReference(V) {
|
||||
Assert(Type(V) === 'Reference');
|
||||
if (Type(V.BaseValue) === 'Object' || HasPrimitiveBase(V) === Value.true) {
|
||||
return Value.true;
|
||||
}
|
||||
return Value.false;
|
||||
}
|
||||
|
||||
// 6.2.4.6 #sec-isunresolvablereference
|
||||
export function IsUnresolvableReference(V) {
|
||||
Assert(Type(V) === 'Reference');
|
||||
if (V.BaseValue === Value.undefined) {
|
||||
return Value.true;
|
||||
}
|
||||
return Value.false;
|
||||
}
|
||||
|
||||
// 6.2.4.7 #sec-issuperreference
|
||||
export function IsSuperReference(V) {
|
||||
// 1. Assert: Type(V) is Reference.
|
||||
Assert(Type(V) === 'Reference');
|
||||
// 2. If V has a thisValue component, return true; otherwise return false.
|
||||
return 'thisValue' in V ? Value.true : Value.false;
|
||||
}
|
||||
|
||||
// 6.2.4.8 #sec-getvalue
|
||||
export function GetValue(V) {
|
||||
ReturnIfAbrupt(V);
|
||||
if (Type(V) !== 'Reference') {
|
||||
return V;
|
||||
}
|
||||
let base = GetBase(V);
|
||||
if (IsUnresolvableReference(V) === Value.true) {
|
||||
return surroundingAgent.Throw('ReferenceError', 'NotDefined', GetReferencedName(V));
|
||||
}
|
||||
if (IsPropertyReference(V) === Value.true) {
|
||||
if (HasPrimitiveBase(V) === Value.true) {
|
||||
Assert(base !== Value.undefined && base !== Value.null);
|
||||
base = X(ToObject(base));
|
||||
}
|
||||
return Q(base.Get(GetReferencedName(V), GetThisValue(V)));
|
||||
} else {
|
||||
return Q(base.GetBindingValue(GetReferencedName(V), IsStrictReference(V)));
|
||||
}
|
||||
}
|
||||
|
||||
// 6.2.4.9 #sec-putvalue
|
||||
export function PutValue(V, W) {
|
||||
ReturnIfAbrupt(V);
|
||||
ReturnIfAbrupt(W);
|
||||
if (Type(V) !== 'Reference') {
|
||||
return surroundingAgent.Throw('ReferenceError', 'NotDefined', V);
|
||||
}
|
||||
let base = GetBase(V);
|
||||
if (IsUnresolvableReference(V) === Value.true) {
|
||||
if (IsStrictReference(V) === Value.true) {
|
||||
return surroundingAgent.Throw('ReferenceError', 'NotDefined', GetReferencedName(V));
|
||||
}
|
||||
const globalObj = GetGlobalObject();
|
||||
return Q(Set(globalObj, GetReferencedName(V), W, Value.false));
|
||||
} else if (IsPropertyReference(V) === Value.true) {
|
||||
if (HasPrimitiveBase(V) === Value.true) {
|
||||
Assert(Type(base) !== 'Undefined' && Type(base) !== 'Null');
|
||||
base = X(ToObject(base));
|
||||
}
|
||||
const succeeded = Q(base.Set(GetReferencedName(V), W, GetThisValue(V)));
|
||||
if (succeeded === Value.false && IsStrictReference(V) === Value.true) {
|
||||
return surroundingAgent.Throw('TypeError', 'CannotSetProperty', GetReferencedName(V), base);
|
||||
}
|
||||
return NormalCompletion(Value.undefined);
|
||||
} else {
|
||||
return Q(base.SetMutableBinding(GetReferencedName(V), W, IsStrictReference(V)));
|
||||
}
|
||||
}
|
||||
|
||||
// 6.2.4.10 #sec-getthisvalue
|
||||
export function GetThisValue(V) {
|
||||
// 1. Assert: IsPropertyReference(V) is true.
|
||||
Assert(IsPropertyReference(V) === Value.true);
|
||||
// 2. If IsSuperReference(V) is true, then
|
||||
if (IsSuperReference(V) === Value.true) {
|
||||
// a. Return the value of the thisValue component of the reference V.
|
||||
return V.thisValue;
|
||||
}
|
||||
// 3. Return GetBase(V).
|
||||
return GetBase(V);
|
||||
}
|
||||
|
||||
// 6.2.4.11 #sec-initializereferencedbinding
|
||||
export function InitializeReferencedBinding(V, W) {
|
||||
ReturnIfAbrupt(V);
|
||||
ReturnIfAbrupt(W);
|
||||
Assert(Type(V) === 'Reference');
|
||||
Assert(IsUnresolvableReference(V) === Value.false);
|
||||
const base = GetBase(V);
|
||||
Assert(Type(base) === 'EnvironmentRecord');
|
||||
return base.InitializeBinding(GetReferencedName(V), W);
|
||||
}
|
||||
@@ -1,254 +0,0 @@
|
||||
import { surroundingAgent } from '../engine.mjs';
|
||||
import { Descriptor, Value, Type } from '../value.mjs';
|
||||
import { Q, X } from '../completion.mjs';
|
||||
import { Evaluate_Pattern } from '../runtime-semantics/all.mjs';
|
||||
import { ParsePattern } from '../parse.mjs';
|
||||
import { isLineTerminator } from '../parser/Lexer.mjs';
|
||||
import {
|
||||
ArrayCreate,
|
||||
Assert,
|
||||
CreateArrayFromList,
|
||||
CreateDataPropertyOrThrow,
|
||||
DefinePropertyOrThrow,
|
||||
OrdinaryCreateFromConstructor,
|
||||
OrdinaryObjectCreate,
|
||||
Set,
|
||||
ToString,
|
||||
} from './all.mjs';
|
||||
|
||||
// #sec-regexpalloc
|
||||
export function RegExpAlloc(newTarget) {
|
||||
const obj = Q(OrdinaryCreateFromConstructor(newTarget, '%RegExp.prototype%', ['RegExpMatcher', 'OriginalSource', 'OriginalFlags']));
|
||||
X(DefinePropertyOrThrow(obj, new Value('lastIndex'), Descriptor({
|
||||
Writable: Value.true,
|
||||
Enumerable: Value.false,
|
||||
Configurable: Value.false,
|
||||
})));
|
||||
return obj;
|
||||
}
|
||||
|
||||
// #sec-regexpinitialize
|
||||
export function RegExpInitialize(obj, pattern, flags) {
|
||||
let P;
|
||||
// 1. If pattern is undefined, let P be the empty String.
|
||||
if (pattern === Value.undefined) {
|
||||
P = new Value('');
|
||||
} else { // 2. Else, let P be ? ToString(pattern).
|
||||
P = Q(ToString(pattern));
|
||||
}
|
||||
let F;
|
||||
// 3. If flags is undefined, let F be the empty String.
|
||||
if (flags === Value.undefined) {
|
||||
F = new Value('');
|
||||
} else { // 4. Else, let F be ? ToString(flags).
|
||||
F = Q(ToString(flags));
|
||||
}
|
||||
const f = F.stringValue();
|
||||
// 5. If F contains any code unit other than "g", "i", "m", "s", "u", or "y" or if it contains the same code unit more than once, throw a SyntaxError exception.
|
||||
if (/^[gimsuy]*$/.test(f) === false || (new globalThis.Set(f).size !== f.length)) {
|
||||
return surroundingAgent.Throw('SyntaxError', 'InvalidRegExpFlags', f);
|
||||
}
|
||||
// 6. If F contains "u", let u be true; else let u be false.
|
||||
const u = f.includes('u');
|
||||
// 7. If u is true, then
|
||||
// a. Let patternText be ! UTF16DecodeString(P).
|
||||
// b. Let patternCharacters be a List whose elements are the code points of patternText.
|
||||
// 8. Else,
|
||||
// a. Let patternText be the result of interpreting each of P's 16-bit elements as a Unicode BMP code point. UTF-16 decoding is not applied to the elements.
|
||||
// b. Let patternCharacters be a List whose elements are the code unit elements of P.
|
||||
// 9. Let parseResult be ParsePattern(patternText, u).
|
||||
const patternText = P.stringValue();
|
||||
const parseResult = ParsePattern(patternText, u);
|
||||
// 10. If parseResult is a non-empty List of SyntaxError objects, throw a SyntaxError exception.
|
||||
if (Array.isArray(parseResult)) {
|
||||
return surroundingAgent.Throw(parseResult[0]);
|
||||
}
|
||||
obj.parsedPattern = parseResult;
|
||||
// 11. Assert: parseResult is a Parse Node for Pattern.
|
||||
Assert(parseResult.type === 'Pattern');
|
||||
// 12. Set obj.[[OriginalSource]] to P.
|
||||
obj.OriginalSource = P;
|
||||
// 13. Set obj.[[OriginalFlags]] to F.
|
||||
obj.OriginalFlags = F;
|
||||
// 14. Set obj.[[RegExpMatcher]] to the Abstract Closure that evaluates parseResult by
|
||||
// applying the semantics provided in 21.2.2 using patternCharacters as the pattern's
|
||||
// List of SourceCharacter values and F as the flag parameters.
|
||||
obj.RegExpMatcher = Evaluate_Pattern(parseResult, F.stringValue());
|
||||
// 15. Perform ? Set(obj, "lastIndex", 0, true).
|
||||
Q(Set(obj, new Value('lastIndex'), new Value(0), Value.true));
|
||||
// 16. Return obj.
|
||||
return obj;
|
||||
}
|
||||
|
||||
// 21.2.3.2.3 #sec-regexpcreate
|
||||
export function RegExpCreate(P, F) {
|
||||
const obj = Q(RegExpAlloc(surroundingAgent.intrinsic('%RegExp%')));
|
||||
return Q(RegExpInitialize(obj, P, F));
|
||||
}
|
||||
|
||||
// #sec-escaperegexppattern
|
||||
export function EscapeRegExpPattern(P, _F) {
|
||||
const source = P.stringValue();
|
||||
if (source === '') {
|
||||
return new Value('(:?)');
|
||||
}
|
||||
let index = 0;
|
||||
let escaped = '';
|
||||
let inClass = false;
|
||||
while (index < source.length) {
|
||||
const c = source[index];
|
||||
switch (c) {
|
||||
case '\\':
|
||||
index += 1;
|
||||
if (isLineTerminator(source[index])) {
|
||||
// nothing
|
||||
} else {
|
||||
escaped += '\\';
|
||||
}
|
||||
break;
|
||||
case '/':
|
||||
index += 1;
|
||||
if (inClass) {
|
||||
escaped += '/';
|
||||
} else {
|
||||
escaped += '\\/';
|
||||
}
|
||||
break;
|
||||
case '[':
|
||||
inClass = true;
|
||||
index += 1;
|
||||
escaped += '[';
|
||||
break;
|
||||
case ']':
|
||||
inClass = false;
|
||||
index += 1;
|
||||
escaped += ']';
|
||||
break;
|
||||
case '\n':
|
||||
index += 1;
|
||||
escaped += '\\n';
|
||||
break;
|
||||
case '\r':
|
||||
index += 1;
|
||||
escaped += '\\r';
|
||||
break;
|
||||
case '\u2028':
|
||||
index += 1;
|
||||
escaped += '\\u2028';
|
||||
break;
|
||||
case '\u2029':
|
||||
index += 1;
|
||||
escaped += '\\u2029';
|
||||
break;
|
||||
default:
|
||||
index += 1;
|
||||
escaped += c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return new Value(escaped);
|
||||
}
|
||||
|
||||
// https://tc39.es/proposal-regexp-match-indices/#sec-getstringindex
|
||||
export function GetStringIndex(S, Input, e) {
|
||||
// 1. Assert: Type(S) is String.
|
||||
Assert(Type(S) === 'String');
|
||||
// 2. Assert: Input is a List of the code points of S interpreted as a UTF-16 encoded string.
|
||||
Assert(Array.isArray(Input));
|
||||
// 3. Assert: e is an integer value ≥ 0 and < the number of elements in Input.
|
||||
Assert(e >= 0);
|
||||
// 4. Let eUTF be the smallest index into S that corresponds to the character at element e of Input.
|
||||
// If e is greater than or equal to the number of elements in Input, then eUTF is the number of code units in S.
|
||||
let eUTF = 0;
|
||||
if (e >= Input.length) {
|
||||
eUTF = S.stringValue().length;
|
||||
} else {
|
||||
for (let i = 0; i < e; i += 1) {
|
||||
eUTF += Input[i].length;
|
||||
}
|
||||
}
|
||||
// 5. Return eUTF.
|
||||
return eUTF;
|
||||
}
|
||||
|
||||
// https://tc39.es/proposal-regexp-match-indices/#sec-getmatchstring
|
||||
export function GetMatchString(S, match) {
|
||||
// 1. Assert: Type(S) is String.
|
||||
Assert(Type(S) === 'String');
|
||||
// 2. Assert: match is a Match Record.
|
||||
Assert('StartIndex' in match && 'EndIndex' in match);
|
||||
// 3. Assert: match.[[StartIndex]] is an integer value ≥ 0 and < the length of S.
|
||||
Assert(match.StartIndex >= 0 && match.StartIndex < S.stringValue().length);
|
||||
// 4. Assert: match.[[EndIndex]] is an integer value ≥ match.[[StartIndex]] and ≤ the length of S.
|
||||
Assert(match.EndIndex >= match.StartIndex && match.EndIndex <= S.stringValue().length);
|
||||
// 5. Return the portion of S between offset match.[[StartIndex]] inclusive and offset match.[[EndIndex]] exclusive.
|
||||
return new Value(S.stringValue().slice(match.StartIndex, match.EndIndex));
|
||||
}
|
||||
|
||||
// https://tc39.es/proposal-regexp-match-indices/#sec-getmatchindicesarray
|
||||
export function GetMatchIndicesArray(S, match) {
|
||||
// 1. Assert: Type(S) is String.
|
||||
Assert(Type(S) === 'String');
|
||||
// 2. Assert: match is a Match Record.
|
||||
Assert('StartIndex' in match && 'EndIndex' in match);
|
||||
// 3. Assert: match.[[StartIndex]] is an integer value ≥ 0 and < the length of S.
|
||||
Assert(match.StartIndex >= 0 && match.StartIndex < S.stringValue().length);
|
||||
// 4. Assert: match.[[EndIndex]] is an integer value ≥ match.[[StartIndex]] and ≤ the length of S.
|
||||
Assert(match.EndIndex >= match.StartIndex && match.EndIndex <= S.stringValue().length);
|
||||
// 1. Return CreateArrayFromList(« match.[[StartIndex]], match.[[EndIndex]] »).
|
||||
return CreateArrayFromList([
|
||||
new Value(match.StartIndex),
|
||||
new Value(match.EndIndex),
|
||||
]);
|
||||
}
|
||||
|
||||
// https://tc39.es/proposal-regexp-match-indices/#sec-makeindicesarray
|
||||
export function MakeIndicesArray(S, indices, groupNames) {
|
||||
// 1. Assert: Type(S) is String.
|
||||
Assert(Type(S) === 'String');
|
||||
// 2. Assert: indices is a List.
|
||||
Assert(Array.isArray(indices));
|
||||
// 3. Assert: groupNames is a List or is undefined.
|
||||
Assert(Array.isArray(indices) || groupNames === Value.undefined);
|
||||
// 4. Let n be the number of elements in indices.
|
||||
const n = indices.length;
|
||||
// 5. Assert: n < 2**32-1.
|
||||
Assert(n < (2 ** 32) - 1);
|
||||
// 6. Set A to ! ArrayCreate(n).
|
||||
// 7. Assert: The value of A's "length" property is n.
|
||||
const A = X(ArrayCreate(new Value(n)));
|
||||
// 8. If groupNames is not undefined, then
|
||||
let groups;
|
||||
if (groupNames !== Value.undefined) {
|
||||
// a. Let groups be ! ObjectCreate(null).
|
||||
groups = X(OrdinaryObjectCreate(Value.null));
|
||||
} else { // 9. Else,
|
||||
// a. Let groups be undefined.
|
||||
groups = Value.undefined;
|
||||
}
|
||||
// 10. Perform ! CreateDataProperty(A, "groups", groups).
|
||||
X(CreateDataPropertyOrThrow(A, new Value('groups'), groups));
|
||||
// 11. For each integer i such that i ≥ 0 and i < n, do
|
||||
for (let i = 0; i < n; i += 1) {
|
||||
// a. Let matchIndices be indices[i].
|
||||
const matchIndices = indices[i];
|
||||
// b. If matchIndices is not undefined, then
|
||||
let matchIndicesArray;
|
||||
if (matchIndices !== Value.undefined) {
|
||||
// i. Let matchIndicesArray be ! GetMatchIndicesArray(S, matchIndices).
|
||||
matchIndicesArray = X(GetMatchIndicesArray(S, matchIndices));
|
||||
} else { // c. Else,
|
||||
// i. Let matchIndicesArray be undefined.
|
||||
matchIndicesArray = Value.undefined;
|
||||
}
|
||||
// d. Perform ! CreateDataProperty(A, ! ToString(i), matchIndicesArray).
|
||||
X(CreateDataPropertyOrThrow(A, X(ToString(new Value(i))), matchIndicesArray));
|
||||
// e. If groupNames is not undefined and groupNames[i] is not undefined, then
|
||||
if (groupNames !== Value.undefined && groupNames[i] !== Value.undefined) {
|
||||
// i. Perform ! CreateDataProperty(groups, groupNames[i], matchIndicesArray).
|
||||
X(CreateDataPropertyOrThrow(groups, groupNames[i], matchIndicesArray));
|
||||
}
|
||||
}
|
||||
// 12. Return A.
|
||||
return A;
|
||||
}
|
||||
@@ -1,210 +0,0 @@
|
||||
import { surroundingAgent } from '../engine.mjs';
|
||||
import {
|
||||
DataBlock,
|
||||
Descriptor,
|
||||
Type,
|
||||
Value,
|
||||
} from '../value.mjs';
|
||||
import { NormalCompletion, Q, X } from '../completion.mjs';
|
||||
import {
|
||||
Assert,
|
||||
CreateDataProperty,
|
||||
Get,
|
||||
HasProperty,
|
||||
IsCallable,
|
||||
OrdinaryObjectCreate,
|
||||
ToBoolean,
|
||||
} from './all.mjs';
|
||||
|
||||
|
||||
// 6.2.5.1 IsAccessorDescriptor
|
||||
export function IsAccessorDescriptor(Desc) {
|
||||
if (Type(Desc) === 'Undefined') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Desc.Get === undefined && Desc.Set === undefined) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// 6.2.5.2 IsDataDescriptor
|
||||
export function IsDataDescriptor(Desc) {
|
||||
if (Type(Desc) === 'Undefined') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Desc.Value === undefined && Desc.Writable === undefined) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// 6.2.5.3 IsGenericDescriptor
|
||||
export function IsGenericDescriptor(Desc) {
|
||||
if (Type(Desc) === 'Undefined') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!IsAccessorDescriptor(Desc) && !IsDataDescriptor(Desc)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// 6.2.5.4 #sec-frompropertydescriptor
|
||||
export function FromPropertyDescriptor(Desc) {
|
||||
if (Type(Desc) === 'Undefined') {
|
||||
return Value.undefined;
|
||||
}
|
||||
const obj = OrdinaryObjectCreate(surroundingAgent.intrinsic('%Object.prototype%'));
|
||||
if (Desc.Value !== undefined) {
|
||||
X(CreateDataProperty(obj, new Value('value'), Desc.Value));
|
||||
}
|
||||
if (Desc.Writable !== undefined) {
|
||||
X(CreateDataProperty(obj, new Value('writable'), Desc.Writable));
|
||||
}
|
||||
if (Desc.Get !== undefined) {
|
||||
X(CreateDataProperty(obj, new Value('get'), Desc.Get));
|
||||
}
|
||||
if (Desc.Set !== undefined) {
|
||||
X(CreateDataProperty(obj, new Value('set'), Desc.Set));
|
||||
}
|
||||
if (Desc.Enumerable !== undefined) {
|
||||
X(CreateDataProperty(obj, new Value('enumerable'), Desc.Enumerable));
|
||||
}
|
||||
if (Desc.Configurable !== undefined) {
|
||||
X(CreateDataProperty(obj, new Value('configurable'), Desc.Configurable));
|
||||
}
|
||||
// Assert: All of the above CreateDataProperty operations return true.
|
||||
return obj;
|
||||
}
|
||||
|
||||
// 6.2.5.5 #sec-topropertydescriptor
|
||||
export function ToPropertyDescriptor(Obj) {
|
||||
if (Type(Obj) !== 'Object') {
|
||||
return surroundingAgent.Throw('TypeError', 'NotAnObject', Obj);
|
||||
}
|
||||
|
||||
const desc = Descriptor({});
|
||||
const hasEnumerable = Q(HasProperty(Obj, new Value('enumerable')));
|
||||
if (hasEnumerable === Value.true) {
|
||||
const enumerable = ToBoolean(Q(Get(Obj, new Value('enumerable'))));
|
||||
desc.Enumerable = enumerable;
|
||||
}
|
||||
const hasConfigurable = Q(HasProperty(Obj, new Value('configurable')));
|
||||
if (hasConfigurable === Value.true) {
|
||||
const conf = ToBoolean(Q(Get(Obj, new Value('configurable'))));
|
||||
desc.Configurable = conf;
|
||||
}
|
||||
const hasValue = Q(HasProperty(Obj, new Value('value')));
|
||||
if (hasValue === Value.true) {
|
||||
const value = Q(Get(Obj, new Value('value')));
|
||||
desc.Value = value;
|
||||
}
|
||||
const hasWritable = Q(HasProperty(Obj, new Value('writable')));
|
||||
if (hasWritable === Value.true) {
|
||||
const writable = ToBoolean(Q(Get(Obj, new Value('writable'))));
|
||||
desc.Writable = writable;
|
||||
}
|
||||
const hasGet = Q(HasProperty(Obj, new Value('get')));
|
||||
if (hasGet === Value.true) {
|
||||
const getter = Q(Get(Obj, new Value('get')));
|
||||
if (IsCallable(getter) === Value.false && Type(getter) !== 'Undefined') {
|
||||
return surroundingAgent.Throw('TypeError', 'NotAFunction', getter);
|
||||
}
|
||||
desc.Get = getter;
|
||||
}
|
||||
const hasSet = Q(HasProperty(Obj, new Value('set')));
|
||||
if (hasSet === Value.true) {
|
||||
const setter = Q(Get(Obj, new Value('set')));
|
||||
if (IsCallable(setter) === Value.false && Type(setter) !== 'Undefined') {
|
||||
return surroundingAgent.Throw('TypeError', 'NotAFunction', setter);
|
||||
}
|
||||
desc.Set = setter;
|
||||
}
|
||||
if (desc.Get !== undefined || desc.Set !== undefined) {
|
||||
if (desc.Value !== undefined || desc.Writable !== undefined) {
|
||||
return surroundingAgent.Throw('TypeError', 'InvalidPropertyDescriptor');
|
||||
}
|
||||
}
|
||||
return desc;
|
||||
}
|
||||
|
||||
// 6.2.5.6 #sec-completepropertydescriptor
|
||||
export function CompletePropertyDescriptor(Desc) {
|
||||
Assert(Type(Desc) === 'Descriptor');
|
||||
const like = Descriptor({
|
||||
Value: Value.undefined,
|
||||
Writable: false,
|
||||
Get: Value.undefined,
|
||||
Set: Value.undefined,
|
||||
Enumerable: false,
|
||||
Configurable: false,
|
||||
});
|
||||
if (IsGenericDescriptor(Desc) || IsDataDescriptor(Desc)) {
|
||||
if (Desc.Value === undefined) {
|
||||
Desc.Value = like.Value;
|
||||
}
|
||||
if (Desc.Writable === undefined) {
|
||||
Desc.Writable = like.Writable;
|
||||
}
|
||||
} else {
|
||||
if (Desc.Get === undefined) {
|
||||
Desc.Get = like.Get;
|
||||
}
|
||||
if (Desc.Set === undefined) {
|
||||
Desc.Set = like.Set;
|
||||
}
|
||||
}
|
||||
if (Desc.Enumerable === undefined) {
|
||||
Desc.Enumerable = like.Enumerable;
|
||||
}
|
||||
if (Desc.Configurable === undefined) {
|
||||
Desc.Configurable = like.Configurable;
|
||||
}
|
||||
return Desc;
|
||||
}
|
||||
|
||||
// 6.2.7.1 #sec-createbytedatablock
|
||||
export function CreateByteDataBlock(size) {
|
||||
size = size.numberValue();
|
||||
Assert(size >= 0);
|
||||
let db;
|
||||
try {
|
||||
db = new DataBlock(size);
|
||||
} catch (err) {
|
||||
return surroundingAgent.Throw('RangeError', 'CannotAllocateDataBlock');
|
||||
}
|
||||
return db;
|
||||
}
|
||||
|
||||
// 6.2.7.3 #sec-copydatablockbytes
|
||||
export function CopyDataBlockBytes(toBlock, toIndex, fromBlock, fromIndex, count) {
|
||||
Assert(fromBlock !== toBlock);
|
||||
Assert(Type(fromBlock) === 'Data Block' || Type(fromBlock) === 'Shared Data Block');
|
||||
Assert(Type(toBlock) === 'Data Block' || Type(toBlock) === 'Shared Data Block');
|
||||
Assert(Number.isSafeInteger(fromIndex) && fromIndex >= 0);
|
||||
Assert(Number.isSafeInteger(toIndex) && toIndex >= 0);
|
||||
Assert(Number.isSafeInteger(count) && count >= 0);
|
||||
const fromSize = fromBlock.byteLength;
|
||||
Assert(fromIndex + count <= fromSize);
|
||||
const toSize = toBlock.byteLength;
|
||||
Assert(toIndex + count <= toSize);
|
||||
while (count > 0) {
|
||||
if (Type(fromBlock) === 'Shared Data Block') {
|
||||
Assert(false);
|
||||
} else {
|
||||
Assert(Type(toBlock) !== 'Shared Data Block');
|
||||
toBlock[toIndex] = fromBlock[fromIndex];
|
||||
}
|
||||
toIndex += 1;
|
||||
fromIndex += 1;
|
||||
count -= 1;
|
||||
}
|
||||
return NormalCompletion(undefined);
|
||||
}
|
||||
@@ -1,145 +0,0 @@
|
||||
import {
|
||||
Descriptor,
|
||||
Type,
|
||||
Value,
|
||||
} from '../value.mjs';
|
||||
import { X } from '../completion.mjs';
|
||||
import {
|
||||
Assert,
|
||||
CanonicalNumericIndexString,
|
||||
DefinePropertyOrThrow,
|
||||
IsInteger,
|
||||
IsPropertyKey,
|
||||
MakeBasicObject,
|
||||
OrdinaryGetOwnProperty,
|
||||
OrdinaryDefineOwnProperty,
|
||||
IsCompatiblePropertyDescriptor,
|
||||
ToInteger,
|
||||
isArrayIndex,
|
||||
} from './all.mjs';
|
||||
|
||||
function StringExoticGetOwnProperty(P) {
|
||||
const S = this;
|
||||
Assert(IsPropertyKey(P));
|
||||
const desc = OrdinaryGetOwnProperty(S, P);
|
||||
if (Type(desc) !== 'Undefined') {
|
||||
return desc;
|
||||
}
|
||||
return X(StringGetOwnProperty(S, P));
|
||||
}
|
||||
|
||||
function StringExoticDefineOwnProperty(P, Desc) {
|
||||
const S = this;
|
||||
Assert(IsPropertyKey(P));
|
||||
const stringDesc = X(StringGetOwnProperty(S, P));
|
||||
if (Type(stringDesc) !== 'Undefined') {
|
||||
const extensible = S.Extensible;
|
||||
return X(IsCompatiblePropertyDescriptor(extensible, Desc, stringDesc));
|
||||
}
|
||||
return X(OrdinaryDefineOwnProperty(S, P, Desc));
|
||||
}
|
||||
|
||||
function StringExoticOwnPropertyKeys() {
|
||||
const O = this;
|
||||
const keys = [];
|
||||
const str = O.StringData;
|
||||
Assert(Type(str) === 'String');
|
||||
const len = str.stringValue().length;
|
||||
|
||||
for (let i = 0; i < len; i += 1) {
|
||||
keys.push(new Value(`${i}`));
|
||||
}
|
||||
|
||||
// For each own property key P of O such that P is an array index and
|
||||
// ToInteger(P) ≥ len, in ascending numeric index order, do
|
||||
// Add P as the last element of keys.
|
||||
for (const P of O.properties.keys()) {
|
||||
// This is written with two nested ifs to work around https://github.com/devsnek/engine262/issues/24
|
||||
if (isArrayIndex(P)) {
|
||||
if (X(ToInteger(P)).numberValue() >= len) {
|
||||
keys.push(P);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For each own property key P of O such that Type(P) is String and
|
||||
// P is not an array index, in ascending chronological order of property creation, do
|
||||
// Add P as the last element of keys.
|
||||
for (const P of O.properties.keys()) {
|
||||
if (Type(P) === 'String' && isArrayIndex(P) === false) {
|
||||
keys.push(P);
|
||||
}
|
||||
}
|
||||
|
||||
// For each own property key P of O such that Type(P) is Symbol,
|
||||
// in ascending chronological order of property creation, do
|
||||
// Add P as the last element of keys.
|
||||
for (const P of O.properties.keys()) {
|
||||
if (Type(P) === 'Symbol') {
|
||||
keys.push(P);
|
||||
}
|
||||
}
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
// 9.4.3.4 #sec-stringcreate
|
||||
export function StringCreate(value, prototype) {
|
||||
// 1. Assert: Type(value) is String.
|
||||
Assert(Type(value) === 'String');
|
||||
// 2. Let S be ! MakeBasicObject(« [[Prototype]], [[Extensible]], [[StringData]] »).
|
||||
const S = X(MakeBasicObject(['Prototype', 'Extensible', 'StringData']));
|
||||
// 3. Set S.[[Prototype]] to prototype.
|
||||
S.Prototype = prototype;
|
||||
// 4. Set S.[[StringData]] to value.
|
||||
S.StringData = value;
|
||||
// 5. Set S.[[GetOwnProperty]] as specified in 9.4.3.1.
|
||||
S.GetOwnProperty = StringExoticGetOwnProperty;
|
||||
// 6. Set S.[[DefineOwnProperty]] as specified in 9.4.3.2.
|
||||
S.DefineOwnProperty = StringExoticDefineOwnProperty;
|
||||
// 7. Set S.[[OwnPropertyKeys]] as specified in 9.4.3.3.
|
||||
S.OwnPropertyKeys = StringExoticOwnPropertyKeys;
|
||||
// 8. Let length be the number of code unit elements in value.
|
||||
const length = new Value(value.stringValue().length);
|
||||
// 9. Perform ! DefinePropertyOrThrow(S, "length", PropertyDescriptor { [[Value]]: length, [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false }).
|
||||
X(DefinePropertyOrThrow(S, new Value('length'), Descriptor({
|
||||
Value: length,
|
||||
Writable: Value.false,
|
||||
Enumerable: Value.false,
|
||||
Configurable: Value.false,
|
||||
})));
|
||||
// 10. Return S.
|
||||
return S;
|
||||
}
|
||||
|
||||
// 9.4.3.5 #sec-stringgetownproperty
|
||||
export function StringGetOwnProperty(S, P) {
|
||||
Assert(Type(S) === 'Object' && 'StringData' in S);
|
||||
Assert(IsPropertyKey(P));
|
||||
if (Type(P) !== 'String') {
|
||||
return Value.undefined;
|
||||
}
|
||||
const index = X(CanonicalNumericIndexString(P));
|
||||
if (Type(index) === 'Undefined') {
|
||||
return Value.undefined;
|
||||
}
|
||||
if (IsInteger(index) === Value.false) {
|
||||
return Value.undefined;
|
||||
}
|
||||
if (Object.is(index.numberValue(), -0)) {
|
||||
return Value.undefined;
|
||||
}
|
||||
const str = S.StringData;
|
||||
Assert(Type(str) === 'String');
|
||||
const len = str.stringValue().length;
|
||||
if (index.numberValue() < 0 || len <= index.numberValue()) {
|
||||
return Value.undefined;
|
||||
}
|
||||
const resultStr = str.stringValue()[index.numberValue()];
|
||||
return Descriptor({
|
||||
Value: new Value(resultStr),
|
||||
Writable: Value.false,
|
||||
Enumerable: Value.true,
|
||||
Configurable: Value.false,
|
||||
});
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
import { Type, Value } from '../value.mjs';
|
||||
import { Assert } from './all.mjs';
|
||||
|
||||
// 19.4.3.3.1 #sec-symboldescriptivestring
|
||||
export function SymbolDescriptiveString(sym) {
|
||||
Assert(Type(sym) === 'Symbol');
|
||||
let desc = sym.Description;
|
||||
if (Type(desc) === 'Undefined') {
|
||||
desc = new Value('');
|
||||
}
|
||||
return new Value(`Symbol(${desc.stringValue()})`);
|
||||
}
|
||||
@@ -1,412 +0,0 @@
|
||||
import {
|
||||
BigIntValue,
|
||||
Type,
|
||||
TypeNumeric,
|
||||
Value,
|
||||
wellKnownSymbols,
|
||||
} from '../value.mjs';
|
||||
import { surroundingAgent } from '../engine.mjs';
|
||||
import { Q, X } from '../completion.mjs';
|
||||
import { OutOfRange } from '../helpers.mjs';
|
||||
import {
|
||||
Assert,
|
||||
Get,
|
||||
ToBoolean,
|
||||
ToNumber,
|
||||
ToNumeric,
|
||||
ToPrimitive,
|
||||
StringToBigInt,
|
||||
isProxyExoticObject,
|
||||
isArrayExoticObject,
|
||||
isIntegerIndexedExoticObject,
|
||||
} from './all.mjs';
|
||||
|
||||
// This file covers abstract operations defined in
|
||||
// 7.2 #sec-testing-and-comparison-operations
|
||||
|
||||
// 7.2.1 #sec-requireobjectcoercible
|
||||
export function RequireObjectCoercible(argument) {
|
||||
const type = Type(argument);
|
||||
switch (type) {
|
||||
case 'Undefined':
|
||||
return surroundingAgent.Throw('TypeError', 'CannotConvertToObject', 'undefined');
|
||||
case 'Null':
|
||||
return surroundingAgent.Throw('TypeError', 'CannotConvertToObject', 'null');
|
||||
case 'Boolean':
|
||||
case 'Number':
|
||||
case 'String':
|
||||
case 'Symbol':
|
||||
case 'BigInt':
|
||||
case 'Object':
|
||||
return argument;
|
||||
default:
|
||||
throw new OutOfRange('RequireObjectCoercible', { type, argument });
|
||||
}
|
||||
}
|
||||
|
||||
// 7.2.2 #sec-isarray
|
||||
export function IsArray(argument) {
|
||||
if (Type(argument) !== 'Object') {
|
||||
return Value.false;
|
||||
}
|
||||
if (isArrayExoticObject(argument)) {
|
||||
return Value.true;
|
||||
}
|
||||
if (isProxyExoticObject(argument)) {
|
||||
if (argument.ProxyHandler === Value.null) {
|
||||
return surroundingAgent.Throw('TypeError', 'ProxyRevoked', 'IsArray');
|
||||
}
|
||||
const target = argument.ProxyTarget;
|
||||
return IsArray(target);
|
||||
}
|
||||
return Value.false;
|
||||
}
|
||||
|
||||
// 7.2.3 #sec-iscallable
|
||||
export function IsCallable(argument) {
|
||||
if (Type(argument) !== 'Object') {
|
||||
return Value.false;
|
||||
}
|
||||
if ('Call' in argument) {
|
||||
return Value.true;
|
||||
}
|
||||
return Value.false;
|
||||
}
|
||||
|
||||
// 7.2.4 #sec-isconstructor
|
||||
export function IsConstructor(argument) {
|
||||
if (Type(argument) !== 'Object') {
|
||||
return Value.false;
|
||||
}
|
||||
if ('Construct' in argument) {
|
||||
return Value.true;
|
||||
}
|
||||
return Value.false;
|
||||
}
|
||||
|
||||
// 7.2.5 #sec-isextensible-o
|
||||
export function IsExtensible(O) {
|
||||
Assert(Type(O) === 'Object');
|
||||
return O.IsExtensible();
|
||||
}
|
||||
|
||||
// 7.2.6 #sec-isinteger
|
||||
export function IsInteger(argument) {
|
||||
if (Type(argument) !== 'Number') {
|
||||
return Value.false;
|
||||
}
|
||||
if (argument.isNaN() || argument.isInfinity()) {
|
||||
return Value.false;
|
||||
}
|
||||
if (Math.floor(Math.abs(argument.numberValue())) !== Math.abs(argument.numberValue())) {
|
||||
return Value.false;
|
||||
}
|
||||
return Value.true;
|
||||
}
|
||||
|
||||
// 7.2.7 #sec-ispropertykey
|
||||
export function IsPropertyKey(argument) {
|
||||
if (Type(argument) === 'String') {
|
||||
return true;
|
||||
}
|
||||
if (Type(argument) === 'Symbol') {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// 7.2.8 #sec-isregexp
|
||||
export function IsRegExp(argument) {
|
||||
if (Type(argument) !== 'Object') {
|
||||
return Value.false;
|
||||
}
|
||||
const matcher = Q(Get(argument, wellKnownSymbols.match));
|
||||
if (matcher !== Value.undefined) {
|
||||
return ToBoolean(matcher);
|
||||
}
|
||||
if ('RegExpMatcher' in argument) {
|
||||
return Value.true;
|
||||
}
|
||||
return Value.false;
|
||||
}
|
||||
|
||||
// 7.2.9 #sec-isstringprefix
|
||||
export function IsStringPrefix(p, q) {
|
||||
Assert(Type(p) === 'String');
|
||||
Assert(Type(q) === 'String');
|
||||
return q.stringValue().startsWith(p.stringValue());
|
||||
}
|
||||
|
||||
// 7.2.10 #sec-samevalue
|
||||
export function SameValue(x, y) {
|
||||
// 1. If Type(x) is different from Type(y), return false.
|
||||
if (Type(x) !== Type(y)) {
|
||||
return Value.false;
|
||||
}
|
||||
// 2. If Type(x) is Number or BigInt, then
|
||||
if (Type(x) === 'Number' || Type(x) === 'BigInt') {
|
||||
// a. Return ! Type(x)::sameValue(x, y).
|
||||
return TypeNumeric(x).sameValue(x, y);
|
||||
}
|
||||
// 3. Return ! SameValueNonNumeric(x, y).
|
||||
return X(SameValueNonNumber(x, y));
|
||||
}
|
||||
|
||||
// 7.2.11 #sec-samevaluezero
|
||||
export function SameValueZero(x, y) {
|
||||
// 1. If Type(x) is different from Type(y), return false.
|
||||
if (Type(x) !== Type(y)) {
|
||||
return Value.false;
|
||||
}
|
||||
// 2. If Type(x) is Number or BigInt, then
|
||||
if (Type(x) === 'Number' || Type(x) === 'BigInt') {
|
||||
// a. Return ! Type(x)::sameValueZero(x, y).
|
||||
return TypeNumeric(x).sameValueZero(x, y);
|
||||
}
|
||||
// 3. Return ! SameValueNonNumeric(x, y).
|
||||
return X(SameValueNonNumber(x, y));
|
||||
}
|
||||
|
||||
// 7.2.12 #sec-samevaluenonnumber
|
||||
export function SameValueNonNumber(x, y) {
|
||||
Assert(Type(x) !== 'Number');
|
||||
Assert(Type(x) === Type(y));
|
||||
|
||||
if (Type(x) === 'Undefined') {
|
||||
return Value.true;
|
||||
}
|
||||
|
||||
if (Type(x) === 'Null') {
|
||||
return Value.true;
|
||||
}
|
||||
|
||||
if (Type(x) === 'String') {
|
||||
if (x.stringValue() === y.stringValue()) {
|
||||
return Value.true;
|
||||
}
|
||||
return Value.false;
|
||||
}
|
||||
|
||||
if (Type(x) === 'Boolean') {
|
||||
if (x === y) {
|
||||
return Value.true;
|
||||
}
|
||||
return Value.false;
|
||||
}
|
||||
|
||||
if (Type(x) === 'Symbol') {
|
||||
return x === y ? Value.true : Value.false;
|
||||
}
|
||||
|
||||
return x === y ? Value.true : Value.false;
|
||||
}
|
||||
|
||||
// 7.2.13 #sec-abstract-relational-comparison
|
||||
export function AbstractRelationalComparison(x, y, LeftFirst = true) {
|
||||
let px;
|
||||
let py;
|
||||
// 1. If the LeftFirst flag is true, then
|
||||
if (LeftFirst === true) {
|
||||
// a. Let px be ? ToPrimitive(x, hint Number).
|
||||
px = Q(ToPrimitive(x, 'Number'));
|
||||
// b. Let py be ? ToPrimitive(y, hint Number).
|
||||
py = Q(ToPrimitive(y, 'Number'));
|
||||
} else {
|
||||
// a. NOTE: The order of evaluation needs to be reversed to preserve left to right evaluation.
|
||||
// b. Let py be ? ToPrimitive(y, hint Number).
|
||||
py = Q(ToPrimitive(y, 'Number'));
|
||||
// c. Let px be ? ToPrimitive(x, hint Number).
|
||||
px = Q(ToPrimitive(x, 'Number'));
|
||||
}
|
||||
// 3. If Type(px) is String and Type(py) is String, then
|
||||
if (Type(px) === 'String' && Type(py) === 'String') {
|
||||
// a. If IsStringPrefix(py, px) is true, return false.
|
||||
if (IsStringPrefix(py, px)) {
|
||||
return Value.false;
|
||||
}
|
||||
// b. If IsStringPrefix(px, py) is true, return true.
|
||||
if (IsStringPrefix(px, py)) {
|
||||
return Value.true;
|
||||
}
|
||||
// c. Let k be the smallest nonnegative integer such that the code unit at index k within px
|
||||
// is different from the code unit at index k within py. (There must be such a k, for
|
||||
// neither String is a prefix of the other.)
|
||||
let k = 0;
|
||||
while (true) {
|
||||
if (px.stringValue()[k] !== py.stringValue()[k]) {
|
||||
break;
|
||||
}
|
||||
k += 1;
|
||||
}
|
||||
// d. Let m be the integer that is the numeric value of the code unit at index k within px.
|
||||
const m = px.stringValue().charCodeAt(k);
|
||||
// e. Let n be the integer that is the numeric value of the code unit at index k within py.
|
||||
const n = py.stringValue().charCodeAt(k);
|
||||
// f. If m < n, return true. Otherwise, return false.
|
||||
if (m < n) {
|
||||
return Value.true;
|
||||
} else {
|
||||
return Value.false;
|
||||
}
|
||||
} else {
|
||||
// a. If Type(px) is BigInt and Type(py) is String, then
|
||||
if (Type(px) === 'BigInt' && Type(py) === 'String') {
|
||||
// i. Let ny be ! StringToBigInt(py).
|
||||
const ny = X(StringToBigInt(py));
|
||||
// ii. If ny is NaN, return undefined.
|
||||
if (Number.isNaN(ny)) {
|
||||
return Value.undefined;
|
||||
}
|
||||
// iii. Return BigInt::lessThan(px, ny).
|
||||
return BigIntValue.lessThan(px, ny);
|
||||
}
|
||||
// b. If Type(px) is String and Type(py) is BigInt, then
|
||||
if (Type(px) === 'String' && Type(py) === 'BigInt') {
|
||||
// i. Let ny be ! StringToBigInt(py).
|
||||
const nx = X(StringToBigInt(px));
|
||||
// ii. If ny is NaN, return undefined.
|
||||
if (Number.isNaN(nx)) {
|
||||
return Value.undefined;
|
||||
}
|
||||
// iii. Return BigInt::lessThan(px, ny).
|
||||
return BigIntValue.lessThan(nx, py);
|
||||
}
|
||||
// c. Let nx be ? ToNumeric(px). NOTE: Because px and py are primitive values evaluation order is not important.
|
||||
const nx = Q(ToNumeric(px));
|
||||
// d. Let ny be ? ToNumeric(py).
|
||||
const ny = Q(ToNumeric(py));
|
||||
// e. If Type(nx) is the same as Type(ny), return Type(nx)::lessThan(nx, ny).
|
||||
if (Type(nx) === Type(ny)) {
|
||||
return TypeNumeric(nx).lessThan(nx, ny);
|
||||
}
|
||||
// f. Assert: Type(nx) is BigInt and Type(ny) is Number, or Type(nx) is Number and Type(ny) is BigInt.
|
||||
Assert((Type(nx) === 'BigInt' && Type(ny) === 'Number') || (Type(nx) === 'Number' && Type(ny) === 'BigInt'));
|
||||
// g. If nx or ny is NaN, return undefined.
|
||||
if ((nx.isNaN && nx.isNaN()) || (ny.isNaN && ny.isNaN())) {
|
||||
return Value.undefined;
|
||||
}
|
||||
// h. If nx is -∞ or ny is +∞, return true.
|
||||
if ((nx.numberValue && nx.numberValue() === -Infinity) || (ny.numberValue && ny.numberValue() === +Infinity)) {
|
||||
return Value.true;
|
||||
}
|
||||
// i. If nx is +∞ or ny is -∞, return false.
|
||||
if ((nx.numberValue && nx.numberValue() === +Infinity) || (ny.numberValue && ny.numberValue() === -Infinity)) {
|
||||
return Value.false;
|
||||
}
|
||||
// j. If the mathematical value of nx is less than the mathematical value of ny, return true; otherwise return false.
|
||||
const a = nx.numberValue ? nx.numberValue() : nx.bigintValue();
|
||||
const b = ny.numberValue ? ny.numberValue() : ny.bigintValue();
|
||||
return a < b ? Value.true : Value.false;
|
||||
}
|
||||
}
|
||||
|
||||
// 7.2.14 #sec-abstract-equality-comparison
|
||||
export function AbstractEqualityComparison(x, y) {
|
||||
// 1. If Type(x) is the same as Type(y), then
|
||||
if (Type(x) === Type(y)) {
|
||||
// a. Return the result of performing Strict Equality Comparison x === y.
|
||||
return StrictEqualityComparison(x, y);
|
||||
}
|
||||
// 2. If x is null and y is undefined, return true.
|
||||
if (x === Value.null && y === Value.undefined) {
|
||||
return Value.true;
|
||||
}
|
||||
// 3. If x is undefined and y is null, return true.
|
||||
if (x === Value.undefined && y === Value.null) {
|
||||
return Value.true;
|
||||
}
|
||||
// 4. If Type(x) is Number and Type(y) is String, return the result of the comparison x == ! ToNumber(y).
|
||||
if (Type(x) === 'Number' && Type(y) === 'String') {
|
||||
return AbstractEqualityComparison(x, X(ToNumber(y)));
|
||||
}
|
||||
// 5. If Type(x) is String and Type(y) is Number, return the result of the comparison ! ToNumber(x) == y.
|
||||
if (Type(x) === 'String' && Type(y) === 'Number') {
|
||||
return AbstractEqualityComparison(X(ToNumber(x)), y);
|
||||
}
|
||||
// 6. If Type(x) is BigInt and Type(y) is String, then
|
||||
if (Type(x) === 'BigInt' && Type(y) === 'String') {
|
||||
// a. Let n be ! StringToBigInt(y).
|
||||
const n = X(StringToBigInt(y));
|
||||
// b. If n is NaN, return false.
|
||||
if (Number.isNaN(n)) {
|
||||
return Value.false;
|
||||
}
|
||||
// c. Return the result of the comparison x == n.
|
||||
return AbstractEqualityComparison(x, n);
|
||||
}
|
||||
// 7. If Type(x) is String and Type(y) is BigInt, return the result of the comparison y == x.
|
||||
if (Type(x) === 'String' && Type(y) === 'BigInt') {
|
||||
return AbstractEqualityComparison(y, x);
|
||||
}
|
||||
// 8. If Type(x) is Boolean, return the result of the comparison ! ToNumber(x) == y.
|
||||
if (Type(x) === 'Boolean') {
|
||||
return AbstractEqualityComparison(X(ToNumber(x)), y);
|
||||
}
|
||||
// 9. If Type(y) is Boolean, return the result of the comparison x == ! ToNumber(y).
|
||||
if (Type(y) === 'Boolean') {
|
||||
return AbstractEqualityComparison(x, X(ToNumber(y)));
|
||||
}
|
||||
// 10. If Type(x) is either String, Number, BigInt, or Symbol and Type(y) is Object, return the result of the comparison x == ToPrimitive(y).
|
||||
if (['String', 'Number', 'BigInt', 'Symbol'].includes(Type(x)) && Type(y) === 'Object') {
|
||||
return AbstractEqualityComparison(x, Q(ToPrimitive(y)));
|
||||
}
|
||||
// 11. If Type(x) is Object and Type(y) is either String, Number, BigInt, or Symbol, return the result of the comparison ToPrimitive(x) == y.
|
||||
if (Type(x) === 'Object' && ['String', 'Number', 'BigInt', 'Symbol'].includes(Type(y))) {
|
||||
return AbstractEqualityComparison(Q(ToPrimitive(x)), y);
|
||||
}
|
||||
// 12. If Type(x) is BigInt and Type(y) is Number, or if Type(x) is Number and Type(y) is BigInt, then
|
||||
if ((Type(x) === 'BigInt' && Type(y) === 'Number') || (Type(x) === 'Number' && Type(y) === 'BigInt')) {
|
||||
// a. If x or y are any of NaN, +∞, or -∞, return false.
|
||||
if ((x.isNaN && (x.isNaN() || !x.isFinite())) || (y.isNaN && (y.isNaN() || !y.isFinite()))) {
|
||||
return Value.false;
|
||||
}
|
||||
// b. If the mathematical value of x is equal to the mathematical value of y, return true; otherwise return false.
|
||||
const a = (x.numberValue ? x.numberValue() : x.bigintValue());
|
||||
const b = (y.numberValue ? y.numberValue() : y.bigintValue());
|
||||
return a == b ? Value.true : Value.false; // eslint-disable-line eqeqeq
|
||||
}
|
||||
// 13. Return false.
|
||||
return Value.false;
|
||||
}
|
||||
|
||||
// 7.2.15 #sec-strict-equality-comparison
|
||||
export function StrictEqualityComparison(x, y) {
|
||||
// 1. If Type(x) is different from Type(y), return false.
|
||||
if (Type(x) !== Type(y)) {
|
||||
return Value.false;
|
||||
}
|
||||
// 2. If Type(x) is Number or BigInt, then
|
||||
if (Type(x) === 'Number' || Type(x) === 'BigInt') {
|
||||
// a. Return ! Type(x)::equal(x, y).
|
||||
return X(TypeNumeric(x).equal(x, y));
|
||||
}
|
||||
// 3. Return ! SameValueNonNumeric(x, y).
|
||||
return SameValueNonNumber(x, y);
|
||||
}
|
||||
|
||||
// #sec-isvalidintegerindex
|
||||
export function IsValidIntegerIndex(O, index) {
|
||||
Assert(isIntegerIndexedExoticObject(O));
|
||||
Assert(Type(index) === 'Number');
|
||||
if (IsInteger(index) === Value.false) {
|
||||
return Value.false;
|
||||
}
|
||||
index = index.numberValue();
|
||||
if (Object.is(index, -0)) {
|
||||
return Value.false;
|
||||
}
|
||||
if (index < 0 || index >= O.ArrayLength.numberValue()) {
|
||||
return Value.false;
|
||||
}
|
||||
return Value.true;
|
||||
}
|
||||
|
||||
// #sec-isnonnegativeinteger
|
||||
export function IsNonNegativeInteger(argument) {
|
||||
// 1. If ! IsInteger(argument) is true and argument ≥ 0, return true.
|
||||
if (X(IsInteger(argument)) === Value.true && argument.numberValue() >= 0) {
|
||||
return Value.true;
|
||||
}
|
||||
// 2. Otherwise, return false.
|
||||
return Value.false;
|
||||
}
|
||||
@@ -1,469 +0,0 @@
|
||||
import {
|
||||
Type,
|
||||
Value,
|
||||
NumberValue,
|
||||
BigIntValue,
|
||||
wellKnownSymbols,
|
||||
} from '../value.mjs';
|
||||
import {
|
||||
surroundingAgent,
|
||||
} from '../engine.mjs';
|
||||
import { Q, X } from '../completion.mjs';
|
||||
import { OutOfRange } from '../helpers.mjs';
|
||||
import { MV_StringNumericLiteral } from '../runtime-semantics/all.mjs';
|
||||
import {
|
||||
Assert,
|
||||
Call,
|
||||
Get,
|
||||
GetMethod,
|
||||
IsCallable,
|
||||
OrdinaryObjectCreate,
|
||||
SameValue,
|
||||
StringCreate,
|
||||
} from './all.mjs';
|
||||
|
||||
// 7.1.1 #sec-toprimitive
|
||||
export function ToPrimitive(input, PreferredType) {
|
||||
Assert(input instanceof Value);
|
||||
if (Type(input) === 'Object') {
|
||||
let hint;
|
||||
if (PreferredType === undefined) {
|
||||
hint = new Value('default');
|
||||
} else if (PreferredType === 'String') {
|
||||
hint = new Value('string');
|
||||
} else {
|
||||
Assert(PreferredType === 'Number');
|
||||
hint = new Value('number');
|
||||
}
|
||||
const exoticToPrim = Q(GetMethod(input, wellKnownSymbols.toPrimitive));
|
||||
if (exoticToPrim !== Value.undefined) {
|
||||
const result = Q(Call(exoticToPrim, input, [hint]));
|
||||
if (Type(result) !== 'Object') {
|
||||
return result;
|
||||
}
|
||||
return surroundingAgent.Throw('TypeError', 'ObjectToPrimitive');
|
||||
}
|
||||
if (hint.stringValue() === 'default') {
|
||||
hint = new Value('number');
|
||||
}
|
||||
return Q(OrdinaryToPrimitive(input, hint));
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
// 7.1.1.1 #sec-ordinarytoprimitive
|
||||
export function OrdinaryToPrimitive(O, hint) {
|
||||
Assert(Type(O) === 'Object');
|
||||
Assert(Type(hint) === 'String' && (hint.stringValue() === 'string' || hint.stringValue() === 'number'));
|
||||
let methodNames;
|
||||
if (hint.stringValue() === 'string') {
|
||||
methodNames = [new Value('toString'), new Value('valueOf')];
|
||||
} else {
|
||||
methodNames = [new Value('valueOf'), new Value('toString')];
|
||||
}
|
||||
for (const name of methodNames) {
|
||||
const method = Q(Get(O, name));
|
||||
if (IsCallable(method) === Value.true) {
|
||||
const result = Q(Call(method, O));
|
||||
if (Type(result) !== 'Object') {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
return surroundingAgent.Throw('TypeError', 'ObjectToPrimitive');
|
||||
}
|
||||
|
||||
// 7.1.2 #sec-toboolean
|
||||
export function ToBoolean(argument) {
|
||||
const type = Type(argument);
|
||||
switch (type) {
|
||||
case 'Undefined':
|
||||
return Value.false;
|
||||
case 'Null':
|
||||
return Value.false;
|
||||
case 'Boolean':
|
||||
return argument;
|
||||
case 'Number':
|
||||
if (argument.numberValue() === 0 || argument.isNaN()) {
|
||||
return Value.false;
|
||||
}
|
||||
return Value.true;
|
||||
case 'String':
|
||||
if (argument.stringValue().length === 0) {
|
||||
return Value.false;
|
||||
}
|
||||
return Value.true;
|
||||
case 'Symbol':
|
||||
return Value.true;
|
||||
case 'BigInt':
|
||||
if (argument.bigintValue() === 0n) {
|
||||
return Value.false;
|
||||
}
|
||||
return Value.true;
|
||||
case 'Object':
|
||||
return Value.true;
|
||||
default:
|
||||
throw new OutOfRange('ToBoolean', { type, argument });
|
||||
}
|
||||
}
|
||||
|
||||
// #sec-tonumeric
|
||||
export function ToNumeric(value) {
|
||||
// 1. Let primValue be ? ToPrimitive(value, hint Number).
|
||||
const primValue = Q(ToPrimitive(value, 'Number'));
|
||||
// 2. If Type(primValue) is BigInt, return primValue.
|
||||
if (Type(primValue) === 'BigInt') {
|
||||
return primValue;
|
||||
}
|
||||
// 3. Return ? ToNumber(primValue).
|
||||
return Q(ToNumber(primValue));
|
||||
}
|
||||
|
||||
// 7.1.3 #sec-tonumber
|
||||
export function ToNumber(argument) {
|
||||
const type = Type(argument);
|
||||
switch (type) {
|
||||
case 'Undefined':
|
||||
return new Value(NaN);
|
||||
case 'Null':
|
||||
return new Value(0);
|
||||
case 'Boolean':
|
||||
if (argument === Value.true) {
|
||||
return new Value(1);
|
||||
}
|
||||
return new Value(0);
|
||||
case 'Number':
|
||||
return argument;
|
||||
case 'String':
|
||||
return MV_StringNumericLiteral(argument.stringValue());
|
||||
case 'BigInt':
|
||||
return surroundingAgent.Throw('TypeError', 'CannotMixBigInts');
|
||||
case 'Symbol':
|
||||
return surroundingAgent.Throw('TypeError', 'CannotConvertSymbol', 'number');
|
||||
case 'Object': {
|
||||
const primValue = Q(ToPrimitive(argument, 'Number'));
|
||||
return Q(ToNumber(primValue));
|
||||
}
|
||||
default:
|
||||
throw new OutOfRange('ToNumber', { type, argument });
|
||||
}
|
||||
}
|
||||
|
||||
const mod = (n, m) => {
|
||||
const r = n % m;
|
||||
return Math.floor(r >= 0 ? r : r + m);
|
||||
};
|
||||
|
||||
// 7.1.4 #sec-tointeger
|
||||
export function ToInteger(argument) {
|
||||
// 1. Let number be ? ToNumber(argument).
|
||||
const number = Q(ToNumber(argument)).numberValue();
|
||||
// 2. If number is NaN, +0, or -0, return +0.
|
||||
if (Number.isNaN(number) || number === 0) {
|
||||
return new Value(0);
|
||||
}
|
||||
// 3. If number is +∞, or -∞, return number.
|
||||
if (!Number.isFinite(number)) {
|
||||
return new Value(number);
|
||||
}
|
||||
// 4. Let integer be the Number value that is the same sign as number and whose magnitude is floor(abs(number)).
|
||||
const integer = Math.sign(number) * Math.floor(Math.abs(number));
|
||||
// 5. If integer is -0, return +0.
|
||||
if (Object.is(integer, -0)) {
|
||||
return new Value(+0);
|
||||
}
|
||||
// 6. Return integer.
|
||||
return new Value(integer);
|
||||
}
|
||||
|
||||
// 7.1.5 #sec-toint32
|
||||
export function ToInt32(argument) {
|
||||
const number = Q(ToNumber(argument)).numberValue();
|
||||
if (Number.isNaN(number) || number === 0 || !Number.isFinite(number)) {
|
||||
return new Value(0);
|
||||
}
|
||||
const int = Math.sign(number) * Math.floor(Math.abs(number));
|
||||
const int32bit = mod(int, 2 ** 32);
|
||||
if (int32bit >= (2 ** 31)) {
|
||||
return new Value(int32bit - (2 ** 32));
|
||||
}
|
||||
return new Value(int32bit);
|
||||
}
|
||||
|
||||
// 7.1.6 #sec-touint32
|
||||
export function ToUint32(argument) {
|
||||
const number = Q(ToNumber(argument)).numberValue();
|
||||
if (Number.isNaN(number) || number === 0 || !Number.isFinite(number)) {
|
||||
return new Value(0);
|
||||
}
|
||||
const int = Math.sign(number) * Math.floor(Math.abs(number));
|
||||
const int32bit = mod(int, 2 ** 32);
|
||||
return new Value(int32bit);
|
||||
}
|
||||
|
||||
// 7.1.7 #sec-toint16
|
||||
export function ToInt16(argument) {
|
||||
const number = Q(ToNumber(argument)).numberValue();
|
||||
if (Number.isNaN(number) || number === 0 || !Number.isFinite(number)) {
|
||||
return new Value(0);
|
||||
}
|
||||
const int = Math.sign(number) * Math.floor(Math.abs(number));
|
||||
const int16bit = mod(int, 2 ** 16);
|
||||
if (int16bit >= (2 ** 15)) {
|
||||
return new Value(int16bit - (2 ** 16));
|
||||
}
|
||||
return new Value(int16bit);
|
||||
}
|
||||
|
||||
// 7.1.8 #sec-touint16
|
||||
export function ToUint16(argument) {
|
||||
const number = Q(ToNumber(argument)).numberValue();
|
||||
if (Number.isNaN(number) || number === 0 || !Number.isFinite(number)) {
|
||||
return new Value(0);
|
||||
}
|
||||
const int = Math.sign(number) * Math.floor(Math.abs(number));
|
||||
const int16bit = mod(int, 2 ** 16);
|
||||
return new Value(int16bit);
|
||||
}
|
||||
|
||||
// 7.1.9 #sec-toint8
|
||||
export function ToInt8(argument) {
|
||||
const number = Q(ToNumber(argument)).numberValue();
|
||||
if (Number.isNaN(number) || number === 0 || !Number.isFinite(number)) {
|
||||
return new Value(0);
|
||||
}
|
||||
const int = Math.sign(number) * Math.floor(Math.abs(number));
|
||||
const int8bit = mod(int, 2 ** 8);
|
||||
if (int8bit >= (2 ** 7)) {
|
||||
return new Value(int8bit - (2 ** 8));
|
||||
}
|
||||
return new Value(int8bit);
|
||||
}
|
||||
|
||||
// 7.1.10 #sec-touint8
|
||||
export function ToUint8(argument) {
|
||||
const number = Q(ToNumber(argument)).numberValue();
|
||||
if (Number.isNaN(number) || number === 0 || !Number.isFinite(number)) {
|
||||
return new Value(0);
|
||||
}
|
||||
const int = Math.sign(number) * Math.floor(Math.abs(number));
|
||||
const int8bit = mod(int, 2 ** 8);
|
||||
return new Value(int8bit);
|
||||
}
|
||||
|
||||
// 7.1.11 #sec-touint8clamp
|
||||
export function ToUint8Clamp(argument) {
|
||||
const number = Q(ToNumber(argument)).numberValue();
|
||||
if (Number.isNaN(number)) {
|
||||
return new Value(0);
|
||||
}
|
||||
if (number <= 0) {
|
||||
return new Value(0);
|
||||
}
|
||||
if (number >= 255) {
|
||||
return new Value(255);
|
||||
}
|
||||
const f = Math.floor(number);
|
||||
if (f + 0.5 < number) {
|
||||
return new Value(f + 1);
|
||||
}
|
||||
if (number < f + 0.5) {
|
||||
return new Value(f);
|
||||
}
|
||||
if (f % 2 === 1) {
|
||||
return new Value(f + 1);
|
||||
}
|
||||
return new Value(f);
|
||||
}
|
||||
|
||||
// #sec-tobigint
|
||||
export function ToBigInt(argument) {
|
||||
// 1. Let prim be ? ToPrimitive(argument, hint Number).
|
||||
const prim = Q(ToPrimitive(argument, 'Number'));
|
||||
// 2. Return the value that prim corresponds to in Table 12 (#table-tobigint).
|
||||
switch (Type(prim)) {
|
||||
case 'Undefined':
|
||||
// Throw a TypeError exception.
|
||||
return surroundingAgent.Throw('TypeError', 'CannotConvertToBigInt', prim);
|
||||
case 'Null':
|
||||
// Throw a TypeError exception.
|
||||
return surroundingAgent.Throw('TypeError', 'CannotConvertToBigInt', prim);
|
||||
case 'Boolean':
|
||||
// Return 1n if prim is true and 0n if prim is false.
|
||||
if (prim === Value.true) {
|
||||
return new Value(1n);
|
||||
}
|
||||
return new Value(0n);
|
||||
case 'BigInt':
|
||||
// Return prim.
|
||||
return prim;
|
||||
case 'Number':
|
||||
// Throw a TypeError exception.
|
||||
return surroundingAgent.Throw('TypeError', 'CannotConvertToBigInt', prim);
|
||||
case 'String': {
|
||||
// 1. Let n be ! StringToBigInt(prim).
|
||||
const n = X(StringToBigInt(prim));
|
||||
// 2. If n is NaN, throw a SyntaxError exception.
|
||||
if (Number.isNaN(n)) {
|
||||
return surroundingAgent.Throw('SyntaxError', 'CannotConvertToBigInt', prim);
|
||||
}
|
||||
// 3. Return n.
|
||||
return n;
|
||||
}
|
||||
case 'Symbol':
|
||||
return surroundingAgent.Throw('TypeError', 'CannotConvertSymbol', 'bigint');
|
||||
default:
|
||||
throw new OutOfRange('ToBigInt', argument);
|
||||
}
|
||||
}
|
||||
|
||||
// #sec-stringtobigint
|
||||
export function StringToBigInt(argument) {
|
||||
// Apply the algorithm in 7.1.4.1 (#sec-tonumber-applied-to-the-string-type) with the following changes:
|
||||
// 1. Replace the StrUnsignedDecimalLiteral production with DecimalDigits to not allow Infinity, decimal points, or exponents.
|
||||
// 2. If the MV is NaN, return NaN, otherwise return the BigInt which exactly corresponds to the MV, rather than rounding to a Number.
|
||||
// TODO: Adapt nearley grammar for this.
|
||||
try {
|
||||
return new Value(BigInt(argument.stringValue()));
|
||||
} catch {
|
||||
return NaN;
|
||||
}
|
||||
}
|
||||
|
||||
// #sec-tobigint64
|
||||
export function ToBigInt64(argument) {
|
||||
// 1. Let n be ? ToBigInt(argument).
|
||||
const n = Q(ToBigInt(argument));
|
||||
// 2. Let int64bit be n modulo 2^64.
|
||||
const int64bit = n.bigintValue() % (2n ** 64n);
|
||||
// 3. If int64bit ≥ 2^63, return int64bit - 2^64; otherwise return int64bit.
|
||||
if (int64bit >= 2n ** 63n) {
|
||||
return new Value(int64bit - (2n ** 64n));
|
||||
}
|
||||
return new Value(int64bit);
|
||||
}
|
||||
|
||||
// #sec-tobiguint64
|
||||
export function ToBigUint64(argument) {
|
||||
// 1. Let n be ? ToBigInt(argument).
|
||||
const n = Q(ToBigInt(argument));
|
||||
// 2. Let int64bit be n modulo 2^64.
|
||||
const int64bit = n.bigintValue() % (2n ** 64n);
|
||||
// 3. Return int64bit.
|
||||
return new Value(int64bit);
|
||||
}
|
||||
|
||||
// 7.1.12 #sec-tostring
|
||||
export function ToString(argument) {
|
||||
const type = Type(argument);
|
||||
switch (type) {
|
||||
case 'Undefined':
|
||||
return new Value('undefined');
|
||||
case 'Null':
|
||||
return new Value('null');
|
||||
case 'Boolean':
|
||||
return new Value(argument === Value.true ? 'true' : 'false');
|
||||
case 'Number':
|
||||
// Return ! Number::toString(argument).
|
||||
return X(NumberValue.toString(argument));
|
||||
case 'String':
|
||||
return argument;
|
||||
case 'Symbol':
|
||||
return surroundingAgent.Throw('TypeError', 'CannotConvertSymbol', 'string');
|
||||
case 'BigInt':
|
||||
// Return ! BigInt::toString(argument).
|
||||
return X(BigIntValue.toString(argument));
|
||||
case 'Object': {
|
||||
const primValue = Q(ToPrimitive(argument, 'String'));
|
||||
return Q(ToString(primValue));
|
||||
}
|
||||
default:
|
||||
throw new OutOfRange('ToString', { type, argument });
|
||||
}
|
||||
}
|
||||
|
||||
// 7.1.13 #sec-toobject
|
||||
export function ToObject(argument) {
|
||||
const type = Type(argument);
|
||||
switch (type) {
|
||||
case 'Undefined':
|
||||
return surroundingAgent.Throw('TypeError', 'CannotConvertToObject', 'undefined');
|
||||
case 'Null':
|
||||
return surroundingAgent.Throw('TypeError', 'CannotConvertToObject', 'null');
|
||||
case 'Boolean': {
|
||||
const obj = OrdinaryObjectCreate(surroundingAgent.intrinsic('%Boolean.prototype%'));
|
||||
obj.BooleanData = argument;
|
||||
return obj;
|
||||
}
|
||||
case 'Number': {
|
||||
const obj = OrdinaryObjectCreate(surroundingAgent.intrinsic('%Number.prototype%'));
|
||||
obj.NumberData = argument;
|
||||
return obj;
|
||||
}
|
||||
case 'String':
|
||||
return StringCreate(argument, surroundingAgent.intrinsic('%String.prototype%'));
|
||||
case 'Symbol': {
|
||||
const obj = OrdinaryObjectCreate(surroundingAgent.intrinsic('%Symbol.prototype%'));
|
||||
obj.SymbolData = argument;
|
||||
return obj;
|
||||
}
|
||||
case 'BigInt': {
|
||||
const obj = OrdinaryObjectCreate(surroundingAgent.intrinsic('%BigInt.prototype%'));
|
||||
obj.BigIntData = argument;
|
||||
return obj;
|
||||
}
|
||||
case 'Object':
|
||||
return argument;
|
||||
default:
|
||||
throw new OutOfRange('ToObject', { type, argument });
|
||||
}
|
||||
}
|
||||
|
||||
// 7.1.14 #sec-topropertykey
|
||||
export function ToPropertyKey(argument) {
|
||||
const key = Q(ToPrimitive(argument, 'String'));
|
||||
if (Type(key) === 'Symbol') {
|
||||
return key;
|
||||
}
|
||||
return X(ToString(key));
|
||||
}
|
||||
|
||||
// 7.1.15 #sec-tolength
|
||||
export function ToLength(argument) {
|
||||
const len = Q(ToInteger(argument));
|
||||
if (len.numberValue() <= 0) {
|
||||
return new Value(0);
|
||||
}
|
||||
return new Value(Math.min(len.numberValue(), (2 ** 53) - 1));
|
||||
}
|
||||
|
||||
// 7.1.16 #sec-canonicalnumericindexstring
|
||||
export function CanonicalNumericIndexString(argument) {
|
||||
Assert(Type(argument) === 'String');
|
||||
if (argument.stringValue() === '-0') {
|
||||
return new Value(-0);
|
||||
}
|
||||
const n = X(ToNumber(argument));
|
||||
if (SameValue(X(ToString(n)), argument) === Value.false) {
|
||||
return Value.undefined;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
// 7.1.17 #sec-toindex
|
||||
export function ToIndex(value) {
|
||||
let index;
|
||||
if (Type(value) === 'Undefined') {
|
||||
index = new Value(0);
|
||||
} else {
|
||||
const integerIndex = Q(ToInteger(value));
|
||||
if (integerIndex.numberValue() < 0) {
|
||||
return surroundingAgent.Throw('RangeError', 'NegativeIndex', 'Index');
|
||||
}
|
||||
index = X(ToLength(integerIndex));
|
||||
if (X(SameValue(integerIndex, index)) === Value.false) {
|
||||
return surroundingAgent.Throw('RangeError', 'OutOfRange', 'Index');
|
||||
}
|
||||
}
|
||||
return index;
|
||||
}
|
||||
@@ -1,240 +0,0 @@
|
||||
import { surroundingAgent } from '../engine.mjs';
|
||||
import { Type, Value } from '../value.mjs';
|
||||
import { Q, X } from '../completion.mjs';
|
||||
import {
|
||||
Assert,
|
||||
ToInt8,
|
||||
ToUint8,
|
||||
ToUint8Clamp,
|
||||
ToInt16,
|
||||
ToUint16,
|
||||
ToInt32,
|
||||
ToUint32,
|
||||
ToBigInt64,
|
||||
ToBigUint64,
|
||||
RequireInternalSlot,
|
||||
Construct,
|
||||
GetIterator,
|
||||
IteratorStep,
|
||||
IteratorValue,
|
||||
SpeciesConstructor,
|
||||
IsDetachedBuffer,
|
||||
IsNonNegativeInteger,
|
||||
IntegerIndexedObjectCreate,
|
||||
GetPrototypeFromConstructor,
|
||||
AllocateArrayBuffer,
|
||||
} from './all.mjs';
|
||||
|
||||
export const typedArrayInfoByName = {
|
||||
Int8Array: {
|
||||
IntrinsicName: '%Int8Array%',
|
||||
ElementType: 'Int8',
|
||||
ElementSize: 1,
|
||||
ConversionOperation: ToInt8,
|
||||
},
|
||||
Uint8Array: {
|
||||
IntrinsicName: '%Uint8Array%',
|
||||
ElementType: 'Uint8',
|
||||
ElementSize: 1,
|
||||
ConversionOperation: ToUint8,
|
||||
},
|
||||
Uint8ClampedArray: {
|
||||
IntrinsicName: '%Uint8ClampedArray%',
|
||||
ElementType: 'Uint8C',
|
||||
ElementSize: 1,
|
||||
ConversionOperation: ToUint8Clamp,
|
||||
},
|
||||
Int16Array: {
|
||||
IntrinsicName: '%Int16Array%',
|
||||
ElementType: 'Int16',
|
||||
ElementSize: 2,
|
||||
ConversionOperation: ToInt16,
|
||||
},
|
||||
Uint16Array: {
|
||||
IntrinsicName: '%Uint16Array%',
|
||||
ElementType: 'Uint16',
|
||||
ElementSize: 2,
|
||||
ConversionOperation: ToUint16,
|
||||
},
|
||||
Int32Array: {
|
||||
IntrinsicName: '%Int32Array%',
|
||||
ElementType: 'Int32',
|
||||
ElementSize: 4,
|
||||
ConversionOperation: ToInt32,
|
||||
},
|
||||
Uint32Array: {
|
||||
IntrinsicName: '%Uint32Array%',
|
||||
ElementType: 'Uint32',
|
||||
ElementSize: 4,
|
||||
ConversionOperation: ToUint32,
|
||||
},
|
||||
BigInt64Array: {
|
||||
IntrinsicName: '%BigInt64Array%',
|
||||
ElementType: 'BigInt64',
|
||||
ElementSize: 8,
|
||||
ConversionOperation: ToBigInt64,
|
||||
},
|
||||
BigUint64Array: {
|
||||
IntrinsicName: '%BigUint64Array%',
|
||||
ElementType: 'BigUint64',
|
||||
ElementSize: 8,
|
||||
ConversionOperation: ToBigUint64,
|
||||
},
|
||||
Float32Array: {
|
||||
IntrinsicName: '%Float32Array%',
|
||||
ElementType: 'Float32',
|
||||
ElementSize: 4,
|
||||
ConversionOperation: undefined,
|
||||
},
|
||||
Float64Array: {
|
||||
IntrinsicName: '%Float64Array%',
|
||||
ElementType: 'Float64',
|
||||
ElementSize: 8,
|
||||
ConversionOperation: undefined,
|
||||
},
|
||||
};
|
||||
|
||||
export const typedArrayInfoByType = {};
|
||||
Object.values(typedArrayInfoByName).forEach((v) => {
|
||||
typedArrayInfoByType[v.ElementType] = v;
|
||||
});
|
||||
|
||||
// #sec-validatetypedarray
|
||||
export function ValidateTypedArray(O) {
|
||||
// 1. Perform ? RequireInternalSlot(O, [[TypedArrayName]]).
|
||||
Q(RequireInternalSlot(O, 'TypedArrayName'));
|
||||
// 2. Assert: O has a [[ViewedArrayBuffer]] internal slot.
|
||||
Assert('ViewedArrayBuffer' in O);
|
||||
// 3. Let buffer be O.[[ViewedArrayBuffer]].
|
||||
const buffer = O.ViewedArrayBuffer;
|
||||
// 4. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
|
||||
if (IsDetachedBuffer(buffer) === Value.true) {
|
||||
return surroundingAgent.Throw('TypeError', 'ArrayBufferDetached');
|
||||
}
|
||||
// 5. Return buffer.
|
||||
return buffer;
|
||||
}
|
||||
|
||||
// #typedarray-create
|
||||
export function TypedArrayCreate(constructor, argumentList) {
|
||||
// 1. Let newTypedArray be ? Construct(constructor, argumentList).
|
||||
const newTypedArray = Q(Construct(constructor, argumentList));
|
||||
// 2. Perform ? ValidateTypedArray(newTypedArray).
|
||||
Q(ValidateTypedArray(newTypedArray));
|
||||
// 3. If argumentList is a List of a single Number, then
|
||||
if (argumentList.length === 1 && Type(argumentList[0]) === 'Number') {
|
||||
// a. If newTypedArray.[[ArrayLength]] < argumentList[0], throw a TypeError exception.
|
||||
if (newTypedArray.ArrayLength.numberValue() < argumentList[0].numberValue()) {
|
||||
return surroundingAgent.Throw('TypeError', 'TypedArrayTooSmall');
|
||||
}
|
||||
}
|
||||
// 4. Return newTypedArray.
|
||||
return newTypedArray;
|
||||
}
|
||||
|
||||
// #sec-allocatetypedarray
|
||||
export function AllocateTypedArray(constructorName, newTarget, defaultProto, length) {
|
||||
// 1. Let proto be ? GetPrototypeFromConstructor(newTarget, defaultProto).
|
||||
const proto = Q(GetPrototypeFromConstructor(newTarget, defaultProto));
|
||||
// 2. Let obj be ! IntegerIndexedObjectCreate(proto).
|
||||
const obj = X(IntegerIndexedObjectCreate(proto));
|
||||
// 3. Assert: obj.[[ViewedArrayBuffer]] is undefined.
|
||||
Assert(obj.ViewedArrayBuffer === Value.undefined);
|
||||
// 4. Set obj.[[TypedArrayName]] to constructorName.
|
||||
obj.TypedArrayName = constructorName;
|
||||
// 5. If constructorName is "BigInt64Array" or "BigUint64Array", set obj.[[ContentType]] to BigInt.
|
||||
// 6. Otherwise, set obj.[[ContentType]] to Number.
|
||||
if (constructorName.stringValue() === 'BigInt64Array' || constructorName.stringValue() === 'BigUint64Array') {
|
||||
obj.ContentType = 'BigInt';
|
||||
} else {
|
||||
obj.ContentType = 'Number';
|
||||
}
|
||||
// 7. If length is not present, then
|
||||
if (length === undefined) {
|
||||
// 1. Set obj.[[ByteLength]] to 0.
|
||||
obj.ByteLength = new Value(0);
|
||||
// 1. Set obj.[[ByteOffset]] to 0.
|
||||
obj.ByteOffset = new Value(0);
|
||||
// 1. Set obj.[[ArrayLength]] to 0.
|
||||
obj.ArrayLength = new Value(0);
|
||||
} else {
|
||||
// a. Perform ? AllocateTypedArrayBuffer(obj, length).
|
||||
Q(AllocateTypedArrayBuffer(obj, length));
|
||||
}
|
||||
// 9. Return obj.
|
||||
return obj;
|
||||
}
|
||||
|
||||
// #sec-allocatetypedarraybuffer
|
||||
export function AllocateTypedArrayBuffer(O, length) {
|
||||
// 1. Assert: O is an Object that has a [[ViewedArrayBuffer]] internal slot.
|
||||
Assert(Type(O) === 'Object' && 'ViewedArrayBuffer' in O);
|
||||
// 2. Assert: O.[[ViewedArrayBuffer]] is undefined.
|
||||
Assert(O.ViewedArrayBuffer === Value.undefined);
|
||||
// 3. Assert: ! IsNonNegativeInteger(length) is true.
|
||||
Assert(X(IsNonNegativeInteger(length)) === Value.true);
|
||||
// 4. Let constructorName be the String value of O.[[TypedArrayName]].
|
||||
const constructorName = O.TypedArrayName.stringValue();
|
||||
// 5. Let elementSize be the Element Size value specified in Table 61 for constructorName.
|
||||
const elementSize = typedArrayInfoByName[constructorName].ElementSize;
|
||||
// 6. Let byteLength be elementSize × length.
|
||||
const byteLength = new Value(elementSize * length.numberValue());
|
||||
// 7. Let data be ? AllocateArrayBuffer(%ArrayBuffer%, byteLength).
|
||||
const data = Q(AllocateArrayBuffer(surroundingAgent.intrinsic('%ArrayBuffer%'), byteLength));
|
||||
// 8. Set O.[[ViewedArrayBuffer]] to data.
|
||||
O.ViewedArrayBuffer = data;
|
||||
// 9. Set O.[[ByteLength]] to byteLength.
|
||||
O.ByteLength = byteLength;
|
||||
// 10. Set O.[[ByteOffset]] to 0.
|
||||
O.ByteOffset = new Value(0);
|
||||
// 11. Set O.[[ArrayLength]] to length.
|
||||
O.ArrayLength = length;
|
||||
// 12. Return O.
|
||||
return O;
|
||||
}
|
||||
|
||||
// #typedarray-species-create
|
||||
export function TypedArraySpeciesCreate(exemplar, argumentList) {
|
||||
// 1. Assert: exemplar is an Object that has [[TypedArrayName]] and [[ContentType]] internal slots.
|
||||
Assert(Type(exemplar) === 'Object'
|
||||
&& 'TypedArrayName' in exemplar
|
||||
&& 'ContentType' in exemplar);
|
||||
// 2. Let defaultConstructor be the intrinsic object listed in column one of Table 61 for exemplar.[[TypedArrayName]].
|
||||
const defaultConstructor = surroundingAgent.intrinsic(typedArrayInfoByName[exemplar.TypedArrayName.stringValue()].IntrinsicName);
|
||||
// 3. Let constructor be ? SpeciesConstructor(exemplar, defaultConstructor).
|
||||
const constructor = Q(SpeciesConstructor(exemplar, defaultConstructor));
|
||||
// 4. Let result be ? TypedArrayCreate(constructor, argumentList).
|
||||
const result = Q(TypedArrayCreate(constructor, argumentList));
|
||||
// 5. Assert: result has [[TypedArrayName]] and [[ContentType]] internal slots.
|
||||
Assert('TypedArrayName' in result && 'ContentType' in result);
|
||||
// 6. If result.[[ContentType]] is not equal to exemplar.[[ContentType]], throw a TypeError exception.
|
||||
if (result.ContentType !== exemplar.ContentType) {
|
||||
return surroundingAgent.Throw('TypeError', 'BufferContentTypeMismatch');
|
||||
}
|
||||
// 7. Return result.
|
||||
return result;
|
||||
}
|
||||
|
||||
// #sec-iterabletolist
|
||||
export function IterableToList(items, method) {
|
||||
// 1. Let iteratorRecord be ? GetIterator(items, sync, method).
|
||||
const iteratorRecord = Q(GetIterator(items, 'sync', method));
|
||||
// 2. Let values be a new empty List.
|
||||
const values = [];
|
||||
// 3. Let next be true.
|
||||
let next = Value.true;
|
||||
// 4. Repeat, while next is not false
|
||||
while (next !== Value.false) {
|
||||
// a. Set next to ? IteratorStep(iteratorRecord).
|
||||
next = Q(IteratorStep(iteratorRecord));
|
||||
// b. If next is not false, then
|
||||
if (next !== Value.false) {
|
||||
// i. Let nextValue be ? IteratorValue(next).
|
||||
const nextValue = Q(IteratorValue(next));
|
||||
// ii. Append nextValue to the end of the List values.
|
||||
values.push(nextValue);
|
||||
}
|
||||
}
|
||||
// 5. Return values.
|
||||
return values;
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
import { surroundingAgent } from '../engine.mjs';
|
||||
import { Value } from '../value.mjs';
|
||||
import { NormalCompletion, Q, X } from '../completion.mjs';
|
||||
import { Assert, Call } from './all.mjs';
|
||||
|
||||
// #sec-clear-kept-objects
|
||||
export function ClearKeptObjects() {
|
||||
// 1. Let agentRecord be the surrounding agent's Agent Record.
|
||||
const agentRecord = surroundingAgent.AgentRecord;
|
||||
// 2. Set agentRecord.[[KeptAlive]] to a new empty List.
|
||||
agentRecord.KeptAlive = new Set();
|
||||
}
|
||||
|
||||
// #sec-addtokeptobjects
|
||||
export function AddToKeptObjects(object) {
|
||||
// 1. Let agentRecord be the surrounding agent's Agent Record.
|
||||
const agentRecord = surroundingAgent.AgentRecord;
|
||||
// 2. Append object to agentRecord.[[KeptAlive]].
|
||||
agentRecord.KeptAlive.add(object);
|
||||
}
|
||||
|
||||
// #sec-weakrefderef
|
||||
export function WeakRefDeref(weakRef) {
|
||||
// 1. Let target be weakRef.[[WeakRefTarget]].
|
||||
const target = weakRef.WeakRefTarget;
|
||||
// 2. If target is not empty, then
|
||||
if (target !== undefined) {
|
||||
// a. Perform ! AddToKeptObjects(target).
|
||||
X(AddToKeptObjects(target));
|
||||
// b. Return target.
|
||||
return target;
|
||||
}
|
||||
// 3. Return undefined.
|
||||
return Value.undefined;
|
||||
}
|
||||
|
||||
// #sec-cleanup-finalization-registry
|
||||
export function CleanupFinalizationRegistry(finalizationRegistry, callback) {
|
||||
// 1. Assert: finalizationRegistry has [[Cells]] and [[CleanupCallback]] internal slots.
|
||||
Assert('Cells' in finalizationRegistry && 'CleanupCallback' in finalizationRegistry);
|
||||
// 2. Set callback to finalizationRegistry.[[CleanupCallback]].
|
||||
if (callback === undefined || callback === Value.undefined) {
|
||||
callback = finalizationRegistry.CleanupCallback;
|
||||
}
|
||||
// 3. While finalizationRegistry.[[Cells]] contains a Record cell such that cell.[[WeakRefTarget]] is empty, an implementation may perform the following steps:
|
||||
for (let i = 0; i < finalizationRegistry.Cells.length; i += 1) {
|
||||
// a. Choose any such _cell_.
|
||||
const cell = finalizationRegistry.Cells[i];
|
||||
if (cell.WeakRefTarget !== undefined) {
|
||||
continue;
|
||||
}
|
||||
// b. Remove cell from finalizationRegistry.[[Cells]].
|
||||
finalizationRegistry.Cells.splice(i, 1);
|
||||
i -= 1;
|
||||
// c. Perform ? Call(callback, undefined, « cell.[[HeldValue]] »).
|
||||
Q(Call(callback, Value.undefined, [cell.HeldValue]));
|
||||
}
|
||||
// 4. Return NormalCompletion(undefined).
|
||||
return NormalCompletion(Value.undefined);
|
||||
}
|
||||
@@ -1,269 +0,0 @@
|
||||
import { Value } from './value.mjs';
|
||||
import {
|
||||
surroundingAgent,
|
||||
ExecutionContext,
|
||||
HostEnqueueFinalizationRegistryCleanupJob,
|
||||
ScriptEvaluation,
|
||||
} from './engine.mjs';
|
||||
import {
|
||||
X,
|
||||
ThrowCompletion,
|
||||
AbruptCompletion,
|
||||
EnsureCompletion,
|
||||
} from './completion.mjs';
|
||||
import {
|
||||
Realm,
|
||||
ClearKeptObjects,
|
||||
CreateIntrinsics,
|
||||
SetRealmGlobalObject,
|
||||
SetDefaultGlobalBindings,
|
||||
} from './abstract-ops/all.mjs';
|
||||
import {
|
||||
ParseScript,
|
||||
ParseModule,
|
||||
} from './parse.mjs';
|
||||
import { SourceTextModuleRecord } from './modules.mjs';
|
||||
|
||||
export * from './value.mjs';
|
||||
export * from './engine.mjs';
|
||||
export * from './completion.mjs';
|
||||
export * from './abstract-ops/all.mjs';
|
||||
export * from './static-semantics/all.mjs';
|
||||
export * from './runtime-semantics/all.mjs';
|
||||
export * from './environment.mjs';
|
||||
export * from './parse.mjs';
|
||||
export * from './modules.mjs';
|
||||
export * from './inspect.mjs';
|
||||
|
||||
export function Throw(...args) {
|
||||
return surroundingAgent.Throw(...args);
|
||||
}
|
||||
|
||||
export function gc() {
|
||||
// #sec-weakref-execution
|
||||
// At any time, if a set of objects S is not live, an ECMAScript implementation may perform the following steps atomically:
|
||||
// 1. For each obj of S, do
|
||||
// a. For each WeakRef ref such that ref.[[WeakRefTarget]] is obj,
|
||||
// i. Set ref.[[WeakRefTarget]] to empty.
|
||||
// b. For each FinalizationRegistry fg such that fg.[[Cells]] contains cell, and cell.[[WeakRefTarget]] is obj,
|
||||
// i. Set cell.[[WeakRefTarget]] to empty.
|
||||
// ii. Optionally, perform ! HostEnqueueFinalizationRegistryCleanupJob(fg).
|
||||
// c. For each WeakMap map such that map.WeakMapData contains a record r such that r.Key is obj,
|
||||
// i. Set r.[[Key]] to empty.
|
||||
// ii. Set r.[[Value]] to empty.
|
||||
// d. For each WeakSet set such that set.[[WeakSetData]] contains obj,
|
||||
// i. Replace the element of set whose value is obj with an element whose value is empty.
|
||||
|
||||
const marked = new Set();
|
||||
const weakrefs = new Set();
|
||||
const fgs = new Set();
|
||||
const weakmaps = new Set();
|
||||
const weaksets = new Set();
|
||||
const ephemeronQueue = [];
|
||||
|
||||
const markCb = (O) => {
|
||||
if (typeof O !== 'object' || O === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (marked.has(O)) {
|
||||
return;
|
||||
}
|
||||
marked.add(O);
|
||||
|
||||
if ('WeakRefTarget' in O && !('HeldValue' in O)) {
|
||||
weakrefs.add(O);
|
||||
markCb(O.properties);
|
||||
markCb(O.Prototype);
|
||||
} else if ('Cells' in O) {
|
||||
fgs.add(O);
|
||||
markCb(O.properties);
|
||||
markCb(O.Prototype);
|
||||
O.Cells.forEach((cell) => {
|
||||
markCb(cell.HeldValue);
|
||||
});
|
||||
} else if ('WeakMapData' in O) {
|
||||
weakmaps.add(O);
|
||||
markCb(O.properties);
|
||||
markCb(O.Prototype);
|
||||
O.WeakMapData.forEach((r) => {
|
||||
ephemeronQueue.push(r);
|
||||
});
|
||||
} else if ('WeakSetData' in O) {
|
||||
weaksets.add(O);
|
||||
markCb(O.properties);
|
||||
markCb(O.Prototype);
|
||||
} else if (O.mark) {
|
||||
O.mark(markCb);
|
||||
}
|
||||
};
|
||||
|
||||
markCb(surroundingAgent);
|
||||
|
||||
while (ephemeronQueue.length > 0) {
|
||||
const item = ephemeronQueue.shift();
|
||||
if (marked.has(item.Key)) {
|
||||
markCb(item.Value);
|
||||
}
|
||||
}
|
||||
|
||||
weakrefs.forEach((ref) => {
|
||||
if (!marked.has(ref.WeakRefTarget)) {
|
||||
ref.WeakRefTarget = undefined;
|
||||
}
|
||||
});
|
||||
|
||||
fgs.forEach((fg) => {
|
||||
let dirty = false;
|
||||
fg.Cells.forEach((cell) => {
|
||||
if (!marked.has(cell.WeakRefTarget)) {
|
||||
cell.WeakRefTarget = undefined;
|
||||
dirty = true;
|
||||
}
|
||||
});
|
||||
if (dirty) {
|
||||
X(HostEnqueueFinalizationRegistryCleanupJob(fg));
|
||||
}
|
||||
});
|
||||
|
||||
weakmaps.forEach((map) => {
|
||||
map.WeakMapData.forEach((r) => {
|
||||
if (!marked.has(r.Key)) {
|
||||
r.Key = undefined;
|
||||
r.Value = undefined;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
weaksets.forEach((set) => {
|
||||
set.WeakSetData.forEach((obj, i) => {
|
||||
if (!marked.has(obj)) {
|
||||
set.WeakSetData[i] = undefined;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// https://tc39.es/ecma262/#sec-jobs
|
||||
export function runJobQueue() {
|
||||
if (surroundingAgent.executionContextStack.some((e) => e.ScriptOrModule !== Value.null)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// At some future point in time, when there is no running execution context
|
||||
// and the execution context stack is empty, the implementation must:
|
||||
while (surroundingAgent.jobQueue.length > 0) { // eslint-disable-line no-constant-condition
|
||||
const {
|
||||
job: abstractClosure,
|
||||
callerRealm,
|
||||
callerScriptOrModule,
|
||||
} = surroundingAgent.jobQueue.shift();
|
||||
|
||||
// 1. Perform any implementation-defined preparation steps.
|
||||
const newContext = new ExecutionContext();
|
||||
surroundingAgent.executionContextStack.push(newContext);
|
||||
newContext.Function = Value.null;
|
||||
newContext.Realm = callerRealm;
|
||||
newContext.ScriptOrModule = callerScriptOrModule;
|
||||
// 2. Call the abstract closure.
|
||||
X(abstractClosure());
|
||||
// 3. Perform any host-defined cleanup steps, after which the execution context stack must be empty.
|
||||
ClearKeptObjects();
|
||||
gc();
|
||||
surroundingAgent.executionContextStack.pop(newContext);
|
||||
}
|
||||
}
|
||||
|
||||
export function evaluateScript(sourceText, realm, hostDefined) {
|
||||
const s = ParseScript(sourceText, realm, hostDefined);
|
||||
if (Array.isArray(s)) {
|
||||
return ThrowCompletion(s[0]);
|
||||
}
|
||||
|
||||
return EnsureCompletion(ScriptEvaluation(s));
|
||||
}
|
||||
|
||||
export class ManagedRealm extends Realm {
|
||||
constructor(HostDefined = {}) {
|
||||
super();
|
||||
// CreateRealm()
|
||||
CreateIntrinsics(this);
|
||||
this.GlobalObject = Value.undefined;
|
||||
this.GlobalEnv = Value.undefined;
|
||||
this.TemplateMap = [];
|
||||
|
||||
// InitializeHostDefinedRealm()
|
||||
const newContext = new ExecutionContext();
|
||||
newContext.Function = Value.null;
|
||||
newContext.Realm = this;
|
||||
newContext.ScriptOrModule = Value.null;
|
||||
surroundingAgent.executionContextStack.push(newContext);
|
||||
SetRealmGlobalObject(this, Value.undefined, Value.undefined);
|
||||
SetDefaultGlobalBindings(this);
|
||||
|
||||
// misc
|
||||
surroundingAgent.executionContextStack.pop(newContext);
|
||||
this.HostDefined = HostDefined;
|
||||
this.topContext = newContext;
|
||||
this.active = false;
|
||||
}
|
||||
|
||||
scope(cb) {
|
||||
if (this.active) {
|
||||
return cb();
|
||||
}
|
||||
this.active = true;
|
||||
surroundingAgent.executionContextStack.push(this.topContext);
|
||||
const r = cb();
|
||||
surroundingAgent.executionContextStack.pop(this.topContext);
|
||||
this.active = false;
|
||||
return r;
|
||||
}
|
||||
|
||||
evaluateScript(sourceText, { specifier } = {}) {
|
||||
if (typeof sourceText !== 'string') {
|
||||
throw new TypeError('sourceText must be a string');
|
||||
}
|
||||
|
||||
const res = this.scope(() => {
|
||||
const realm = surroundingAgent.currentRealmRecord;
|
||||
return evaluateScript(sourceText, realm, {
|
||||
specifier,
|
||||
public: { specifier },
|
||||
});
|
||||
});
|
||||
|
||||
if (!(res instanceof AbruptCompletion)) {
|
||||
runJobQueue();
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
createSourceTextModule(specifier, sourceText) {
|
||||
if (typeof sourceText !== 'string') {
|
||||
throw new TypeError('sourceText must be a string');
|
||||
}
|
||||
if (typeof specifier !== 'string') {
|
||||
throw new TypeError('specifier must be a string');
|
||||
}
|
||||
const module = this.scope(() => ParseModule(sourceText, this, {
|
||||
specifier,
|
||||
SourceTextModuleRecord: ManagedSourceTextModuleRecord,
|
||||
}));
|
||||
if (Array.isArray(module)) {
|
||||
return ThrowCompletion(module[0]);
|
||||
}
|
||||
return module;
|
||||
}
|
||||
}
|
||||
|
||||
class ManagedSourceTextModuleRecord extends SourceTextModuleRecord {
|
||||
Evaluate() {
|
||||
const r = super.Evaluate();
|
||||
if (!(r instanceof AbruptCompletion)) {
|
||||
runJobQueue();
|
||||
}
|
||||
return r;
|
||||
}
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
import { surroundingAgent } from './engine.mjs';
|
||||
import {
|
||||
Assert,
|
||||
CreateBuiltinFunction,
|
||||
PerformPromiseThen,
|
||||
PromiseResolve,
|
||||
SetFunctionLength,
|
||||
} from './abstract-ops/all.mjs';
|
||||
import { Value } from './value.mjs';
|
||||
import { resume } from './helpers.mjs';
|
||||
|
||||
// #sec-completion-record-specification-type
|
||||
export function Completion(init) {
|
||||
if (new.target === Completion) {
|
||||
this.Type = init.Type;
|
||||
this.Value = init.Value;
|
||||
this.Target = init.Target;
|
||||
} else {
|
||||
// 1. Assert: completionRecord is a Completion Record.
|
||||
Assert(init instanceof Completion);
|
||||
// 2. Return completionRecord as the Completion Record of this abstract operation.
|
||||
return init;
|
||||
}
|
||||
}
|
||||
|
||||
// NON-SPEC
|
||||
Completion.prototype.mark = function mark(m) {
|
||||
m(this.Value);
|
||||
};
|
||||
|
||||
// #sec-normalcompletion
|
||||
export function NormalCompletion(argument) {
|
||||
// 1. Return Completion { [[Type]]: normal, [[Value]]: argument, [[Target]]: empty }.
|
||||
return new Completion({ Type: 'normal', Value: argument, Target: undefined });
|
||||
}
|
||||
|
||||
Object.defineProperty(NormalCompletion, Symbol.hasInstance, {
|
||||
value: function hasInstance(v) {
|
||||
return v instanceof Completion && v.Type === 'normal';
|
||||
},
|
||||
writable: true,
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
});
|
||||
|
||||
export class AbruptCompletion {
|
||||
static [Symbol.hasInstance](v) {
|
||||
return v instanceof Completion && v.Type !== 'normal';
|
||||
}
|
||||
}
|
||||
|
||||
// #sec-throwcompletion
|
||||
export function ThrowCompletion(argument) {
|
||||
// 1. Return Completion { [[Type]]: throw, [[Value]]: argument, [[Target]]: empty }.
|
||||
return new Completion({ Type: 'throw', Value: argument, Target: undefined });
|
||||
}
|
||||
|
||||
// 6.2.3.4 #sec-updateempty
|
||||
export function UpdateEmpty(completionRecord, value) {
|
||||
Assert(completionRecord instanceof Completion);
|
||||
// 1. Assert: If completionRecord.[[Type]] is either return or throw, then completionRecord.[[Value]] is not empty.
|
||||
Assert(!(completionRecord.Type === 'return' || completionRecord.Type === 'throw') || completionRecord.Value !== undefined);
|
||||
// 2. If completionRecord.[[Value]] is not empty, return Completion(completionRecord).
|
||||
if (completionRecord.Value !== undefined) {
|
||||
return Completion(completionRecord);
|
||||
}
|
||||
// 3. Return Completion { [[Type]]: completionRecord.[[Type]], [[Value]]: value, [[Target]]: completionRecord.[[Target]] }.
|
||||
return new Completion({ Type: completionRecord.Type, Value: value, Target: completionRecord.Target });
|
||||
}
|
||||
|
||||
// #sec-returnifabrupt
|
||||
export function ReturnIfAbrupt() {
|
||||
/* istanbul skip next */
|
||||
throw new TypeError('ReturnIfAbrupt requires build');
|
||||
}
|
||||
|
||||
// #sec-returnifabrupt-shorthands ? OperationName()
|
||||
export const Q = ReturnIfAbrupt;
|
||||
|
||||
// #sec-returnifabrupt-shorthands ! OperationName()
|
||||
export function X() {
|
||||
/* istanbul skip next */
|
||||
throw new TypeError('X() requires build');
|
||||
}
|
||||
|
||||
// 25.6.1.1.1 #sec-ifabruptrejectpromise
|
||||
export function IfAbruptRejectPromise() {
|
||||
/* istanbul skip next */
|
||||
throw new TypeError('IfAbruptRejectPromise requires build');
|
||||
}
|
||||
|
||||
export function EnsureCompletion(val) {
|
||||
if (val instanceof Completion) {
|
||||
return val;
|
||||
}
|
||||
return NormalCompletion(val);
|
||||
}
|
||||
|
||||
export function AwaitFulfilledFunctions([value]) {
|
||||
const F = surroundingAgent.activeFunctionObject;
|
||||
const asyncContext = F.AsyncContext;
|
||||
const prevContext = surroundingAgent.runningExecutionContext;
|
||||
// Suspend prevContext
|
||||
surroundingAgent.executionContextStack.push(asyncContext);
|
||||
resume(asyncContext, NormalCompletion(value));
|
||||
Assert(surroundingAgent.runningExecutionContext === prevContext);
|
||||
return Value.undefined;
|
||||
}
|
||||
|
||||
function AwaitRejectedFunctions([reason]) {
|
||||
const F = surroundingAgent.activeFunctionObject;
|
||||
const asyncContext = F.AsyncContext;
|
||||
const prevContext = surroundingAgent.runningExecutionContext;
|
||||
// Suspend prevContext
|
||||
surroundingAgent.executionContextStack.push(asyncContext);
|
||||
resume(asyncContext, ThrowCompletion(reason));
|
||||
Assert(surroundingAgent.runningExecutionContext === prevContext);
|
||||
return Value.undefined;
|
||||
}
|
||||
|
||||
export function* Await(value) {
|
||||
const asyncContext = surroundingAgent.runningExecutionContext;
|
||||
const promise = Q(PromiseResolve(surroundingAgent.intrinsic('%Promise%'), value));
|
||||
const stepsFulfilled = AwaitFulfilledFunctions;
|
||||
const onFulfilled = X(CreateBuiltinFunction(stepsFulfilled, ['AsyncContext']));
|
||||
X(SetFunctionLength(onFulfilled, new Value(1)));
|
||||
onFulfilled.AsyncContext = asyncContext;
|
||||
const stepsRejected = AwaitRejectedFunctions;
|
||||
const onRejected = X(CreateBuiltinFunction(stepsRejected, ['AsyncContext']));
|
||||
X(SetFunctionLength(onRejected, new Value(1)));
|
||||
onRejected.AsyncContext = asyncContext;
|
||||
X(PerformPromiseThen(promise, onFulfilled, onRejected));
|
||||
surroundingAgent.executionContextStack.pop(asyncContext);
|
||||
const completion = yield Value.undefined;
|
||||
return completion;
|
||||
}
|
||||
@@ -1,404 +0,0 @@
|
||||
import { Value } from './value.mjs';
|
||||
import {
|
||||
AbruptCompletion,
|
||||
EnsureCompletion,
|
||||
NormalCompletion,
|
||||
ThrowCompletion,
|
||||
Q, X,
|
||||
} from './completion.mjs';
|
||||
import {
|
||||
IsCallable,
|
||||
Call, Construct, Assert, GetModuleNamespace,
|
||||
PerformPromiseThen, CreateBuiltinFunction,
|
||||
GetActiveScriptOrModule,
|
||||
CleanupFinalizationRegistry,
|
||||
CreateArrayFromList,
|
||||
} from './abstract-ops/all.mjs';
|
||||
import { GlobalDeclarationInstantiation } from './runtime-semantics/all.mjs';
|
||||
import { Evaluate } from './evaluator.mjs';
|
||||
import { CyclicModuleRecord } from './modules.mjs';
|
||||
import { CallSite, unwind } from './helpers.mjs';
|
||||
import * as messages from './messages.mjs';
|
||||
|
||||
export const FEATURES = Object.freeze([
|
||||
{
|
||||
name: 'Top-Level Await',
|
||||
flag: 'top-level-await',
|
||||
url: 'https://github.com/tc39/proposal-top-level-await',
|
||||
},
|
||||
{
|
||||
name: 'Hashbang Grammar',
|
||||
flag: 'hashbang',
|
||||
url: 'https://github.com/tc39/proposal-hashbang',
|
||||
},
|
||||
{
|
||||
name: 'Numeric Separators',
|
||||
flag: 'numeric-separators',
|
||||
url: 'https://github.com/tc39/proposal-numeric-separator',
|
||||
},
|
||||
{
|
||||
name: 'RegExp Match Indices',
|
||||
flag: 'regexp-match-indices',
|
||||
url: 'https://github.com/tc39/proposal-regexp-match-indices',
|
||||
},
|
||||
{
|
||||
name: 'FinalizationRegistry.prototype.cleanupSome',
|
||||
flag: 'cleanup-some',
|
||||
url: 'https://github.com/tc39/proposal-cleanup-some',
|
||||
},
|
||||
{
|
||||
name: 'Arbitrary Module Namespace Names',
|
||||
flag: 'arbitrary-module-namespace-names',
|
||||
url: 'https://github.com/tc39/ecma262/pull/2154',
|
||||
},
|
||||
].map(Object.freeze));
|
||||
|
||||
let agentSignifier = 0;
|
||||
// #sec-agents
|
||||
export class Agent {
|
||||
constructor(options = {}) {
|
||||
// #table-agent-record
|
||||
const Signifier = agentSignifier;
|
||||
agentSignifier += 1;
|
||||
this.AgentRecord = {
|
||||
LittleEndian: Value.true,
|
||||
CanBlock: Value.true,
|
||||
Signifier,
|
||||
IsLockFree1: Value.true,
|
||||
IsLockFree2: Value.true,
|
||||
CandidateExecution: undefined,
|
||||
KeptAlive: new Set(),
|
||||
};
|
||||
|
||||
// #execution-context-stack
|
||||
this.executionContextStack = [];
|
||||
const stackPop = this.executionContextStack.pop;
|
||||
this.executionContextStack.pop = function pop(ctx) {
|
||||
if (!ctx.poppedForTailCall) {
|
||||
const popped = stackPop.call(this);
|
||||
Assert(popped === ctx);
|
||||
}
|
||||
};
|
||||
|
||||
// NON-SPEC
|
||||
this.jobQueue = [];
|
||||
this.hostDefinedOptions = {
|
||||
...options,
|
||||
features: FEATURES.reduce((acc, { flag }) => {
|
||||
if (options.features) {
|
||||
acc[flag] = options.features.includes(flag);
|
||||
} else {
|
||||
acc[flag] = false;
|
||||
}
|
||||
return acc;
|
||||
}, {}),
|
||||
};
|
||||
}
|
||||
|
||||
// #running-execution-context
|
||||
get runningExecutionContext() {
|
||||
return this.executionContextStack[this.executionContextStack.length - 1];
|
||||
}
|
||||
|
||||
// #current-realm
|
||||
get currentRealmRecord() {
|
||||
return this.runningExecutionContext.Realm;
|
||||
}
|
||||
|
||||
// #active-function-object
|
||||
get activeFunctionObject() {
|
||||
return this.runningExecutionContext.Function;
|
||||
}
|
||||
|
||||
// Get an intrinsic by name for the current realm
|
||||
intrinsic(name) {
|
||||
return this.currentRealmRecord.Intrinsics[name];
|
||||
}
|
||||
|
||||
// Generate a throw completion using message templates
|
||||
Throw(type, template, ...templateArgs) {
|
||||
if (type instanceof Value) {
|
||||
return ThrowCompletion(type);
|
||||
}
|
||||
const message = messages[template](...templateArgs);
|
||||
const cons = this.currentRealmRecord.Intrinsics[`%${type}%`];
|
||||
let error;
|
||||
if (type === 'AggregateError') {
|
||||
error = X(Construct(cons, [
|
||||
X(CreateArrayFromList([])),
|
||||
new Value(message),
|
||||
]));
|
||||
} else {
|
||||
error = X(Construct(cons, [new Value(message)]));
|
||||
}
|
||||
return ThrowCompletion(error);
|
||||
}
|
||||
|
||||
queueJob(queueName, job) {
|
||||
const callerContext = this.runningExecutionContext;
|
||||
const callerRealm = callerContext.Realm;
|
||||
const callerScriptOrModule = GetActiveScriptOrModule();
|
||||
const pending = {
|
||||
queueName,
|
||||
job,
|
||||
callerRealm,
|
||||
callerScriptOrModule,
|
||||
};
|
||||
this.jobQueue.push(pending);
|
||||
}
|
||||
|
||||
// NON-SPEC: Check if a feature is enabled in this agent.
|
||||
feature(name) {
|
||||
return this.hostDefinedOptions.features[name];
|
||||
}
|
||||
|
||||
// NON-SPEC
|
||||
mark(m) {
|
||||
this.AgentRecord.KeptAlive.forEach((v) => {
|
||||
m(v);
|
||||
});
|
||||
this.executionContextStack.forEach((e) => {
|
||||
m(e);
|
||||
});
|
||||
this.jobQueue.forEach((j) => {
|
||||
m(j.callerRealm);
|
||||
m(j.callerScriptOrModule);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export let surroundingAgent;
|
||||
export function setSurroundingAgent(a) {
|
||||
surroundingAgent = a;
|
||||
}
|
||||
|
||||
// #sec-execution-contexts
|
||||
export class ExecutionContext {
|
||||
constructor() {
|
||||
this.codeEvaluationState = undefined;
|
||||
this.Function = undefined;
|
||||
this.Realm = undefined;
|
||||
this.ScriptOrModule = undefined;
|
||||
this.VariableEnvironment = undefined;
|
||||
this.LexicalEnvironment = undefined;
|
||||
|
||||
// NON-SPEC
|
||||
this.callSite = new CallSite(this);
|
||||
this.promiseCapability = undefined;
|
||||
this.poppedForTailCall = false;
|
||||
}
|
||||
|
||||
copy() {
|
||||
const e = new ExecutionContext();
|
||||
e.codeEvaluationState = this.codeEvaluationState;
|
||||
e.Function = this.Function;
|
||||
e.Realm = this.Realm;
|
||||
e.ScriptOrModule = this.ScriptOrModule;
|
||||
e.VariableEnvironment = this.VariableEnvironment;
|
||||
e.LexicalEnvironment = this.LexicalEnvironment;
|
||||
|
||||
e.callSite = this.callSite.clone(e);
|
||||
e.promiseCapability = this.promiseCapability;
|
||||
return e;
|
||||
}
|
||||
|
||||
// NON-SPEC
|
||||
mark(m) {
|
||||
m(this.Function);
|
||||
m(this.Realm);
|
||||
m(this.ScriptOrModule);
|
||||
m(this.VariableEnvironment);
|
||||
m(this.LexicalEnvironment);
|
||||
m(this.promiseCapability);
|
||||
}
|
||||
}
|
||||
|
||||
// 15.1.10 #sec-runtime-semantics-scriptevaluation
|
||||
export function ScriptEvaluation(scriptRecord) {
|
||||
if (surroundingAgent.hostDefinedOptions.boost) {
|
||||
return surroundingAgent.hostDefinedOptions.boost.evaluateScript(scriptRecord);
|
||||
}
|
||||
|
||||
const globalEnv = scriptRecord.Realm.GlobalEnv;
|
||||
const scriptContext = new ExecutionContext();
|
||||
scriptContext.Function = Value.null;
|
||||
scriptContext.Realm = scriptRecord.Realm;
|
||||
scriptContext.ScriptOrModule = scriptRecord;
|
||||
scriptContext.VariableEnvironment = globalEnv;
|
||||
scriptContext.LexicalEnvironment = globalEnv;
|
||||
scriptContext.HostDefined = scriptRecord.HostDefined;
|
||||
// Suspend runningExecutionContext
|
||||
surroundingAgent.executionContextStack.push(scriptContext);
|
||||
const scriptBody = scriptRecord.ECMAScriptCode;
|
||||
let result = EnsureCompletion(GlobalDeclarationInstantiation(scriptBody, globalEnv));
|
||||
|
||||
if (result.Type === 'normal') {
|
||||
result = EnsureCompletion(unwind(Evaluate(scriptBody)));
|
||||
}
|
||||
|
||||
if (result.Type === 'normal' && !result.Value) {
|
||||
result = NormalCompletion(Value.undefined);
|
||||
}
|
||||
|
||||
// Suspend scriptCtx
|
||||
surroundingAgent.executionContextStack.pop(scriptContext);
|
||||
// Resume(surroundingAgent.runningExecutionContext);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// #sec-hostenqueuepromisejob
|
||||
export function HostEnqueuePromiseJob(job, _realm) {
|
||||
surroundingAgent.queueJob('PromiseJobs', job);
|
||||
}
|
||||
|
||||
// #sec-agentsignifier
|
||||
export function AgentSignifier() {
|
||||
// 1. Let AR be the Agent Record of the surrounding agent.
|
||||
const AR = surroundingAgent.AgentRecord;
|
||||
// 2. Return AR.[[Signifier]].
|
||||
return AR.Signifier;
|
||||
}
|
||||
|
||||
export function HostEnsureCanCompileStrings(callerRealm, calleeRealm) {
|
||||
if (surroundingAgent.hostDefinedOptions.ensureCanCompileStrings !== undefined) {
|
||||
Q(surroundingAgent.hostDefinedOptions.ensureCanCompileStrings(callerRealm, calleeRealm));
|
||||
}
|
||||
return NormalCompletion(undefined);
|
||||
}
|
||||
|
||||
export function HostPromiseRejectionTracker(promise, operation) {
|
||||
const realm = surroundingAgent.currentRealmRecord;
|
||||
if (realm && realm.HostDefined.promiseRejectionTracker) {
|
||||
X(realm.HostDefined.promiseRejectionTracker(promise, operation));
|
||||
}
|
||||
}
|
||||
|
||||
export function HostHasSourceTextAvailable(func) {
|
||||
if (surroundingAgent.hostDefinedOptions.hasSourceTextAvailable) {
|
||||
return X(surroundingAgent.hostDefinedOptions.hasSourceTextAvailable(func));
|
||||
}
|
||||
return Value.true;
|
||||
}
|
||||
|
||||
export function HostResolveImportedModule(referencingScriptOrModule, specifier) {
|
||||
const realm = referencingScriptOrModule.Realm || surroundingAgent.currentRealmRecord;
|
||||
if (realm.HostDefined.resolveImportedModule) {
|
||||
specifier = specifier.stringValue();
|
||||
if (referencingScriptOrModule !== Value.null) {
|
||||
if (!referencingScriptOrModule.HostDefined.moduleMap) {
|
||||
referencingScriptOrModule.HostDefined.moduleMap = new Map();
|
||||
}
|
||||
if (referencingScriptOrModule.HostDefined.moduleMap.has(specifier)) {
|
||||
return referencingScriptOrModule.HostDefined.moduleMap.get(specifier);
|
||||
}
|
||||
}
|
||||
const resolved = Q(realm.HostDefined.resolveImportedModule(referencingScriptOrModule, specifier));
|
||||
if (referencingScriptOrModule !== Value.null) {
|
||||
referencingScriptOrModule.HostDefined.moduleMap.set(specifier, resolved);
|
||||
}
|
||||
return resolved;
|
||||
}
|
||||
return surroundingAgent.Throw('Error', 'CouldNotResolveModule', specifier);
|
||||
}
|
||||
|
||||
function FinishDynamicImport(referencingScriptOrModule, specifier, promiseCapability, completion) {
|
||||
// 1. If completion is an abrupt completion, then perform ! Call(promiseCapability.[[Reject]], undefined, « completion.[[Value]] »).
|
||||
if (completion instanceof AbruptCompletion) {
|
||||
X(Call(promiseCapability.Reject, Value.undefined, [completion.Value]));
|
||||
} else { // 2. Else,
|
||||
// a. Assert: completion is a normal completion and completion.[[Value]] is undefined.
|
||||
Assert(completion instanceof NormalCompletion);
|
||||
// b. Let moduleRecord be ! HostResolveImportedModule(referencingScriptOrModule, specifier).
|
||||
const moduleRecord = X(HostResolveImportedModule(referencingScriptOrModule, specifier));
|
||||
// c. Assert: Evaluate has already been invoked on moduleRecord and successfully completed.
|
||||
// d. Let namespace be GetModuleNamespace(moduleRecord).
|
||||
const namespace = EnsureCompletion(GetModuleNamespace(moduleRecord));
|
||||
// e. If namespace is an abrupt completion, perform ! Call(promiseCapability.[[Reject]], undefined, « namespace.[[Value]] »).
|
||||
if (namespace instanceof AbruptCompletion) {
|
||||
X(Call(promiseCapability.Reject, Value.undefined, [namespace.Value]));
|
||||
} else {
|
||||
// f. Else, perform ! Call(promiseCapability.[[Resolve]], undefined, « namespace.[[Value]] »).
|
||||
X(Call(promiseCapability.Resolve, Value.undefined, [namespace.Value]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function HostImportModuleDynamically(referencingScriptOrModule, specifier, promiseCapability) {
|
||||
surroundingAgent.queueJob('ImportModuleDynamicallyJobs', () => {
|
||||
const finish = (c) => FinishDynamicImport(referencingScriptOrModule, specifier, promiseCapability, c);
|
||||
const c = (() => {
|
||||
const module = Q(HostResolveImportedModule(referencingScriptOrModule, specifier));
|
||||
Q(module.Link());
|
||||
const maybePromise = Q(module.Evaluate());
|
||||
if (module instanceof CyclicModuleRecord) {
|
||||
const onFulfilled = CreateBuiltinFunction(([v = Value.undefined]) => {
|
||||
finish(NormalCompletion(v));
|
||||
return Value.undefined;
|
||||
}, []);
|
||||
const onRejected = CreateBuiltinFunction(([r = Value.undefined]) => {
|
||||
finish(ThrowCompletion(r));
|
||||
return Value.undefined;
|
||||
}, []);
|
||||
PerformPromiseThen(maybePromise, onFulfilled, onRejected);
|
||||
} else {
|
||||
finish(NormalCompletion(undefined));
|
||||
}
|
||||
})();
|
||||
if (c instanceof AbruptCompletion) {
|
||||
finish(c);
|
||||
}
|
||||
});
|
||||
return NormalCompletion(Value.undefined);
|
||||
}
|
||||
|
||||
// #sec-hostgetimportmetaproperties
|
||||
export function HostGetImportMetaProperties(moduleRecord) {
|
||||
const realm = surroundingAgent.currentRealmRecord;
|
||||
if (realm.HostDefined.getImportMetaProperties) {
|
||||
return X(realm.HostDefined.getImportMetaProperties(moduleRecord.HostDefined.public));
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
// #sec-hostfinalizeimportmeta
|
||||
export function HostFinalizeImportMeta(importMeta, moduleRecord) {
|
||||
const realm = surroundingAgent.currentRealmRecord;
|
||||
if (realm.HostDefined.finalizeImportMeta) {
|
||||
return X(realm.HostDefined.finalizeImportMeta(importMeta, moduleRecord.HostDefined.public));
|
||||
}
|
||||
return Value.undefined;
|
||||
}
|
||||
|
||||
// #sec-host-cleanup-finalization-registry
|
||||
const scheduledForCleanup = new Set();
|
||||
export function HostEnqueueFinalizationRegistryCleanupJob(fg) {
|
||||
if (surroundingAgent.hostDefinedOptions.cleanupFinalizationRegistry !== undefined) {
|
||||
Q(surroundingAgent.hostDefinedOptions.cleanupFinalizationRegistry(fg));
|
||||
} else {
|
||||
if (!scheduledForCleanup.has(fg)) {
|
||||
scheduledForCleanup.add(fg);
|
||||
surroundingAgent.queueJob('FinalizationCleanup', () => {
|
||||
scheduledForCleanup.delete(fg);
|
||||
CleanupFinalizationRegistry(fg);
|
||||
});
|
||||
}
|
||||
}
|
||||
return NormalCompletion(undefined);
|
||||
}
|
||||
|
||||
// #sec-hostmakejobcallback
|
||||
export function HostMakeJobCallback(callback) {
|
||||
// 1. Assert: IsCallable(callback) is true.
|
||||
Assert(IsCallable(callback) === Value.true);
|
||||
// 2. Return the JobCallback Record { [[Callback]]: callback, [[HostDefined]]: empty }.
|
||||
return { Callback: callback, HostDefined: undefined };
|
||||
}
|
||||
|
||||
// #sec-hostcalljobcallback
|
||||
export function HostCallJobCallback(jobCallback, V, argumentsList) {
|
||||
// 1. Assert: IsCallable(jobCallback.[[Callback]]) is true.
|
||||
Assert(IsCallable(jobCallback.Callback) === Value.true);
|
||||
// 1. Return ? Call(jobCallback.[[Callback]], V, argumentsList).
|
||||
return Q(Call(jobCallback.Callback, V, argumentsList));
|
||||
}
|
||||
@@ -1,982 +0,0 @@
|
||||
import { AbstractModuleRecord } from './modules.mjs';
|
||||
import {
|
||||
Descriptor,
|
||||
Reference,
|
||||
Type,
|
||||
Value,
|
||||
wellKnownSymbols,
|
||||
} from './value.mjs';
|
||||
import { surroundingAgent } from './engine.mjs';
|
||||
import {
|
||||
Assert,
|
||||
DefinePropertyOrThrow,
|
||||
Get,
|
||||
HasOwnProperty,
|
||||
HasProperty,
|
||||
IsDataDescriptor,
|
||||
IsExtensible,
|
||||
IsPropertyKey,
|
||||
Set,
|
||||
ToBoolean,
|
||||
isECMAScriptFunctionObject,
|
||||
} from './abstract-ops/all.mjs';
|
||||
import { NormalCompletion, Q, X } from './completion.mjs';
|
||||
import { ValueMap } from './helpers.mjs';
|
||||
|
||||
// #sec-environment-records
|
||||
export class EnvironmentRecord {
|
||||
constructor() {
|
||||
this.OuterEnv = undefined;
|
||||
}
|
||||
|
||||
// NON-SPEC
|
||||
mark(m) {
|
||||
m(this.OuterEnv);
|
||||
}
|
||||
}
|
||||
|
||||
// #sec-declarative-environment-records
|
||||
export class DeclarativeEnvironmentRecord extends EnvironmentRecord {
|
||||
constructor() {
|
||||
super();
|
||||
this.bindings = new ValueMap();
|
||||
}
|
||||
|
||||
// #sec-declarative-environment-records-hasbinding-n
|
||||
HasBinding(N) {
|
||||
// 1. Let envRec be the declarative Environment Record for which the method was invoked.
|
||||
const envRec = this;
|
||||
// 2. If envRec has a binding for the name that is the value of N, return true.
|
||||
if (envRec.bindings.has(N)) {
|
||||
return Value.true;
|
||||
}
|
||||
// 3. Return false.
|
||||
return Value.false;
|
||||
}
|
||||
|
||||
// #sec-declarative-environment-records-createmutablebinding-n-d
|
||||
CreateMutableBinding(N, D) {
|
||||
// 1. Let envRec be the declarative Environment Record for which the method was invoked.
|
||||
const envRec = this;
|
||||
// 2. Assert: envRec does not already have a binding for N.
|
||||
Assert(!envRec.bindings.has(N));
|
||||
// 3. Create a mutable binding in envRec for N and record that it is uninitialized. If D
|
||||
// is true, record that the newly created binding may be delted by a subsequent
|
||||
// DeleteBinding call.
|
||||
this.bindings.set(N, {
|
||||
indirect: false,
|
||||
initialized: false,
|
||||
mutable: true,
|
||||
strict: undefined,
|
||||
deletable: D === Value.true,
|
||||
value: undefined,
|
||||
mark(m) {
|
||||
m(this.value);
|
||||
},
|
||||
});
|
||||
// 4. Return NormalCompletion(empty).
|
||||
return NormalCompletion(undefined);
|
||||
}
|
||||
|
||||
// #sec-declarative-environment-records-createimmutablebinding-n-s
|
||||
CreateImmutableBinding(N, S) {
|
||||
// 1. Let envRec be the declarative Environment Record for which the method was invoked.
|
||||
const envRec = this;
|
||||
// 2. Assert: envRec does not already have a binding for N.
|
||||
Assert(!envRec.bindings.has(N));
|
||||
// 3. Create an immutable binding in envRec for N and record that it is uninitialized. If
|
||||
// S is true, record that the newly created binding is a strict binding.
|
||||
this.bindings.set(N, {
|
||||
indirect: false,
|
||||
initialized: false,
|
||||
mutable: false,
|
||||
strict: S === Value.true,
|
||||
deletable: false,
|
||||
value: undefined,
|
||||
mark(m) {
|
||||
m(this.value);
|
||||
},
|
||||
});
|
||||
// 4. Return NormalCompletion(empty).
|
||||
return NormalCompletion(undefined);
|
||||
}
|
||||
|
||||
// #sec-declarative-environment-records-initializebinding-n-v
|
||||
InitializeBinding(N, V) {
|
||||
// 1. Let envRec be the declarative Environment Record for which the method was invoked.
|
||||
const envRec = this;
|
||||
// 2. Assert: envRec must have an uninitialized binding for N.
|
||||
const binding = envRec.bindings.get(N);
|
||||
Assert(binding !== undefined && binding.initialized === false);
|
||||
// 3. Set the bound value for N in envRec to V.
|
||||
binding.value = V;
|
||||
// 4. Record that the binding for N in envRec has been initialized.
|
||||
binding.initialized = true;
|
||||
// 5. Return NormalCompletion(empty).
|
||||
return NormalCompletion(undefined);
|
||||
}
|
||||
|
||||
// #sec-declarative-environment-records-setmutablebinding-n-v-s
|
||||
SetMutableBinding(N, V, S) {
|
||||
Assert(IsPropertyKey(N));
|
||||
// 1. Let envRec be the declarative Environment Record for which the method was invoked.
|
||||
const envRec = this;
|
||||
// 2. If envRec does not have a binding for N, then
|
||||
if (!envRec.bindings.has(N)) {
|
||||
// a. If S is true, throw a ReferenceError exception.
|
||||
if (S === Value.true) {
|
||||
return surroundingAgent.Throw('ReferenceError', 'NotDefined', N);
|
||||
}
|
||||
// b. Perform envRec.CreateMutableBinding(N, true).
|
||||
envRec.CreateMutableBinding(N, true);
|
||||
// c. Perform envRec.InitializeBinding(N, V).
|
||||
envRec.InitializeBinding(N, V);
|
||||
// d. Return NormalCompletion(empty).
|
||||
return NormalCompletion(undefined);
|
||||
}
|
||||
const binding = this.bindings.get(N);
|
||||
// 3. If the binding for N in envRec is a strict binding, set S to true.
|
||||
if (binding.strict === true) {
|
||||
S = Value.true;
|
||||
}
|
||||
// 4. If the binding for N in envRec has not yet been initialized, throw a ReferenceError exception.
|
||||
if (binding.initialized === false) {
|
||||
return surroundingAgent.Throw('ReferenceError', 'NotInitialized', N);
|
||||
}
|
||||
// 5. Else if the binding for N in envRec is a mutable binding, change its bound value to V.
|
||||
if (binding.mutable === true) {
|
||||
binding.value = V;
|
||||
} else {
|
||||
// a. Assert: This is an attempt to change the value of an immutable binding.
|
||||
// b. If S is true, throw a TypeError exception.
|
||||
if (S === Value.true) {
|
||||
return surroundingAgent.Throw('TypeError', 'AssignmentToConstant', N);
|
||||
}
|
||||
}
|
||||
// 7. Return NormalCompletion(empty).
|
||||
return NormalCompletion(undefined);
|
||||
}
|
||||
|
||||
// #sec-declarative-environment-records-getbindingvalue-n-s
|
||||
GetBindingValue(N) {
|
||||
// 1. Let envRec be the declarative Environment Record for which the method was invoked.
|
||||
const envRec = this;
|
||||
// 2. Assert: envRec has a binding for N.
|
||||
const binding = envRec.bindings.get(N);
|
||||
Assert(binding !== undefined);
|
||||
// 3. If the binding for N in envRec is an uninitialized binding, throw a ReferenceError exception.
|
||||
if (binding.initialized === false) {
|
||||
return surroundingAgent.Throw('ReferenceError', 'NotInitialized', N);
|
||||
}
|
||||
// 4. Return the value currently bound to N in envRec.
|
||||
return binding.value;
|
||||
}
|
||||
|
||||
// #sec-declarative-environment-records-deletebinding-n
|
||||
DeleteBinding(N) {
|
||||
// 1. Let envRec be the declarative Environment Record for which the method was invoked.
|
||||
const envRec = this;
|
||||
// 2. Assert: envRec has a binding for the name that is the value of N.
|
||||
const binding = envRec.bindings.get(N);
|
||||
Assert(binding !== undefined);
|
||||
// 3. If the binding for N in envRec cannot be deleted, return false.
|
||||
if (binding.deletable === false) {
|
||||
return Value.false;
|
||||
}
|
||||
// 4. Remove the binding for N from envRec.
|
||||
envRec.bindings.delete(N);
|
||||
// 5. Return true.
|
||||
return Value.true;
|
||||
}
|
||||
|
||||
// #sec-declarative-environment-records-hasthisbinding
|
||||
HasThisBinding() {
|
||||
// 1. Return false.
|
||||
return Value.false;
|
||||
}
|
||||
|
||||
// #sec-declarative-environment-records-hassuperbinding
|
||||
HasSuperBinding() {
|
||||
// 1. Return false.
|
||||
return Value.false;
|
||||
}
|
||||
|
||||
// #sec-declarative-environment-records-withbaseobject
|
||||
WithBaseObject() {
|
||||
// 1. Return undefined.
|
||||
return Value.undefined;
|
||||
}
|
||||
|
||||
// NON-SPEC
|
||||
mark(m) {
|
||||
m(this.bindings);
|
||||
}
|
||||
}
|
||||
|
||||
// #sec-object-environment-records
|
||||
export class ObjectEnvironmentRecord extends EnvironmentRecord {
|
||||
constructor(BindingObject) {
|
||||
super();
|
||||
this.bindingObject = BindingObject;
|
||||
this.withEnvironment = false;
|
||||
}
|
||||
|
||||
// #sec-object-environment-records-hasbinding-n
|
||||
HasBinding(N) {
|
||||
// 1. Let envRec be the object Environment Record for which the method was invoked.
|
||||
const envRec = this;
|
||||
// 2. Let bindings be the binding object for envRec.
|
||||
const bindings = envRec.bindingObject;
|
||||
// 3. Let foundBinding be ? HasProperty(bindings, N).
|
||||
const foundBinding = Q(HasProperty(bindings, N));
|
||||
// 4. If foundBinding is false, return false.
|
||||
if (foundBinding === Value.false) {
|
||||
return Value.false;
|
||||
}
|
||||
// 5. If the withEnvironment flag of envRec i s false, return true.
|
||||
if (envRec.withEnvironment === false) {
|
||||
return Value.true;
|
||||
}
|
||||
// 6. Let unscopables be ? Get(bindings, @@unscopables).
|
||||
const unscopables = Q(Get(bindings, wellKnownSymbols.unscopables));
|
||||
// 7. If Type(unscopables) is Object, then
|
||||
if (Type(unscopables) === 'Object') {
|
||||
// a. Let blocked be ! ToBoolean(? Get(unscopables, N)).
|
||||
const blocked = X(ToBoolean(Q(Get(unscopables, N))));
|
||||
// b. If blocked is true, return false.
|
||||
if (blocked === Value.true) {
|
||||
return Value.false;
|
||||
}
|
||||
}
|
||||
// 8. Return true.
|
||||
return Value.true;
|
||||
}
|
||||
|
||||
// #sec-object-environment-records-createmutablebinding-n-d
|
||||
CreateMutableBinding(N, D) {
|
||||
// 1. Let envRec be the object Environment Record for which the method was invoked.
|
||||
const envRec = this;
|
||||
// 2. Let envRec be the object Environment Record for which the method was invoked.
|
||||
const bindings = envRec.bindingObject;
|
||||
// 3. Return ? DefinePropertyOrThrow(bindings, N, PropertyDescriptor { [[Value]]: undefined, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: D }).
|
||||
return Q(DefinePropertyOrThrow(bindings, N, Descriptor({
|
||||
Value: Value.undefined,
|
||||
Writable: Value.true,
|
||||
Enumerable: Value.true,
|
||||
Configurable: D,
|
||||
})));
|
||||
}
|
||||
|
||||
// #sec-object-environment-records-createimmutablebinding-n-s
|
||||
CreateImmutableBinding(_N, _S) {
|
||||
Assert(false, 'CreateImmutableBinding called on an Object Environment Record');
|
||||
}
|
||||
|
||||
// #sec-object-environment-records-initializebinding-n-v
|
||||
InitializeBinding(N, V) {
|
||||
// 1. Let envRec be the object Environment Record for which the method was invoked.
|
||||
const envRec = this;
|
||||
// 2. Assert: envRec must have an uninitialized binding for N.
|
||||
// 3. Record that the binding for N in envRec has been initialized.
|
||||
// 4. Return ? envRec.SetMutableBinding(N, V, false).
|
||||
return Q(envRec.SetMutableBinding(N, V, Value.false));
|
||||
}
|
||||
|
||||
// #sec-object-environment-records-setmutablebinding-n-v-s
|
||||
SetMutableBinding(N, V, S) {
|
||||
// 1. Let envRec be the object Environment Record for which the method was invoked.
|
||||
const envRec = this;
|
||||
// 2. Let bindings be the binding object for envRec.
|
||||
const bindings = envRec.bindingObject;
|
||||
// 3. Let stillExists be ? HasProperty(bindings, N).
|
||||
const stillExists = Q(HasProperty(bindings, N));
|
||||
// 4. If stillExists is false and S is true, throw a ReferenceError exception.
|
||||
if (stillExists === Value.false && S === Value.true) {
|
||||
return surroundingAgent.Throw('ReferenceError', 'NotDefined', N);
|
||||
}
|
||||
// 5. Return ? Set(bindings, N, V, S).
|
||||
return Q(Set(bindings, N, V, S));
|
||||
}
|
||||
|
||||
// #sec-object-environment-records-getbindingvalue-n-s
|
||||
GetBindingValue(N, S) {
|
||||
// 1. Let envRec be the object Environment Record for which the method was invoked.
|
||||
const envRec = this;
|
||||
// 2. Let bindings be the binding object for envRec.
|
||||
const bindings = envRec.bindingObject;
|
||||
// 3. Let value be ? HasProperty(bindings, N).
|
||||
const value = Q(HasProperty(bindings, N));
|
||||
// 4. If value is false, then
|
||||
if (value === Value.false) {
|
||||
// a. If S is false, return the value undefined; otherwise throw a ReferenceError exception.
|
||||
if (S === Value.false) {
|
||||
return Value.undefined;
|
||||
} else {
|
||||
return surroundingAgent.Throw('ReferenceError', 'NotDefined', N);
|
||||
}
|
||||
}
|
||||
// 5. Return ? Get(bindings, N).
|
||||
return Q(Get(bindings, N));
|
||||
}
|
||||
|
||||
// #sec-object-environment-records-deletebinding-n
|
||||
DeleteBinding(N) {
|
||||
// 1. Let envRec be the object Environment Record for which the method was invoked.
|
||||
const envRec = this;
|
||||
// 2. Let bindings be the binding object for envRec.
|
||||
const bindings = envRec.bindingObject;
|
||||
// 3. Return ? bindings.[[Delete]](N).
|
||||
return Q(bindings.Delete(N));
|
||||
}
|
||||
|
||||
// #sec-object-environment-records-hasthisbinding
|
||||
HasThisBinding() {
|
||||
// 1. Return false.
|
||||
return Value.false;
|
||||
}
|
||||
|
||||
// #sec-object-environment-records-hassuperbinding
|
||||
HasSuperBinding() {
|
||||
// 1. Return falase.
|
||||
return Value.false;
|
||||
}
|
||||
|
||||
// #sec-object-environment-records-withbaseobject
|
||||
WithBaseObject() {
|
||||
// 1. Let envRec be the object Environment Record for which the method was invoked.
|
||||
const envRec = this;
|
||||
// 2. If the withEnvironment flag of envRec is true, return the binding object for envRec.
|
||||
if (envRec.withEnvironment === true) {
|
||||
return envRec.bindingObject;
|
||||
}
|
||||
// 3. Otherwise, return undefined.
|
||||
return Value.undefined;
|
||||
}
|
||||
|
||||
// NON-SPEC
|
||||
mark(m) {
|
||||
m(this.bindingObject);
|
||||
}
|
||||
}
|
||||
|
||||
// #sec-function-environment-records
|
||||
export class FunctionEnvironmentRecord extends DeclarativeEnvironmentRecord {
|
||||
constructor() {
|
||||
super();
|
||||
this.ThisValue = undefined;
|
||||
this.ThisBindingValue = undefined;
|
||||
this.FunctionObject = undefined;
|
||||
this.HomeObject = Value.undefined;
|
||||
this.NewTarget = undefined;
|
||||
}
|
||||
|
||||
// #sec-bindthisvalue
|
||||
BindThisValue(V) {
|
||||
// 1. Let envRec be the function Environment Record for which the method was invoked.
|
||||
const envRec = this;
|
||||
// 2. Assert: envRec.[[ThisBindingStatus]] is not lexical.
|
||||
Assert(envRec.ThisBindingStatus !== 'lexical');
|
||||
// 3. If envRec.[[ThisBindingStatus]] is initialized, throw a ReferenceError exception.
|
||||
if (envRec.ThisBindingStatus === 'initialized') {
|
||||
return surroundingAgent.Throw('ReferenceError', 'InvalidThis');
|
||||
}
|
||||
// 4. Set envRec.[[ThisValue]] to V.
|
||||
envRec.ThisValue = V;
|
||||
// 5. Set envRec.[[ThisBindingStatus]] to initialized.
|
||||
envRec.ThisBindingStatus = 'initialized';
|
||||
// 6. Return V.
|
||||
return V;
|
||||
}
|
||||
|
||||
// #sec-function-environment-records-hasthisbinding
|
||||
HasThisBinding() {
|
||||
// 1. Let envRec be the function Environment Record for which the method was invoked.
|
||||
const envRec = this;
|
||||
// 2. If envRec.[[ThisBindingStatus]] is lexical, return false; otherwise, return true.
|
||||
if (envRec.ThisBindingStatus === 'lexical') {
|
||||
return Value.false;
|
||||
} else {
|
||||
return Value.true;
|
||||
}
|
||||
}
|
||||
|
||||
// #sec-function-environment-records-hassuperbinding
|
||||
HasSuperBinding() {
|
||||
// 1. Let envRec be the function Environment Record for which the method was invoked.
|
||||
const envRec = this;
|
||||
// 2. If envRec.[[ThisBindingStatus]] is lexical, return false.
|
||||
if (envRec.ThisBindingStatus === 'lexical') {
|
||||
return Value.false;
|
||||
}
|
||||
// 3. If envRec.[[HomeObject]] has the value undefined, return false; otherwise, return true.
|
||||
if (Type(envRec.HomeObject) === 'Undefined') {
|
||||
return Value.false;
|
||||
} else {
|
||||
return Value.true;
|
||||
}
|
||||
}
|
||||
|
||||
// #sec-function-environment-records-getthisbinding
|
||||
GetThisBinding() {
|
||||
// 1. Let envRec be the function Environment Record for which the method was invoked.
|
||||
const envRec = this;
|
||||
// 2. Assert: envRec.[[ThisBindingStatus]] is not lexical.
|
||||
Assert(envRec.ThisBindingStatus !== 'lexical');
|
||||
// 3. If envRec.[[ThisBindingStatus]] is uninitialized, throw a ReferenceError exception.
|
||||
if (envRec.ThisBindingStatus === 'uninitialized') {
|
||||
return surroundingAgent.Throw('ReferenceError', 'InvalidThis');
|
||||
}
|
||||
// 4. Return envRec.[[ThisValue]].
|
||||
return envRec.ThisValue;
|
||||
}
|
||||
|
||||
// #sec-getsuperbase
|
||||
GetSuperBase() {
|
||||
// 1. Let envRec be the function Environment Record for which the method was invoked.
|
||||
const envRec = this;
|
||||
// 2. Let home be envRec.[[HomeObject]].
|
||||
const home = envRec.HomeObject;
|
||||
// 3. If home has the value undefined, return undefined.
|
||||
if (Type(home) === 'Undefined') {
|
||||
return Value.undefined;
|
||||
}
|
||||
// 4. Assert: Type(home) is Object.
|
||||
Assert(Type(home) === 'Object');
|
||||
// 5. Return ? home.[[GetPrototypeOf]]().
|
||||
return Q(home.GetPrototypeOf());
|
||||
}
|
||||
|
||||
mark(m) {
|
||||
super.mark(m);
|
||||
m(this.ThisValue);
|
||||
m(this.ThisBindingValue);
|
||||
m(this.FunctionObject);
|
||||
m(this.HomeObject);
|
||||
m(this.NewTarget);
|
||||
}
|
||||
}
|
||||
|
||||
// #sec-global-environment-records
|
||||
export class GlobalEnvironmentRecord extends EnvironmentRecord {
|
||||
constructor() {
|
||||
super();
|
||||
this.ObjectRecord = undefined;
|
||||
this.GlobalThisValue = undefined;
|
||||
this.DeclarativeRecord = undefined;
|
||||
this.VarNames = undefined;
|
||||
}
|
||||
|
||||
// #sec-global-environment-records-hasbinding-n
|
||||
HasBinding(N) {
|
||||
// 1. Let envRec be the global Environment Record for which the method was invoked.
|
||||
const envRec = this;
|
||||
// 2. Let DclRec be envRec.[[DeclarativeRecord]].
|
||||
const DclRec = envRec.DeclarativeRecord;
|
||||
// 3. If DclRec.HasBinding(N) is true, return true.
|
||||
if (DclRec.HasBinding(N) === Value.true) {
|
||||
return Value.true;
|
||||
}
|
||||
// 4. If DclRec.HasBinding(N) is true, return true.
|
||||
const ObjRec = envRec.ObjectRecord;
|
||||
// 5. Let ObjRec be envRec.[[ObjectRecord]].
|
||||
return ObjRec.HasBinding(N);
|
||||
}
|
||||
|
||||
// #sec-global-environment-records-createmutablebinding-n-d
|
||||
CreateMutableBinding(N, D) {
|
||||
// 1. Let envRec be the global Environment Record for which the method was invoked.
|
||||
const envRec = this;
|
||||
// 2. Let DclRec be envRec.[[DeclarativeRecord]].
|
||||
const DclRec = envRec.DeclarativeRecord;
|
||||
// 3. If DclRec.HasBinding(N) is true, throw a TypeError exception.
|
||||
if (DclRec.HasBinding(N) === Value.true) {
|
||||
return surroundingAgent.Throw('TypeError', 'AlreadyDeclared', N);
|
||||
}
|
||||
// 4. Return DclRec.CreateMutableBinding(N, D).
|
||||
return DclRec.CreateMutableBinding(N, D);
|
||||
}
|
||||
|
||||
// #sec-global-environment-records-createimmutablebinding-n-s
|
||||
CreateImmutableBinding(N, S) {
|
||||
// 1. Let envRec be the global Environment Record for which the method was invoked.
|
||||
const envRec = this;
|
||||
// 2. Let DclRec be envRec.[[DeclarativeRecord]].
|
||||
const DclRec = envRec.DeclarativeRecord;
|
||||
// 3. If DclRec.HasBinding(N) is true, throw a TypeError exception.
|
||||
if (DclRec.HasBinding(N) === Value.true) {
|
||||
return surroundingAgent.Throw('TypeError', 'AlreadyDeclared', N);
|
||||
}
|
||||
// Return DclRec.CreateImmutableBinding(N, S).
|
||||
return DclRec.CreateImmutableBinding(N, S);
|
||||
}
|
||||
|
||||
// #sec-global-environment-records-initializebinding-n-v
|
||||
InitializeBinding(N, V) {
|
||||
// 1. Let envRec be the global Environment Record for which the method was invoked.
|
||||
const envRec = this;
|
||||
// 2. Let DclRec be envRec.[[DeclarativeRecord]].
|
||||
const DclRec = envRec.DeclarativeRecord;
|
||||
// 3. If DclRec.HasBinding(N) is true, then
|
||||
if (DclRec.HasBinding(N) === Value.true) {
|
||||
// a. Return DclRec.InitializeBinding(N, V).
|
||||
return DclRec.InitializeBinding(N, V);
|
||||
}
|
||||
// 4. Assert: If the binding exists, it must be in the object Environment Record.
|
||||
// 5. Let ObjRec be envRec.[[ObjectRecord]].
|
||||
const ObjRec = envRec.ObjectRecord;
|
||||
// 6. Return ? ObjRec.InitializeBinding(N, V).
|
||||
return ObjRec.InitializeBinding(N, V);
|
||||
}
|
||||
|
||||
// #sec-global-environment-records-setmutablebinding-n-v-s
|
||||
SetMutableBinding(N, V, S) {
|
||||
// 1. Let envRec be the global Environment Record for which the method was invoked.
|
||||
const envRec = this;
|
||||
// 2. Let DclRec be envRec.[[DeclarativeRecord]].
|
||||
const DclRec = envRec.DeclarativeRecord;
|
||||
// 3. If DclRec.HasBinding(N) is true, then
|
||||
if (DclRec.HasBinding(N) === Value.true) {
|
||||
// a. Return DclRec.SetMutableBinding(N, V, S).
|
||||
return DclRec.SetMutableBinding(N, V, S);
|
||||
}
|
||||
// 4. Let ObjRec be envRec.[[ObjectRecord]].
|
||||
const ObjRec = envRec.ObjectRecord;
|
||||
// 5. Return ? ObjRec.SetMutableBinding(N, V, S).
|
||||
return Q(ObjRec.SetMutableBinding(N, V, S));
|
||||
}
|
||||
|
||||
// #sec-global-environment-records-getbindingvalue-n-s
|
||||
GetBindingValue(N, S) {
|
||||
// 1. Let envRec be the global Environment Record for which the method was invoked.
|
||||
const envRec = this;
|
||||
// 2. Let DclRec be envRec.[[DeclarativeRecord]].
|
||||
const DclRec = envRec.DeclarativeRecord;
|
||||
// 3. If DclRec.HasBinding(N) is true, then
|
||||
if (DclRec.HasBinding(N) === Value.true) {
|
||||
// a. Return DclRec.GetBindingValue(N, S).
|
||||
return DclRec.GetBindingValue(N, S);
|
||||
}
|
||||
// 4. Let ObjRec be envRec.[[ObjectRecord]].
|
||||
const ObjRec = envRec.ObjectRecord;
|
||||
// 5. Return ? ObjRec.GetBindingValue(N, S).
|
||||
return Q(ObjRec.GetBindingValue(N, S));
|
||||
}
|
||||
|
||||
// #sec-global-environment-records-deletebinding-n
|
||||
DeleteBinding(N) {
|
||||
// 1. Let envRec be the global Environment Record for which the method was invoked.
|
||||
const envRec = this;
|
||||
// 2. Let DclRec be envRec.[[DeclarativeRecord]].
|
||||
const DclRec = this.DeclarativeRecord;
|
||||
// 3. Let DclRec be envRec.[[DeclarativeRecord]].
|
||||
if (DclRec.HasBinding(N) === Value.true) {
|
||||
// a. Return DclRec.DeleteBinding(N).
|
||||
return Q(DclRec.DeleteBinding(N));
|
||||
}
|
||||
// 4. Let ObjRec be envRec.[[ObjectRecord]].
|
||||
const ObjRec = envRec.ObjectRecord;
|
||||
// 5. Let globalObject be the binding object for ObjRec.
|
||||
const globalObject = ObjRec.bindingObject;
|
||||
// 6. Let existingProp be ? HasOwnProperty(globalObject, N).
|
||||
const existingProp = Q(HasOwnProperty(globalObject, N));
|
||||
// 7. If existingProp is true, then
|
||||
if (existingProp === Value.true) {
|
||||
// a. Let status be ? ObjRec.DeleteBinding(N).
|
||||
const status = Q(ObjRec.DeleteBinding(N));
|
||||
// b. If status is true, then
|
||||
if (status === Value.true) {
|
||||
// i. Let varNames be envRec.[[VarNames]].
|
||||
const varNames = envRec.VarNames;
|
||||
// ii. If N is an element of varNames, remove that element from the varNames.
|
||||
if (varNames.includes(N)) {
|
||||
varNames.splice(varNames.indexOf(N), 1);
|
||||
}
|
||||
}
|
||||
// c. Return status.
|
||||
return status;
|
||||
}
|
||||
// 8. Return true.
|
||||
return Value.true;
|
||||
}
|
||||
|
||||
// #sec-global-environment-records-hasthisbinding
|
||||
HasThisBinding() {
|
||||
// Return true.
|
||||
return Value.true;
|
||||
}
|
||||
|
||||
// #sec-global-environment-records-hassuperbinding
|
||||
HasSuperBinding() {
|
||||
// 1. Return false.
|
||||
return Value.false;
|
||||
}
|
||||
|
||||
// #sec-global-environment-records-withbaseobject
|
||||
WithBaseObject() {
|
||||
// 1. Return undefined.
|
||||
return Value.undefined;
|
||||
}
|
||||
|
||||
// #sec-global-environment-records-getthisbinding
|
||||
GetThisBinding() {
|
||||
// 1. Let envRec be the global Environment Record for which the method was invoked.
|
||||
const envRec = this;
|
||||
// 2. Return envRec.[[GlobalThisValue]].
|
||||
return envRec.GlobalThisValue;
|
||||
}
|
||||
|
||||
// #sec-hasvardeclaration
|
||||
HasVarDeclaration(N) {
|
||||
// 1. Let envRec be the global Environment Record for which the method was invoked.
|
||||
const envRec = this;
|
||||
// 2. Let varDeclaredNames be envRec.[[VarNames]].
|
||||
const varDeclaredNames = envRec.VarNames;
|
||||
// 3. If varDeclaredNames contains N, return true.
|
||||
if (varDeclaredNames.includes(N)) {
|
||||
return Value.true;
|
||||
}
|
||||
// 4. Return false.
|
||||
return Value.false;
|
||||
}
|
||||
|
||||
// #sec-haslexicaldeclaration
|
||||
HasLexicalDeclaration(N) {
|
||||
// 1. Let envRec be the global Environment Record for which the method was invoked.
|
||||
const envRec = this;
|
||||
// 2. Let envRec be the global Environment Record for which the method was invoked.
|
||||
const DclRec = envRec.DeclarativeRecord;
|
||||
// 3. Let DclRec be envRec.[[DeclarativeRecord]].
|
||||
return DclRec.HasBinding(N);
|
||||
}
|
||||
|
||||
// #sec-hasrestrictedglobalproperty
|
||||
HasRestrictedGlobalProperty(N) {
|
||||
// 1. Let envRec be the global Environment Record for which the method was invoked.
|
||||
const envRec = this;
|
||||
// 2. Let ObjRec be envRec.[[ObjectRecord]].
|
||||
const ObjRec = envRec.ObjectRecord;
|
||||
// 3. Let globalObject be the binding object for ObjRec.
|
||||
const globalObject = ObjRec.bindingObject;
|
||||
// 4. Let existingProp be ? globalObject.[[GetOwnProperty]](N).
|
||||
const existingProp = Q(globalObject.GetOwnProperty(N));
|
||||
// 5. If existingProp is undefined, return false.
|
||||
if (existingProp === Value.undefined) {
|
||||
return Value.false;
|
||||
}
|
||||
// 6. If existingProp.[[Configurable]] is true, return false.
|
||||
if (existingProp.Configurable === Value.true) {
|
||||
return Value.false;
|
||||
}
|
||||
// Return true.
|
||||
return Value.true;
|
||||
}
|
||||
|
||||
// #sec-candeclareglobalvar
|
||||
CanDeclareGlobalVar(N) {
|
||||
// 1. Let envRec be the global Environment Record for which the method was invoked.
|
||||
const envRec = this;
|
||||
// 2. Let ObjRec be envRec.[[ObjectRecord]].
|
||||
const ObjRec = envRec.ObjectRecord;
|
||||
// 3. Let globalObject be the binding object for ObjRec.
|
||||
const globalObject = ObjRec.bindingObject;
|
||||
// 4. Let hasProperty be ? HasOwnProperty(globalObject, N).
|
||||
const hasProperty = Q(HasOwnProperty(globalObject, N));
|
||||
// 5. If hasProperty is true, return true.
|
||||
if (hasProperty === Value.true) {
|
||||
return Value.true;
|
||||
}
|
||||
// 6. Return ? IsExtensible(globalObject).
|
||||
return Q(IsExtensible(globalObject));
|
||||
}
|
||||
|
||||
// #sec-candeclareglobalfunction
|
||||
CanDeclareGlobalFunction(N) {
|
||||
// 1. Let envRec be the global Environment Record for which the method was invoked.
|
||||
const envRec = this;
|
||||
// 2. Let ObjRec be envRec.[[ObjectRecord]].
|
||||
const ObjRec = envRec.ObjectRecord;
|
||||
// 3. Let globalObject be the binding object for ObjRec.
|
||||
const globalObject = ObjRec.bindingObject;
|
||||
// 4. Let existingProp be ? globalObject.[[GetOwnProperty]](N).
|
||||
const existingProp = Q(globalObject.GetOwnProperty(N));
|
||||
// 5. If existingProp is undefined, return ? IsExtensible(globalObject).
|
||||
if (existingProp === Value.undefined) {
|
||||
return Q(IsExtensible(globalObject));
|
||||
}
|
||||
// 6. If existingProp.[[Configurable]] is true, return true.
|
||||
if (existingProp.Configurable === Value.true) {
|
||||
return Value.true;
|
||||
}
|
||||
// 7. If IsDataDescriptor(existingProp) is true and existingProp has attribute values
|
||||
// { [[Writable]]: true, [[Enumerable]]: true }, return true.
|
||||
if (IsDataDescriptor(existingProp) === true
|
||||
&& existingProp.Writable === Value.true
|
||||
&& existingProp.Enumerable === Value.true) {
|
||||
return Value.true;
|
||||
}
|
||||
// 8. Return false.
|
||||
return Value.false;
|
||||
}
|
||||
|
||||
// #sec-createglobalvarbinding
|
||||
CreateGlobalVarBinding(N, D) {
|
||||
// 1. Let envRec be the global Environment Record for which the method was invoked.
|
||||
const envRec = this;
|
||||
// 2. Let ObjRec be envRec.[[ObjectRecord]].
|
||||
const ObjRec = envRec.ObjectRecord;
|
||||
// 3. Let globalObject be the binding object for ObjRec.
|
||||
const globalObject = ObjRec.bindingObject;
|
||||
// 4. Let hasProperty be ? HasOwnProperty(globalObject, N).
|
||||
const hasProperty = Q(HasOwnProperty(globalObject, N));
|
||||
// 5. Let extensible be ? IsExtensible(globalObject).
|
||||
const extensible = Q(IsExtensible(globalObject));
|
||||
// 6. If hasProperty is false and extensible is true, then
|
||||
if (hasProperty === Value.false && extensible === Value.true) {
|
||||
// a. Perform ? ObjRec.CreateMutableBinding(N, D).
|
||||
Q(ObjRec.CreateMutableBinding(N, D));
|
||||
// b. Perform ? ObjRec.InitializeBinding(N, undefined).
|
||||
Q(ObjRec.InitializeBinding(N, Value.undefined));
|
||||
}
|
||||
// 7. Let varDeclaredNames be envRec.[[VarNames]].
|
||||
const varDeclaredNames = envRec.VarNames;
|
||||
// 8. If varDeclaredNames does not contain N, then
|
||||
if (!varDeclaredNames.includes(N)) {
|
||||
// a. Append N to varDeclaredNames.
|
||||
varDeclaredNames.push(N);
|
||||
}
|
||||
// return NormalCompletion(empty).
|
||||
return NormalCompletion(undefined);
|
||||
}
|
||||
|
||||
// #sec-createglobalfunctionbinding
|
||||
CreateGlobalFunctionBinding(N, V, D) {
|
||||
// 1. Let envRec be the global Environment Record for which the method was invoked.
|
||||
const envRec = this;
|
||||
// 2. Let ObjRec be envRec.[[ObjectRecord]].
|
||||
const ObjRec = envRec.ObjectRecord;
|
||||
// 3. Let globalObject be the binding object for ObjRec.
|
||||
const globalObject = ObjRec.bindingObject;
|
||||
// 4. Let existingProp be ? globalObject.[[GetOwnProperty]](N).
|
||||
const existingProp = Q(globalObject.GetOwnProperty(N));
|
||||
// 5. If existingProp is undefined or existingProp.[[Configurable]] is true, then
|
||||
let desc;
|
||||
if (existingProp === Value.undefined || existingProp.Configurable === Value.true) {
|
||||
// a. Let desc be the PropertyDescriptor { [[Value]]: V, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: D }.
|
||||
desc = Descriptor({
|
||||
Value: V,
|
||||
Writable: Value.true,
|
||||
Enumerable: Value.true,
|
||||
Configurable: D,
|
||||
});
|
||||
} else {
|
||||
// a. Let desc be the PropertyDescriptor { [[Value]]: V }.
|
||||
desc = Descriptor({
|
||||
Value: V,
|
||||
});
|
||||
}
|
||||
// 7. Perform ? DefinePropertyOrThrow(globalObject, N, desc).
|
||||
Q(DefinePropertyOrThrow(globalObject, N, desc));
|
||||
// 8. Record that the binding for N in ObjRec has been initialized.
|
||||
// 9. Perform ? Set(globalObject, N, V, false).
|
||||
Q(Set(globalObject, N, V, Value.false));
|
||||
// 10. Let varDeclaredNames be envRec.[[VarNames]].
|
||||
const varDeclaredNames = envRec.VarNames;
|
||||
// 11. If varDeclaredNames does not contain N, then
|
||||
if (!varDeclaredNames.includes(N)) {
|
||||
// a. Append N to varDeclaredNames.
|
||||
varDeclaredNames.push(N);
|
||||
}
|
||||
// 1. Return NormalCompletion(empty).
|
||||
return NormalCompletion(undefined);
|
||||
}
|
||||
|
||||
mark(m) {
|
||||
m(this.ObjectRecord);
|
||||
m(this.GlobalThisValue);
|
||||
m(this.DeclarativeRecord);
|
||||
}
|
||||
}
|
||||
|
||||
// #sec-module-environment-records
|
||||
export class ModuleEnvironmentRecord extends DeclarativeEnvironmentRecord {
|
||||
// #sec-module-environment-records-getbindingvalue-n-s
|
||||
GetBindingValue(N, S) {
|
||||
// 1. Assert: S is true.
|
||||
Assert(S === Value.true);
|
||||
// 2. Let envRec be the module Environment Record for which the method was invoked.
|
||||
const envRec = this;
|
||||
// 3. Assert: envRec has a binding for N.
|
||||
const binding = envRec.bindings.get(N);
|
||||
Assert(binding !== undefined);
|
||||
// 4. If the binding for N is an indirect binding, then
|
||||
if (binding.indirect === true) {
|
||||
// a. Let M and N2 be the indirection values provided when this binding for N was created.
|
||||
const [M, N2] = binding.target;
|
||||
// b.Let targetEnv be M.[[Environment]].
|
||||
const targetEnv = M.Environment;
|
||||
// c. If targetEnv is undefined, throw a ReferenceError exception.
|
||||
if (targetEnv === Value.undefined) {
|
||||
return surroundingAgent.Throw('ReferenceError', 'NotDefined', N);
|
||||
}
|
||||
// d. Return ? targetEnv.GetBindingValue(N2, true).
|
||||
return Q(targetEnv.GetBindingValue(N2, Value.true));
|
||||
}
|
||||
// 5. If the binding for N in envRec is an uninitialized binding, throw a ReferenceError exception.
|
||||
if (binding.initialized === false) {
|
||||
return surroundingAgent.Throw('ReferenceError', 'NotInitialized', N);
|
||||
}
|
||||
// 6. Return the value currently bound to N in envRec.
|
||||
return binding.value;
|
||||
}
|
||||
|
||||
// #sec-module-environment-records-deletebinding-n
|
||||
DeleteBinding() {
|
||||
Assert(false, 'This method is never invoked. See #sec-delete-operator-static-semantics-early-errors');
|
||||
}
|
||||
|
||||
// #sec-module-environment-records-hasthisbinding
|
||||
HasThisBinding() {
|
||||
// Return true.
|
||||
return Value.true;
|
||||
}
|
||||
|
||||
// #sec-module-environment-records-getthisbinding
|
||||
GetThisBinding() {
|
||||
// Return undefined.
|
||||
return Value.undefined;
|
||||
}
|
||||
|
||||
// #sec-createimportbinding
|
||||
CreateImportBinding(N, M, N2) {
|
||||
// 1. Let envRec be the module Environment Record for which the method was invoked.
|
||||
const envRec = this;
|
||||
// 2. Assert: envRec does not already have a binding for N.
|
||||
Assert(envRec.HasBinding(N) === Value.false);
|
||||
// 3. Assert: M is a Module Record.
|
||||
Assert(M instanceof AbstractModuleRecord);
|
||||
// 4. Assert: When M.[[Environment]] is instantiated it will have a direct binding for N2.
|
||||
// 5. Create an immutable indirect binding in envRec for N that references M and N2 as its target binding and record that the binding is initialized.
|
||||
envRec.bindings.set(N, {
|
||||
indirect: true,
|
||||
target: [M, N2],
|
||||
initialized: true,
|
||||
mark(m) {
|
||||
m(this.target[0]);
|
||||
m(this.target[1]);
|
||||
},
|
||||
});
|
||||
// 6. Return NormalCompletion(empty).
|
||||
return NormalCompletion(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
// 8.1.2.1 #sec-getidentifierreference
|
||||
export function GetIdentifierReference(env, name, strict) {
|
||||
// 1. If lex is the value null, then
|
||||
if (env === Value.null) {
|
||||
// a. Return a value of type Reference whose base value component is undefined, whose
|
||||
// referenced name component is name, and whose strict reference flag is strict.
|
||||
return new Reference({
|
||||
BaseValue: Value.undefined,
|
||||
ReferencedName: name,
|
||||
StrictReference: strict,
|
||||
});
|
||||
}
|
||||
// 2. Let exists be ? envRec.HasBinding(name).
|
||||
const exists = Q(env.HasBinding(name));
|
||||
// 3. If exists is true, then
|
||||
if (exists === Value.true) {
|
||||
// a. Return a value of type Reference whose base value component is envRec, whose
|
||||
// referenced name component is name, and whose strict reference flag is strict.
|
||||
return new Reference({
|
||||
BaseValue: env,
|
||||
ReferencedName: name,
|
||||
StrictReference: strict,
|
||||
});
|
||||
} else {
|
||||
// a. Let outer be env.[[OuterEnv]].
|
||||
const outer = env.OuterEnv;
|
||||
// b. Return ? GetIdentifierReference(outer, name, strict).
|
||||
return Q(GetIdentifierReference(outer, name, strict));
|
||||
}
|
||||
}
|
||||
|
||||
// #sec-newdeclarativeenvironment
|
||||
export function NewDeclarativeEnvironment(E) {
|
||||
// 1. Let env be a new declarative Environment Record containing O as the binding object.
|
||||
const env = new DeclarativeEnvironmentRecord();
|
||||
// 2. Set env.[[OuterEnv]] to E.
|
||||
env.OuterEnv = E;
|
||||
// 3. Return env.
|
||||
return env;
|
||||
}
|
||||
|
||||
// #sec-newobjectenvironment
|
||||
export function NewObjectEnvironment(O, E) {
|
||||
// 1. Let env be a new object Environment Record containing O as the binding object.
|
||||
const env = new ObjectEnvironmentRecord(O);
|
||||
// 2. Set env.[[OuterEnv]] to E.
|
||||
env.OuterEnv = E;
|
||||
// 3. Return env.
|
||||
return env;
|
||||
}
|
||||
|
||||
// #sec-newfunctionenvironment
|
||||
export function NewFunctionEnvironment(F, newTarget) {
|
||||
// 1. Assert: F is an ECMAScript function.
|
||||
Assert(isECMAScriptFunctionObject(F));
|
||||
// 2. Assert: Type(newTarget) is Undefined or Object.
|
||||
Assert(Type(newTarget) === 'Undefined' || Type(newTarget) === 'Object');
|
||||
// 3. Let env be a new function Environment Record containing no bindings.
|
||||
const env = new FunctionEnvironmentRecord();
|
||||
// 4. Set env.[[FunctionObject]] to F.
|
||||
env.FunctionObject = F;
|
||||
// 5. If F.[[ThisMode]] is lexical, set env.[[ThisBindingStatus]] to lexical.
|
||||
if (F.ThisMode === 'lexical') {
|
||||
env.ThisBindingStatus = 'lexical';
|
||||
} else { // 6. Else, set env.[[ThisBindingStatus]] to uninitialized.
|
||||
env.ThisBindingStatus = 'uninitialized';
|
||||
}
|
||||
// 7. Let home be F.[[HomeObject]].
|
||||
const home = F.HomeObject;
|
||||
// 8. Set env.[[HomeObject]] to home.
|
||||
env.HomeObject = home;
|
||||
// 9. Set env.[[NewTarget]] to newTarget.
|
||||
env.NewTarget = newTarget;
|
||||
// 10. Set env.[[OuterEnv]] to F.[[Environment]].
|
||||
env.OuterEnv = F.Environment;
|
||||
// 11. Return env.
|
||||
return env;
|
||||
}
|
||||
|
||||
// #sec-newglobalenvironment
|
||||
export function NewGlobalEnvironment(G, thisValue) {
|
||||
// 1. Let objRec be a new object Environment Record containing G as the binding object.
|
||||
const objRec = new ObjectEnvironmentRecord(G);
|
||||
// 2. Let dclRec be a new declarative Environment Record containing no bindings.
|
||||
const dclRec = new DeclarativeEnvironmentRecord();
|
||||
// 3. Let env be a new global Environment Record.
|
||||
const env = new GlobalEnvironmentRecord();
|
||||
// 4. Set env.[[ObjectRecord]] to objRec.
|
||||
env.ObjectRecord = objRec;
|
||||
// 5. Set env.[[GlobalThisValue]] to thisValue.
|
||||
env.GlobalThisValue = thisValue;
|
||||
// 6. Set env.[[DeclarativeRecord]] to dclRec.
|
||||
env.DeclarativeRecord = dclRec;
|
||||
// 7. Set env.[[VarNames]] to a new empty List.
|
||||
env.VarNames = [];
|
||||
// 8. Set env.[[OuterEnv]] to null.
|
||||
env.OuterEnv = Value.null;
|
||||
// 9. Return env.
|
||||
return env;
|
||||
}
|
||||
|
||||
// #sec-newmoduleenvironment
|
||||
export function NewModuleEnvironment(E) {
|
||||
// 1. Let env be a new module Environment Record containing no bindings.
|
||||
const env = new ModuleEnvironmentRecord();
|
||||
// 2. Set env.[[OuterEnv]] to E.
|
||||
env.OuterEnv = E;
|
||||
// 3. Return env.
|
||||
return env;
|
||||
}
|
||||
@@ -1,326 +0,0 @@
|
||||
import { surroundingAgent } from './engine.mjs';
|
||||
import { OutOfRange } from './helpers.mjs';
|
||||
import {
|
||||
Evaluate_Script,
|
||||
Evaluate_ScriptBody,
|
||||
Evaluate_Module,
|
||||
Evaluate_ModuleBody,
|
||||
Evaluate_ImportDeclaration,
|
||||
Evaluate_ExportDeclaration,
|
||||
Evaluate_ClassDeclaration,
|
||||
Evaluate_LexicalDeclaration,
|
||||
Evaluate_FunctionDeclaration,
|
||||
Evaluate_HoistableDeclaration,
|
||||
Evaluate_Block,
|
||||
Evaluate_VariableStatement,
|
||||
Evaluate_ExpressionStatement,
|
||||
Evaluate_EmptyStatement,
|
||||
Evaluate_IfStatement,
|
||||
Evaluate_ReturnStatement,
|
||||
Evaluate_TryStatement,
|
||||
Evaluate_ThrowStatement,
|
||||
Evaluate_DebuggerStatement,
|
||||
Evaluate_BreakableStatement,
|
||||
Evaluate_LabelledStatement,
|
||||
Evaluate_ForBinding,
|
||||
Evaluate_CaseClause,
|
||||
Evaluate_BreakStatement,
|
||||
Evaluate_ContinueStatement,
|
||||
Evaluate_WithStatement,
|
||||
Evaluate_IdentifierReference,
|
||||
Evaluate_CommaOperator,
|
||||
Evaluate_This,
|
||||
Evaluate_Literal,
|
||||
Evaluate_ArrayLiteral,
|
||||
Evaluate_ObjectLiteral,
|
||||
Evaluate_TemplateLiteral,
|
||||
Evaluate_ClassExpression,
|
||||
Evaluate_FunctionExpression,
|
||||
Evaluate_GeneratorExpression,
|
||||
Evaluate_AsyncFunctionExpression,
|
||||
Evaluate_AsyncGeneratorExpression,
|
||||
Evaluate_AdditiveExpression,
|
||||
Evaluate_MultiplicativeExpression,
|
||||
Evaluate_ExponentiationExpression,
|
||||
Evaluate_UpdateExpression,
|
||||
Evaluate_ShiftExpression,
|
||||
Evaluate_LogicalORExpression,
|
||||
Evaluate_LogicalANDExpression,
|
||||
Evaluate_BinaryBitwiseExpression,
|
||||
Evaluate_RelationalExpression,
|
||||
Evaluate_CoalesceExpression,
|
||||
Evaluate_EqualityExpression,
|
||||
Evaluate_CallExpression,
|
||||
Evaluate_NewExpression,
|
||||
Evaluate_MemberExpression,
|
||||
Evaluate_OptionalExpression,
|
||||
Evaluate_TaggedTemplateExpression,
|
||||
Evaluate_SuperCall,
|
||||
Evaluate_SuperProperty,
|
||||
Evaluate_NewTarget,
|
||||
Evaluate_ImportMeta,
|
||||
Evaluate_ImportCall,
|
||||
Evaluate_AwaitExpression,
|
||||
Evaluate_YieldExpression,
|
||||
Evaluate_ParenthesizedExpression,
|
||||
Evaluate_AssignmentExpression,
|
||||
Evaluate_UnaryExpression,
|
||||
Evaluate_ArrowFunction,
|
||||
Evaluate_AsyncArrowFunction,
|
||||
Evaluate_ConditionalExpression,
|
||||
Evaluate_RegularExpressionLiteral,
|
||||
Evaluate_AnyFunctionBody,
|
||||
Evaluate_ExpressionBody,
|
||||
} from './runtime-semantics/all.mjs';
|
||||
|
||||
export function* Evaluate(node) {
|
||||
surroundingAgent.runningExecutionContext.callSite.setLocation(node);
|
||||
|
||||
if (surroundingAgent.hostDefinedOptions.onNodeEvaluation) {
|
||||
surroundingAgent.hostDefinedOptions.onNodeEvaluation(node, surroundingAgent.currentRealmRecord);
|
||||
}
|
||||
|
||||
let result;
|
||||
switch (node.type) {
|
||||
// Language
|
||||
case 'Script':
|
||||
result = yield* Evaluate_Script(node);
|
||||
break;
|
||||
case 'ScriptBody':
|
||||
result = yield* Evaluate_ScriptBody(node);
|
||||
break;
|
||||
case 'Module':
|
||||
result = yield* Evaluate_Module(node);
|
||||
break;
|
||||
case 'ModuleBody':
|
||||
result = yield* Evaluate_ModuleBody(node);
|
||||
break;
|
||||
// Statements
|
||||
case 'Block':
|
||||
result = yield* Evaluate_Block(node);
|
||||
break;
|
||||
case 'VariableStatement':
|
||||
result = yield* Evaluate_VariableStatement(node);
|
||||
break;
|
||||
case 'EmptyStatement':
|
||||
result = Evaluate_EmptyStatement(node);
|
||||
break;
|
||||
case 'IfStatement':
|
||||
result = yield* Evaluate_IfStatement(node);
|
||||
break;
|
||||
case 'ExpressionStatement':
|
||||
result = yield* Evaluate_ExpressionStatement(node);
|
||||
break;
|
||||
case 'WhileStatement':
|
||||
case 'DoWhileStatement':
|
||||
case 'SwitchStatement':
|
||||
case 'ForStatement':
|
||||
case 'ForInStatement':
|
||||
case 'ForOfStatement':
|
||||
case 'ForAwaitStatement':
|
||||
result = yield* Evaluate_BreakableStatement(node);
|
||||
break;
|
||||
case 'ForBinding':
|
||||
result = Evaluate_ForBinding(node);
|
||||
break;
|
||||
case 'CaseClause':
|
||||
case 'DefaultClause':
|
||||
result = yield* Evaluate_CaseClause(node);
|
||||
break;
|
||||
case 'BreakStatement':
|
||||
result = Evaluate_BreakStatement(node);
|
||||
break;
|
||||
case 'ContinueStatement':
|
||||
result = Evaluate_ContinueStatement(node);
|
||||
break;
|
||||
case 'LabelledStatement':
|
||||
result = yield* Evaluate_LabelledStatement(node);
|
||||
break;
|
||||
case 'ReturnStatement':
|
||||
result = yield* Evaluate_ReturnStatement(node);
|
||||
break;
|
||||
case 'ThrowStatement':
|
||||
result = yield* Evaluate_ThrowStatement(node);
|
||||
break;
|
||||
case 'TryStatement':
|
||||
result = yield* Evaluate_TryStatement(node);
|
||||
break;
|
||||
case 'DebuggerStatement':
|
||||
result = Evaluate_DebuggerStatement(node);
|
||||
break;
|
||||
case 'WithStatement':
|
||||
result = yield* Evaluate_WithStatement(node);
|
||||
break;
|
||||
// Declarations
|
||||
case 'ImportDeclaration':
|
||||
result = Evaluate_ImportDeclaration(node);
|
||||
break;
|
||||
case 'ExportDeclaration':
|
||||
result = yield* Evaluate_ExportDeclaration(node);
|
||||
break;
|
||||
case 'ClassDeclaration':
|
||||
result = yield* Evaluate_ClassDeclaration(node);
|
||||
break;
|
||||
case 'LexicalDeclaration':
|
||||
result = yield* Evaluate_LexicalDeclaration(node);
|
||||
break;
|
||||
case 'FunctionDeclaration':
|
||||
result = Evaluate_FunctionDeclaration(node);
|
||||
break;
|
||||
case 'GeneratorDeclaration':
|
||||
case 'AsyncFunctionDeclaration':
|
||||
case 'AsyncGeneratorDeclaration':
|
||||
result = Evaluate_HoistableDeclaration(node);
|
||||
break;
|
||||
// Expressions
|
||||
case 'CommaOperator':
|
||||
result = yield* Evaluate_CommaOperator(node);
|
||||
break;
|
||||
case 'ThisExpression':
|
||||
result = Evaluate_This(node);
|
||||
break;
|
||||
case 'IdentifierReference':
|
||||
result = Evaluate_IdentifierReference(node);
|
||||
break;
|
||||
case 'NullLiteral':
|
||||
case 'BooleanLiteral':
|
||||
case 'NumericLiteral':
|
||||
case 'StringLiteral':
|
||||
result = Evaluate_Literal(node);
|
||||
break;
|
||||
case 'ArrayLiteral':
|
||||
result = yield* Evaluate_ArrayLiteral(node);
|
||||
break;
|
||||
case 'ObjectLiteral':
|
||||
result = yield* Evaluate_ObjectLiteral(node);
|
||||
break;
|
||||
case 'FunctionExpression':
|
||||
result = yield* Evaluate_FunctionExpression(node);
|
||||
break;
|
||||
case 'ClassExpression':
|
||||
result = yield* Evaluate_ClassExpression(node);
|
||||
break;
|
||||
case 'GeneratorExpression':
|
||||
result = yield* Evaluate_GeneratorExpression(node);
|
||||
break;
|
||||
case 'AsyncFunctionExpression':
|
||||
result = yield* Evaluate_AsyncFunctionExpression(node);
|
||||
break;
|
||||
case 'AsyncGeneratorExpression':
|
||||
result = yield* Evaluate_AsyncGeneratorExpression(node);
|
||||
break;
|
||||
case 'TemplateLiteral':
|
||||
result = yield* Evaluate_TemplateLiteral(node);
|
||||
break;
|
||||
case 'ParenthesizedExpression':
|
||||
result = yield* Evaluate_ParenthesizedExpression(node);
|
||||
break;
|
||||
case 'AdditiveExpression':
|
||||
result = yield* Evaluate_AdditiveExpression(node);
|
||||
break;
|
||||
case 'MultiplicativeExpression':
|
||||
result = yield* Evaluate_MultiplicativeExpression(node);
|
||||
break;
|
||||
case 'ExponentiationExpression':
|
||||
result = yield* Evaluate_ExponentiationExpression(node);
|
||||
break;
|
||||
case 'UpdateExpression':
|
||||
result = yield* Evaluate_UpdateExpression(node);
|
||||
break;
|
||||
case 'ShiftExpression':
|
||||
result = yield* Evaluate_ShiftExpression(node);
|
||||
break;
|
||||
case 'LogicalORExpression':
|
||||
result = yield* Evaluate_LogicalORExpression(node);
|
||||
break;
|
||||
case 'LogicalANDExpression':
|
||||
result = yield* Evaluate_LogicalANDExpression(node);
|
||||
break;
|
||||
case 'BitwiseANDExpression':
|
||||
case 'BitwiseXORExpression':
|
||||
case 'BitwiseORExpression':
|
||||
result = yield* Evaluate_BinaryBitwiseExpression(node);
|
||||
break;
|
||||
case 'RelationalExpression':
|
||||
result = yield* Evaluate_RelationalExpression(node);
|
||||
break;
|
||||
case 'CoalesceExpression':
|
||||
result = yield* Evaluate_CoalesceExpression(node);
|
||||
break;
|
||||
case 'EqualityExpression':
|
||||
result = yield* Evaluate_EqualityExpression(node);
|
||||
break;
|
||||
case 'CallExpression':
|
||||
result = yield* Evaluate_CallExpression(node);
|
||||
break;
|
||||
case 'NewExpression':
|
||||
result = yield* Evaluate_NewExpression(node);
|
||||
break;
|
||||
case 'MemberExpression':
|
||||
result = yield* Evaluate_MemberExpression(node);
|
||||
break;
|
||||
case 'OptionalExpression':
|
||||
result = yield* Evaluate_OptionalExpression(node);
|
||||
break;
|
||||
case 'TaggedTemplateExpression':
|
||||
result = yield* Evaluate_TaggedTemplateExpression(node);
|
||||
break;
|
||||
case 'SuperProperty':
|
||||
result = yield* Evaluate_SuperProperty(node);
|
||||
break;
|
||||
case 'SuperCall':
|
||||
result = yield* Evaluate_SuperCall(node);
|
||||
break;
|
||||
case 'NewTarget':
|
||||
result = Evaluate_NewTarget(node);
|
||||
break;
|
||||
case 'ImportMeta':
|
||||
result = Evaluate_ImportMeta(node);
|
||||
break;
|
||||
case 'ImportCall':
|
||||
result = yield* Evaluate_ImportCall(node);
|
||||
break;
|
||||
case 'AssignmentExpression':
|
||||
result = yield* Evaluate_AssignmentExpression(node);
|
||||
break;
|
||||
case 'YieldExpression':
|
||||
result = yield* Evaluate_YieldExpression(node);
|
||||
break;
|
||||
case 'AwaitExpression':
|
||||
result = yield* Evaluate_AwaitExpression(node);
|
||||
break;
|
||||
case 'UnaryExpression':
|
||||
result = yield* Evaluate_UnaryExpression(node);
|
||||
break;
|
||||
case 'ArrowFunction':
|
||||
result = yield* Evaluate_ArrowFunction(node);
|
||||
break;
|
||||
case 'AsyncArrowFunction':
|
||||
result = yield* Evaluate_AsyncArrowFunction(node);
|
||||
break;
|
||||
case 'ConditionalExpression':
|
||||
result = yield* Evaluate_ConditionalExpression(node);
|
||||
break;
|
||||
case 'RegularExpressionLiteral':
|
||||
result = Evaluate_RegularExpressionLiteral(node);
|
||||
break;
|
||||
case 'AsyncFunctionBody':
|
||||
case 'GeneratorBody':
|
||||
case 'AsyncGeneratorBody':
|
||||
result = yield* Evaluate_AnyFunctionBody(node);
|
||||
break;
|
||||
case 'ExpressionBody':
|
||||
result = yield* Evaluate_ExpressionBody(node);
|
||||
break;
|
||||
default:
|
||||
throw new OutOfRange('Evaluate', node);
|
||||
}
|
||||
|
||||
if (surroundingAgent.hostDefinedOptions.onNodeEvaluationComplete) {
|
||||
surroundingAgent.hostDefinedOptions.onNodeEvaluationComplete(
|
||||
{ node, result },
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -1,349 +0,0 @@
|
||||
import { surroundingAgent } from './engine.mjs';
|
||||
import { Type, Value, Descriptor } from './value.mjs';
|
||||
import { ToString, DefinePropertyOrThrow, CreateBuiltinFunction } from './abstract-ops/all.mjs';
|
||||
import { X, AwaitFulfilledFunctions } from './completion.mjs';
|
||||
|
||||
function convertValueForKey(key) {
|
||||
if (typeof key === 'string') {
|
||||
return Symbol.for(`engine262_helper_key_${key}`);
|
||||
}
|
||||
switch (Type(key)) {
|
||||
case 'String':
|
||||
return key.stringValue();
|
||||
case 'Number':
|
||||
if (key.numberValue() === 0 && Object.is(key.numberValue(), -0)) {
|
||||
return key;
|
||||
}
|
||||
return key.numberValue();
|
||||
default:
|
||||
return key;
|
||||
}
|
||||
}
|
||||
|
||||
export class ValueMap {
|
||||
constructor() {
|
||||
this.map = new Map();
|
||||
}
|
||||
|
||||
get size() {
|
||||
return this.map.size;
|
||||
}
|
||||
|
||||
get(key) {
|
||||
return this.map.get(convertValueForKey(key));
|
||||
}
|
||||
|
||||
set(key, value) {
|
||||
this.map.set(convertValueForKey(key), value);
|
||||
return this;
|
||||
}
|
||||
|
||||
has(key) {
|
||||
return this.map.has(convertValueForKey(key));
|
||||
}
|
||||
|
||||
delete(key) {
|
||||
return this.map.delete(convertValueForKey(key));
|
||||
}
|
||||
|
||||
* keys() {
|
||||
for (const [key] of this.entries()) {
|
||||
yield key;
|
||||
}
|
||||
}
|
||||
|
||||
entries() {
|
||||
return this[Symbol.iterator]();
|
||||
}
|
||||
|
||||
forEach(cb) {
|
||||
for (const [key, value] of this.entries()) {
|
||||
cb(value, key, this);
|
||||
}
|
||||
}
|
||||
|
||||
* [Symbol.iterator]() {
|
||||
for (const [key, value] of this.map.entries()) {
|
||||
if (typeof key === 'string' || typeof key === 'number') {
|
||||
yield [new Value(key), value];
|
||||
} else {
|
||||
yield [key, value];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mark(m) {
|
||||
for (const [k, v] of this.entries()) {
|
||||
m(k);
|
||||
m(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class ValueSet {
|
||||
constructor(init) {
|
||||
this.set = new Set();
|
||||
if (init !== undefined && init !== null) {
|
||||
for (const item of init) {
|
||||
this.add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get size() {
|
||||
return this.set.size;
|
||||
}
|
||||
|
||||
add(item) {
|
||||
this.set.add(convertValueForKey(item));
|
||||
return this;
|
||||
}
|
||||
|
||||
has(item) {
|
||||
return this.set.has(convertValueForKey(item));
|
||||
}
|
||||
|
||||
delete(item) {
|
||||
return this.set.delete(convertValueForKey(item));
|
||||
}
|
||||
|
||||
values() {
|
||||
return this[Symbol.iterator]();
|
||||
}
|
||||
|
||||
* [Symbol.iterator]() {
|
||||
for (const key of this.set.values()) {
|
||||
if (typeof key === 'string' || typeof key === 'number') {
|
||||
yield new Value(key);
|
||||
} else {
|
||||
yield key;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mark(m) {
|
||||
for (const v of this.values()) {
|
||||
m(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class OutOfRange extends RangeError {
|
||||
/* istanbul ignore next */
|
||||
constructor(fn, detail) {
|
||||
super(`${fn}() argument out of range`);
|
||||
this.detail = detail;
|
||||
}
|
||||
}
|
||||
|
||||
export function unwind(iterator, maxSteps = 1) {
|
||||
let steps = 0;
|
||||
while (true) {
|
||||
const { done, value } = iterator.next('Unwind');
|
||||
if (done) {
|
||||
return value;
|
||||
}
|
||||
/* istanbul ignore next */
|
||||
steps += 1;
|
||||
if (steps > maxSteps) {
|
||||
throw new RangeError('Max steps exceeded');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const kSafeToResume = Symbol('kSameToResume');
|
||||
|
||||
export function handleInResume(fn, ...args) {
|
||||
const bound = () => fn(...args);
|
||||
bound[kSafeToResume] = true;
|
||||
return bound;
|
||||
}
|
||||
|
||||
export function resume(context, completion) {
|
||||
const { value } = context.codeEvaluationState.next(completion);
|
||||
if (typeof value === 'function' && value[kSafeToResume] === true) {
|
||||
return X(value());
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
export class CallSite {
|
||||
constructor(context) {
|
||||
this.context = context;
|
||||
this.lastNode = null;
|
||||
this.constructCall = false;
|
||||
}
|
||||
|
||||
clone(context = this.context) {
|
||||
const c = new CallSite(context);
|
||||
c.lastNode = this.lastNode;
|
||||
c.constructCall = this.constructCall;
|
||||
return c;
|
||||
}
|
||||
|
||||
isTopLevel() {
|
||||
return this.context.Function === Value.null;
|
||||
}
|
||||
|
||||
isConstructCall() {
|
||||
return this.constructCall;
|
||||
}
|
||||
|
||||
isAsync() {
|
||||
if (this.context.Function !== Value.null && this.context.Function.ECMAScriptCode) {
|
||||
const code = this.context.Function.ECMAScriptCode;
|
||||
return code.type === 'AsyncFunctionBody' || code.type === 'AsyncGeneratorBody';
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
isNative() {
|
||||
return !!this.context.Function.nativeFunction;
|
||||
}
|
||||
|
||||
getFunctionName() {
|
||||
if (this.context.Function !== Value.null) {
|
||||
const name = this.context.Function.properties.get(new Value('name'));
|
||||
if (name) {
|
||||
return X(ToString(name.Value)).stringValue();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
getSpecifier() {
|
||||
if (this.context.ScriptOrModule !== Value.null) {
|
||||
return this.context.ScriptOrModule.HostDefined.specifier;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
setLocation(node) {
|
||||
this.lastNode = node;
|
||||
}
|
||||
|
||||
get lineNumber() {
|
||||
if (this.lastNode) {
|
||||
return this.lastNode.location.start.line;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
get columnNumber() {
|
||||
if (this.lastNode) {
|
||||
return this.lastNode.location.start.column;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
loc() {
|
||||
if (this.isNative()) {
|
||||
return 'native';
|
||||
}
|
||||
let out = '';
|
||||
const specifier = this.getSpecifier();
|
||||
if (specifier) {
|
||||
out += specifier;
|
||||
} else {
|
||||
out += '<anonymous>';
|
||||
}
|
||||
if (this.lineNumber !== null) {
|
||||
out += `:${this.lineNumber}`;
|
||||
if (this.columnNumber !== null) {
|
||||
out += `:${this.columnNumber}`;
|
||||
}
|
||||
}
|
||||
return out.trim();
|
||||
}
|
||||
|
||||
toString() {
|
||||
const isAsync = this.isAsync();
|
||||
const functionName = this.getFunctionName();
|
||||
const isConstructCall = this.isConstructCall();
|
||||
const isMethodCall = !isConstructCall && !this.isTopLevel();
|
||||
|
||||
let string = isAsync ? 'async ' : '';
|
||||
|
||||
if (isConstructCall) {
|
||||
string += 'new ';
|
||||
}
|
||||
|
||||
if (isMethodCall || isConstructCall) {
|
||||
if (functionName) {
|
||||
string += functionName;
|
||||
} else {
|
||||
string += '<anonymous>';
|
||||
}
|
||||
} else if (functionName) {
|
||||
string += functionName;
|
||||
} else {
|
||||
return `${string}${this.loc()}`;
|
||||
}
|
||||
|
||||
return `${string} (${this.loc()})`;
|
||||
}
|
||||
}
|
||||
|
||||
function captureAsyncStack(stack) {
|
||||
let promise = stack[0].context.promiseCapability.Promise;
|
||||
for (let i = 0; i < 10; i += 1) {
|
||||
if (promise.PromiseFulfillReactions.length !== 1) {
|
||||
return;
|
||||
}
|
||||
const [reaction] = promise.PromiseFulfillReactions;
|
||||
if (reaction.Handler && reaction.Handler.Callback.nativeFunction === AwaitFulfilledFunctions) {
|
||||
const asyncContext = reaction.Handler.Callback.AsyncContext;
|
||||
stack.push(asyncContext.callSite.clone());
|
||||
if ('PromiseState' in asyncContext.promiseCapability.Promise) {
|
||||
promise = asyncContext.promiseCapability.Promise;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} else if (reaction.Capability !== Value.undefined) {
|
||||
if ('PromiseState' in reaction.Capability.Promise) {
|
||||
promise = reaction.Capability.Promise;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function captureStack(O) {
|
||||
const stack = [];
|
||||
for (let i = surroundingAgent.executionContextStack.length - 2; i >= 0; i -= 1) {
|
||||
const e = surroundingAgent.executionContextStack[i];
|
||||
if (e.VariableEnvironment === undefined && e.Function === Value.null) {
|
||||
break;
|
||||
}
|
||||
stack.push(e.callSite.clone());
|
||||
if (e.callSite.isAsync()) {
|
||||
i -= 1; // skip original execution context which has no useful information.
|
||||
}
|
||||
}
|
||||
|
||||
if (stack.length > 0 && stack[0].context.promiseCapability) {
|
||||
captureAsyncStack(stack);
|
||||
}
|
||||
|
||||
let cache = null;
|
||||
|
||||
X(DefinePropertyOrThrow(O, new Value('stack'), Descriptor({
|
||||
Get: CreateBuiltinFunction(() => {
|
||||
if (cache === null) {
|
||||
let errorString = X(ToString(O)).stringValue();
|
||||
stack.forEach((s) => {
|
||||
errorString = `${errorString}\n at ${s.toString()}`;
|
||||
});
|
||||
cache = new Value(errorString);
|
||||
}
|
||||
return cache;
|
||||
}, []),
|
||||
Set: CreateBuiltinFunction(([value = Value.undefined]) => {
|
||||
cache = value;
|
||||
return Value.undefined;
|
||||
}, []),
|
||||
Enumerable: Value.false,
|
||||
Configurable: Value.true,
|
||||
})));
|
||||
}
|
||||
@@ -1,205 +0,0 @@
|
||||
import { surroundingAgent } from './engine.mjs';
|
||||
import { Type, Value, wellKnownSymbols } from './value.mjs';
|
||||
import {
|
||||
Call, IsArray, Get, LengthOfArrayLike,
|
||||
EscapeRegExpPattern,
|
||||
} from './abstract-ops/all.mjs';
|
||||
import { Q, X } from './completion.mjs';
|
||||
|
||||
const bareKeyRe = /^[a-zA-Z_][a-zA-Z_0-9]*$/;
|
||||
|
||||
const getObjectTag = (value, wrap) => {
|
||||
let s;
|
||||
try {
|
||||
s = X(Get(value, wellKnownSymbols.toStringTag)).stringValue();
|
||||
} catch {}
|
||||
try {
|
||||
const c = X(Get(value, new Value('constructor')));
|
||||
s = X(Get(c, new Value('name'))).stringValue();
|
||||
} catch {}
|
||||
if (s) {
|
||||
if (wrap) {
|
||||
return `[${s}] `;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
const compactObject = (realm, value) => {
|
||||
try {
|
||||
const toString = X(Get(value, new Value('toString')));
|
||||
const objectToString = realm.Intrinsics['%Object.prototype.toString%'];
|
||||
if (toString.nativeFunction === objectToString.nativeFunction) {
|
||||
return X(Call(toString, value)).stringValue();
|
||||
} else {
|
||||
const tag = getObjectTag(value, false) || 'Unknown';
|
||||
const ctor = X(Get(value, new Value('constructor')));
|
||||
if (Type(ctor) === 'Object') {
|
||||
const ctorName = X(Get(ctor, new Value('name'))).stringValue();
|
||||
if (ctorName !== '') {
|
||||
return `#<${ctorName}>`;
|
||||
}
|
||||
return `[object ${tag}]`;
|
||||
}
|
||||
return `[object ${tag}]`;
|
||||
}
|
||||
} catch (e) {
|
||||
return '[object Unknown]';
|
||||
}
|
||||
};
|
||||
|
||||
const INSPECTORS = {
|
||||
Completion: (v, ctx, i) => i(v.Value),
|
||||
Null: () => 'null',
|
||||
Undefined: () => 'undefined',
|
||||
Boolean: (v) => v.boolean.toString(),
|
||||
Number: (v) => {
|
||||
const n = v.numberValue();
|
||||
if (n === 0 && Object.is(n, -0)) {
|
||||
return '-0';
|
||||
}
|
||||
return n.toString();
|
||||
},
|
||||
BigInt: (v) => `${v.bigintValue()}n`,
|
||||
String: (v) => {
|
||||
const s = JSON.stringify(v.stringValue()).slice(1, -1);
|
||||
return `'${s}'`;
|
||||
},
|
||||
Symbol: (v) => `Symbol(${v.Description === Value.undefined ? '' : v.Description.stringValue()})`,
|
||||
Object: (v, ctx, i) => {
|
||||
if (ctx.inspected.includes(v)) {
|
||||
return '[Circular]';
|
||||
}
|
||||
if ('PromiseState' in v) {
|
||||
ctx.indent += 1;
|
||||
const result = v.PromiseState === 'pending' ? 'undefined' : i(v.PromiseResult);
|
||||
ctx.indent -= 1;
|
||||
return `Promise {
|
||||
[[PromiseState]]: ${v.PromiseState}
|
||||
[[PromiseResult]]: ${result}
|
||||
}`;
|
||||
}
|
||||
|
||||
if ('Call' in v) {
|
||||
const name = v.properties.get(new Value('name'));
|
||||
if (name !== undefined && name.Value.stringValue() !== '') {
|
||||
return `[Function: ${name.Value.stringValue()}]`;
|
||||
}
|
||||
return '[Function]';
|
||||
}
|
||||
|
||||
if ('ErrorData' in v) {
|
||||
let e = Q(Get(v, new Value('stack')));
|
||||
if (!e.stringValue) {
|
||||
const toString = Q(Get(v, new Value('toString')));
|
||||
e = X(Call(toString, v));
|
||||
}
|
||||
return e.stringValue();
|
||||
}
|
||||
|
||||
if ('RegExpMatcher' in v) {
|
||||
const P = EscapeRegExpPattern(v.OriginalSource, v.OriginalFlags).stringValue();
|
||||
const F = v.OriginalFlags.stringValue();
|
||||
return `/${P}/${F}`;
|
||||
}
|
||||
|
||||
if ('DateValue' in v) {
|
||||
const d = new Date(v.DateValue.numberValue());
|
||||
if (Number.isNaN(d.getTime())) {
|
||||
return '[Date Invalid]';
|
||||
}
|
||||
return `[Date ${d.toISOString()}]`;
|
||||
}
|
||||
|
||||
if ('BooleanData' in v) {
|
||||
return `[Boolean ${i(v.BooleanData)}]`;
|
||||
}
|
||||
if ('NumberData' in v) {
|
||||
return `[Number ${i(v.NumberData)}]`;
|
||||
}
|
||||
if ('BigIntData' in v) {
|
||||
return `[BigInt ${i(v.BigIntData)}]`;
|
||||
}
|
||||
if ('StringData' in v) {
|
||||
return `[String ${i(v.StringData)}]`;
|
||||
}
|
||||
if ('SymbolData' in v) {
|
||||
return `[Symbol ${i(v.SymbolData)}]`;
|
||||
}
|
||||
|
||||
ctx.indent += 1;
|
||||
ctx.inspected.push(v);
|
||||
|
||||
try {
|
||||
const isArray = IsArray(v) === Value.true;
|
||||
const isTypedArray = 'TypedArrayName' in v;
|
||||
if (isArray || isTypedArray) {
|
||||
const length = X(LengthOfArrayLike(v)).numberValue();
|
||||
let holes = 0;
|
||||
const out = [];
|
||||
for (let j = 0; j < length; j += 1) {
|
||||
const elem = X(v.GetOwnProperty(new Value(j.toString())));
|
||||
if (elem === Value.undefined) {
|
||||
holes += 1;
|
||||
} else {
|
||||
if (holes > 0) {
|
||||
out.push(`<${holes} empty items>`);
|
||||
holes = 0;
|
||||
}
|
||||
if (elem.Value) {
|
||||
out.push(i(elem.Value));
|
||||
} else {
|
||||
out.push('<accessor>');
|
||||
}
|
||||
}
|
||||
}
|
||||
return `${isTypedArray ? `${v.TypedArrayName.stringValue()} ` : ''}[${out.join(', ')}]`;
|
||||
}
|
||||
|
||||
const keys = X(v.OwnPropertyKeys());
|
||||
const cache = [];
|
||||
for (const key of keys) {
|
||||
const C = X(v.GetOwnProperty(key));
|
||||
if (C.Enumerable === Value.true) {
|
||||
cache.push([
|
||||
Type(key) === 'String' && bareKeyRe.test(key.stringValue()) ? key.stringValue() : i(key),
|
||||
C.Value ? i(C.Value) : '<accessor>',
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
const tag = getObjectTag(v);
|
||||
let out = tag && tag !== 'Object' ? `${tag} {` : '{';
|
||||
if (cache.length > 5) {
|
||||
cache.forEach((c) => {
|
||||
out = `${out}\n${' '.repeat(ctx.indent)}${c[0]}: ${c[1]},`;
|
||||
});
|
||||
return `${out}\n${' '.repeat(ctx.indent - 1)}}`;
|
||||
} else {
|
||||
const oc = ctx.compact;
|
||||
ctx.compact = true;
|
||||
cache.forEach((c, index) => {
|
||||
out = `${out}${index === 0 ? '' : ','} ${c[0]}: ${c[1]}`;
|
||||
});
|
||||
ctx.compact = oc;
|
||||
return `${out} }`;
|
||||
}
|
||||
} catch {
|
||||
return compactObject(ctx, v);
|
||||
} finally {
|
||||
ctx.indent -= 1;
|
||||
ctx.inspected.pop();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export function inspect(value) {
|
||||
const context = {
|
||||
realm: surroundingAgent.currentRealmRecord,
|
||||
indent: 0,
|
||||
inspected: [],
|
||||
};
|
||||
const inner = (v) => INSPECTORS[Type(v)](v, context, inner);
|
||||
return inner(value);
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
import { surroundingAgent } from '../engine.mjs';
|
||||
import { Value, Descriptor } from '../value.mjs';
|
||||
import {
|
||||
CreateMethodProperty,
|
||||
ToString,
|
||||
IterableToList,
|
||||
OrdinaryCreateFromConstructor,
|
||||
DefinePropertyOrThrow,
|
||||
CreateArrayFromList,
|
||||
} from '../abstract-ops/all.mjs';
|
||||
import { Q, X } from '../completion.mjs';
|
||||
import { captureStack } from '../helpers.mjs';
|
||||
import { BootstrapConstructor } from './Bootstrap.mjs';
|
||||
|
||||
// #sec-aggregate-error-constructor
|
||||
function AggregateErrorConstructor([errors = Value.undefined, message = Value.undefined], { NewTarget }) {
|
||||
// 1. If NewTarget is undefined, let newTarget be the active function object, else let newTarget be NewTarget.
|
||||
let newTarget;
|
||||
if (NewTarget === Value.undefined) {
|
||||
newTarget = surroundingAgent.activeFunctionObject;
|
||||
} else {
|
||||
newTarget = NewTarget;
|
||||
}
|
||||
// 2. Let O be ? OrdinaryCreateFromConstructor(newTarget, "%AggregateError.prototype%", « [[ErrorData]] »).
|
||||
const O = Q(OrdinaryCreateFromConstructor(newTarget, '%AggregateError.prototype%', [
|
||||
'ErrorData',
|
||||
]));
|
||||
// 3. If message is not undefined, then
|
||||
if (message !== Value.undefined) {
|
||||
// a. Let msg be ? ToString(message).
|
||||
const msg = Q(ToString(message));
|
||||
// b. Perform ! CreateMethodProperty(O, "message", msg).
|
||||
X(CreateMethodProperty(O, new Value('message'), msg));
|
||||
}
|
||||
// 4. Let errorsList be ? IterableToList(errors).
|
||||
const errorsList = Q(IterableToList(errors));
|
||||
// 5. Perform ! DefinePropertyOrThrow(O, "errors", Property Descriptor { [[Configurable]]: true, [[Enumerable]]: false, [[Writable]]: true, [[Value]]: ! CreateArrayFromList(errorsList) }).
|
||||
X(DefinePropertyOrThrow(O, new Value('errors'), Descriptor({
|
||||
Configurable: Value.true,
|
||||
Enumerable: Value.false,
|
||||
Writable: Value.true,
|
||||
Value: X(CreateArrayFromList(errorsList)),
|
||||
})));
|
||||
|
||||
// NON-SPEC
|
||||
X(captureStack(O));
|
||||
|
||||
// 6. Return O.
|
||||
return O;
|
||||
}
|
||||
|
||||
export function BootstrapAggregateError(realmRec) {
|
||||
const c = BootstrapConstructor(realmRec, AggregateErrorConstructor, 'AggregateError', 2, realmRec.Intrinsics['%AggregateError.prototype%'], []);
|
||||
c.Prototype = realmRec.Intrinsics['%Error%'];
|
||||
realmRec.Intrinsics['%AggregateError%'] = c;
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
import { Value } from '../value.mjs';
|
||||
import { BootstrapPrototype } from './Bootstrap.mjs';
|
||||
|
||||
export function BootstrapAggregateErrorPrototype(realmRec) {
|
||||
const proto = BootstrapPrototype(realmRec, [
|
||||
['name', new Value('AggregateError')],
|
||||
['message', new Value('')],
|
||||
], realmRec.Intrinsics['%Error.prototype%'], 'AggregateError');
|
||||
|
||||
realmRec.Intrinsics['%AggregateError.prototype%'] = proto;
|
||||
}
|
||||
@@ -1,215 +0,0 @@
|
||||
import {
|
||||
surroundingAgent,
|
||||
} from '../engine.mjs';
|
||||
import {
|
||||
AbruptCompletion,
|
||||
Q,
|
||||
ThrowCompletion, X,
|
||||
} from '../completion.mjs';
|
||||
import {
|
||||
ArrayCreate,
|
||||
Assert,
|
||||
Call,
|
||||
Construct,
|
||||
CreateDataProperty,
|
||||
CreateDataPropertyOrThrow,
|
||||
Get,
|
||||
GetIterator,
|
||||
GetMethod,
|
||||
GetPrototypeFromConstructor,
|
||||
IsArray,
|
||||
IsCallable,
|
||||
IsConstructor,
|
||||
IteratorClose,
|
||||
IteratorStep,
|
||||
IteratorValue,
|
||||
Set,
|
||||
LengthOfArrayLike,
|
||||
ToObject,
|
||||
ToString,
|
||||
ToUint32,
|
||||
} from '../abstract-ops/all.mjs';
|
||||
import {
|
||||
Type,
|
||||
Value,
|
||||
wellKnownSymbols,
|
||||
} from '../value.mjs';
|
||||
import { OutOfRange } from '../helpers.mjs';
|
||||
import { BootstrapConstructor } from './Bootstrap.mjs';
|
||||
|
||||
// 22.1.1 #sec-array-constructor
|
||||
function ArrayConstructor(argumentsList, { NewTarget }) {
|
||||
const numberOfArgs = argumentsList.length;
|
||||
if (numberOfArgs === 0) {
|
||||
// 22.1.1.1 #sec-array-constructor-array
|
||||
Assert(numberOfArgs === 0);
|
||||
if (Type(NewTarget) === 'Undefined') {
|
||||
NewTarget = surroundingAgent.activeFunctionObject;
|
||||
}
|
||||
const proto = GetPrototypeFromConstructor(NewTarget, '%Array.prototype%');
|
||||
return ArrayCreate(new Value(0), proto);
|
||||
} else if (numberOfArgs === 1) {
|
||||
// 22.1.1.2 #sec-array-len
|
||||
const [len] = argumentsList;
|
||||
Assert(numberOfArgs === 1);
|
||||
if (Type(NewTarget) === 'Undefined') {
|
||||
NewTarget = surroundingAgent.activeFunctionObject;
|
||||
}
|
||||
const proto = GetPrototypeFromConstructor(NewTarget, '%Array.prototype%');
|
||||
const array = ArrayCreate(new Value(0), proto);
|
||||
let intLen;
|
||||
if (Type(len) !== 'Number') {
|
||||
const defineStatus = X(CreateDataProperty(array, new Value('0'), len));
|
||||
Assert(defineStatus === Value.true);
|
||||
intLen = new Value(1);
|
||||
} else {
|
||||
intLen = ToUint32(len);
|
||||
if (intLen.numberValue() !== len.numberValue()) {
|
||||
return surroundingAgent.Throw('RangeError', 'InvalidArrayLength', len);
|
||||
}
|
||||
}
|
||||
Set(array, new Value('length'), intLen, Value.true);
|
||||
return array;
|
||||
} else if (numberOfArgs >= 2) {
|
||||
// 22.1.1.3 #sec-array-items
|
||||
const items = argumentsList;
|
||||
Assert(numberOfArgs >= 2);
|
||||
if (Type(NewTarget) === 'Undefined') {
|
||||
NewTarget = surroundingAgent.activeFunctionObject;
|
||||
}
|
||||
const proto = GetPrototypeFromConstructor(NewTarget, '%Array.prototype%');
|
||||
const array = ArrayCreate(new Value(0), proto);
|
||||
let k = 0;
|
||||
while (k < numberOfArgs) {
|
||||
const Pk = ToString(new Value(k));
|
||||
const itemK = items[k];
|
||||
const defineStatus = X(CreateDataProperty(array, Pk, itemK));
|
||||
Assert(defineStatus === Value.true);
|
||||
k += 1;
|
||||
}
|
||||
Assert(X(Get(array, new Value('length'))).numberValue() === numberOfArgs);
|
||||
return array;
|
||||
}
|
||||
|
||||
throw new OutOfRange('ArrayConstructor', numberOfArgs);
|
||||
}
|
||||
|
||||
// 22.1.2.1 #sec-array.from
|
||||
function Array_from([items = Value.undefined, mapfn = Value.undefined, thisArg = Value.undefined], { thisValue }) {
|
||||
const C = thisValue;
|
||||
let mapping;
|
||||
let A;
|
||||
if (mapfn === Value.undefined) {
|
||||
mapping = false;
|
||||
} else {
|
||||
if (IsCallable(mapfn) === Value.false) {
|
||||
return surroundingAgent.Throw('TypeError', 'NotAFunction', mapfn);
|
||||
}
|
||||
mapping = true;
|
||||
}
|
||||
const usingIterator = Q(GetMethod(items, wellKnownSymbols.iterator));
|
||||
if (usingIterator !== Value.undefined) {
|
||||
if (IsConstructor(C) === Value.true) {
|
||||
A = Q(Construct(C));
|
||||
} else {
|
||||
A = X(ArrayCreate(new Value(0)));
|
||||
}
|
||||
const iteratorRecord = Q(GetIterator(items, 'sync', usingIterator));
|
||||
let k = 0;
|
||||
while (true) { // eslint-disable-line no-constant-condition
|
||||
if (k >= (2 ** 53) - 1) {
|
||||
const error = ThrowCompletion(surroundingAgent.Throw('TypeError', 'ArrayPastSafeLength').Value);
|
||||
return Q(IteratorClose(iteratorRecord, error));
|
||||
}
|
||||
const Pk = X(ToString(new Value(k)));
|
||||
const next = Q(IteratorStep(iteratorRecord));
|
||||
if (next === Value.false) {
|
||||
Q(Set(A, new Value('length'), new Value(k), Value.true));
|
||||
return A;
|
||||
}
|
||||
const nextValue = Q(IteratorValue(next));
|
||||
let mappedValue;
|
||||
if (mapping) {
|
||||
mappedValue = Call(mapfn, thisArg, [nextValue, new Value(k)]);
|
||||
if (mappedValue instanceof AbruptCompletion) {
|
||||
return Q(IteratorClose(iteratorRecord, mappedValue));
|
||||
}
|
||||
mappedValue = mappedValue.Value;
|
||||
} else {
|
||||
mappedValue = nextValue;
|
||||
}
|
||||
const defineStatus = CreateDataPropertyOrThrow(A, Pk, mappedValue);
|
||||
if (defineStatus instanceof AbruptCompletion) {
|
||||
return Q(IteratorClose(iteratorRecord, defineStatus));
|
||||
}
|
||||
k += 1;
|
||||
}
|
||||
}
|
||||
const arrayLike = X(ToObject(items));
|
||||
const len = Q(LengthOfArrayLike(arrayLike));
|
||||
if (IsConstructor(C) === Value.true) {
|
||||
A = Q(Construct(C, [len]));
|
||||
} else {
|
||||
A = Q(ArrayCreate(len));
|
||||
}
|
||||
let k = 0;
|
||||
while (k < len.numberValue()) {
|
||||
const Pk = X(ToString(new Value(k)));
|
||||
const kValue = Q(Get(arrayLike, Pk));
|
||||
let mappedValue;
|
||||
if (mapping === true) {
|
||||
mappedValue = Q(Call(mapfn, thisArg, [kValue, new Value(k)]));
|
||||
} else {
|
||||
mappedValue = kValue;
|
||||
}
|
||||
Q(CreateDataPropertyOrThrow(A, Pk, mappedValue));
|
||||
k += 1;
|
||||
}
|
||||
Q(Set(A, new Value('length'), len, Value.true));
|
||||
return A;
|
||||
}
|
||||
|
||||
// 22.1.2.2 #sec-array.isarray
|
||||
function Array_isArray([arg = Value.undefined]) {
|
||||
return Q(IsArray(arg));
|
||||
}
|
||||
|
||||
// 22.1.2.3 #sec-array.of
|
||||
function Array_of(items, { thisValue }) {
|
||||
const len = items.length;
|
||||
// Let items be the List of arguments passed to this function.
|
||||
const C = thisValue;
|
||||
let A;
|
||||
if (IsConstructor(C) === Value.true) {
|
||||
A = Q(Construct(C, [new Value(len)]));
|
||||
} else {
|
||||
A = Q(ArrayCreate(new Value(len)));
|
||||
}
|
||||
let k = 0;
|
||||
while (k < len) {
|
||||
const kValue = items[k];
|
||||
const Pk = X(ToString(new Value(k)));
|
||||
Q(CreateDataPropertyOrThrow(A, Pk, kValue));
|
||||
k += 1;
|
||||
}
|
||||
Q(Set(A, new Value('length'), new Value(len), Value.true));
|
||||
return A;
|
||||
}
|
||||
|
||||
// 22.1.2.5 #sec-get-array-@@species
|
||||
function Array_speciesGetter(args, { thisValue }) {
|
||||
return thisValue;
|
||||
}
|
||||
|
||||
export function BootstrapArray(realmRec) {
|
||||
const proto = realmRec.Intrinsics['%Array.prototype%'];
|
||||
|
||||
const cons = BootstrapConstructor(realmRec, ArrayConstructor, 'Array', 1, proto, [
|
||||
['from', Array_from, 1],
|
||||
['isArray', Array_isArray, 1],
|
||||
['of', Array_of, 0],
|
||||
[wellKnownSymbols.species, [Array_speciesGetter]],
|
||||
]);
|
||||
|
||||
realmRec.Intrinsics['%Array%'] = cons;
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
import { surroundingAgent } from '../engine.mjs';
|
||||
import { Type, Value, wellKnownSymbols } from '../value.mjs';
|
||||
import { Q } from '../completion.mjs';
|
||||
import { ToIndex, AllocateArrayBuffer } from '../abstract-ops/all.mjs';
|
||||
import { BootstrapConstructor } from './Bootstrap.mjs';
|
||||
|
||||
// #sec-arraybuffer-length
|
||||
function ArrayBufferConstructor([length = Value.undefined], { NewTarget }) {
|
||||
// 1. If NewTarget is undefined, throw a TypeError exception.
|
||||
if (NewTarget === Value.undefined) {
|
||||
return surroundingAgent.Throw('TypeError', 'ConstructorNonCallable', this);
|
||||
}
|
||||
// 2. Let byteLength be ? ToIndex(length).
|
||||
const byteLength = Q(ToIndex(length));
|
||||
// 3. Return ? AllocateArrayBuffer(NewTarget, byteLength).
|
||||
return Q(AllocateArrayBuffer(NewTarget, byteLength));
|
||||
}
|
||||
|
||||
// #sec-arraybuffer.isview
|
||||
function ArrayBuffer_isView([arg = Value.undefined]) {
|
||||
// 1. If Type(arg) is not Object, return false.
|
||||
if (Type(arg) !== 'Object') {
|
||||
return Value.false;
|
||||
}
|
||||
// 2. If arg has a [[ViewedArrayBuffer]] internal slot, return true.
|
||||
if ('ViewedArrayBuffer' in arg) {
|
||||
return Value.true;
|
||||
}
|
||||
// 3. Return false.
|
||||
return Value.false;
|
||||
}
|
||||
|
||||
// #sec-get-arraybuffer-@@species
|
||||
function ArrayBuffer_species(a, { thisValue }) {
|
||||
return thisValue;
|
||||
}
|
||||
|
||||
export function BootstrapArrayBuffer(realmRec) {
|
||||
const c = BootstrapConstructor(realmRec, ArrayBufferConstructor, 'ArrayBuffer', 1, realmRec.Intrinsics['%ArrayBuffer.prototype%'], [
|
||||
['isView', ArrayBuffer_isView, 1],
|
||||
[wellKnownSymbols.species, [ArrayBuffer_species]],
|
||||
]);
|
||||
realmRec.Intrinsics['%ArrayBuffer%'] = c;
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
import { surroundingAgent } from '../engine.mjs';
|
||||
import { Value } from '../value.mjs';
|
||||
import { Q } from '../completion.mjs';
|
||||
import {
|
||||
RequireInternalSlot, IsDetachedBuffer, IsSharedArrayBuffer,
|
||||
SpeciesConstructor, Construct, ToInteger, SameValue, CopyDataBlockBytes,
|
||||
} from '../abstract-ops/all.mjs';
|
||||
import { BootstrapPrototype } from './Bootstrap.mjs';
|
||||
|
||||
// #sec-get-arraybuffer.prototype.bytelength
|
||||
function ArrayBufferProto_byteLength(args, { thisValue }) {
|
||||
// 1. Let O be this value.
|
||||
const O = thisValue;
|
||||
// 2. Perform ? RequireInternalSlot(O, [[ArrayBufferData]]).
|
||||
Q(RequireInternalSlot(O, 'ArrayBufferData'));
|
||||
// 3. If IsSharedArrayBuffer(O) is true, throw a TypeError exception.
|
||||
if (IsSharedArrayBuffer(O) === Value.true) {
|
||||
return surroundingAgent.Throw('TypeError', 'ArrayBufferShared');
|
||||
}
|
||||
// 4. If IsDetachedBuffer(O) is true, throw a TypeError exception.
|
||||
if (IsDetachedBuffer(O) === Value.true) {
|
||||
return surroundingAgent.Throw('TypeError', 'ArrayBufferDetached');
|
||||
}
|
||||
// 5. Let length be O.[[ArrayBufferByteLength]].
|
||||
const length = O.ArrayBufferByteLength;
|
||||
// 6. Return length.
|
||||
return length;
|
||||
}
|
||||
|
||||
// #sec-arraybuffer.prototype.slice
|
||||
function ArrayBufferProto_slice([start = Value.undefined, end = Value.undefined], { thisValue }) {
|
||||
// 1. Let O be the this value.
|
||||
const O = thisValue;
|
||||
// 2. Perform ? RequireInternalSlot(O, [[ArrayBufferData]]).
|
||||
Q(RequireInternalSlot(O, 'ArrayBufferData'));
|
||||
// 3. If IsSharedArrayBuffer(O) is true, throw a TypeError exception.
|
||||
if (IsSharedArrayBuffer(O) === Value.true) {
|
||||
return surroundingAgent.Throw('TypeError', 'ArrayBufferShared');
|
||||
}
|
||||
// 4. If IsDetachedBuffer(O) is true, throw a TypeError exception.
|
||||
if (IsDetachedBuffer(O) === Value.true) {
|
||||
return surroundingAgent.Throw('TypeError', 'ArrayBufferDetached');
|
||||
}
|
||||
// 5. Let len be O.[[ArrayBufferByteLength]].
|
||||
const len = O.ArrayBufferByteLength.numberValue();
|
||||
// 6. Let relativeStart be ? ToInteger(start).
|
||||
const relativeStart = Q(ToInteger(start)).numberValue();
|
||||
let first;
|
||||
// 7. If relativeStart < 0, let first be max((len + relativeStart), 0); else let first be min(relativeStart, len).
|
||||
if (relativeStart < 0) {
|
||||
first = Math.max(len + relativeStart, 0);
|
||||
} else {
|
||||
first = Math.min(relativeStart, len);
|
||||
}
|
||||
let relativeEnd;
|
||||
// 8. If end is undefined, let relativeEnd be len; else let relativeEnd be ? ToInteger(end).
|
||||
if (end === Value.undefined) {
|
||||
relativeEnd = len;
|
||||
} else {
|
||||
relativeEnd = Q(ToInteger(end)).numberValue();
|
||||
}
|
||||
let final;
|
||||
// 9. If relativeEnd < 0, let final be max((len + relativeEnd), 0); else let final be min(relativeEnd, len).
|
||||
if (relativeEnd < 0) {
|
||||
final = Math.max(len + relativeEnd, 0);
|
||||
} else {
|
||||
final = Math.min(relativeEnd, len);
|
||||
}
|
||||
// 10. Let newLen be max(final - first, 0).
|
||||
const newLen = Math.max(final - first, 0);
|
||||
// 11. Let ctor be ? SpeciesConstructor(O, %ArrayBuffer%).
|
||||
const ctor = Q(SpeciesConstructor(O, surroundingAgent.intrinsic('%ArrayBuffer%')));
|
||||
// 12. Let new be ? Construct(ctor, « newLen »).
|
||||
const newO = Q(Construct(ctor, [new Value(newLen)]));
|
||||
// 13. Perform ? RequireInternalSlot(new, [[ArrayBufferData]]).
|
||||
Q(RequireInternalSlot(newO, 'ArrayBufferData'));
|
||||
// 14. If IsSharedArrayBuffer(new) is true, throw a TypeError exception.
|
||||
if (IsSharedArrayBuffer(newO) === Value.true) {
|
||||
return surroundingAgent.Throw('TypeError', 'ArrayBufferShared');
|
||||
}
|
||||
// 15. If IsDetachedBuffer(new) is true, throw a TypeError exception.
|
||||
if (IsDetachedBuffer(newO) === Value.true) {
|
||||
return surroundingAgent.Throe('TypeError', 'ArrayBufferDetached');
|
||||
}
|
||||
// 16. If SameValue(new, O) is true, throw a TypeError exception.
|
||||
if (SameValue(newO, O) === Value.true) {
|
||||
return surroundingAgent.Throw('TypeError', 'SubclassSameValue', newO);
|
||||
}
|
||||
// 17. If new.[[ArrayBufferByteLength]] < newLen, throw a TypeError exception.
|
||||
if (newO.ArrayBufferByteLength.numberValue() < newLen) {
|
||||
return surroundingAgent.Throw('TypeError', 'SubclassLengthTooSmall', newO);
|
||||
}
|
||||
// 18. NOTE: Side-effects of the above steps may have detached O.
|
||||
// 19. If IsDetachedBuffer(O) is true, throw a TypeError exception.
|
||||
if (IsDetachedBuffer(O) === Value.true) {
|
||||
return surroundingAgent.Throw('TypeError', 'ArrayBufferDetached');
|
||||
}
|
||||
// 20. Let fromBuf be O.[[ArrayBufferData]].
|
||||
const fromBuf = O.ArrayBufferData;
|
||||
// 21. Let toBuf be new.[[ArrayBufferData]].
|
||||
const toBuf = newO.ArrayBufferData;
|
||||
// 22. Perform CopyDataBlockBytes(toBuf, 0, fromBuf, first, newLen).
|
||||
CopyDataBlockBytes(toBuf, 0, fromBuf, first, newLen);
|
||||
// 23. Return new.
|
||||
return newO;
|
||||
}
|
||||
|
||||
export function BootstrapArrayBufferPrototype(realmRec) {
|
||||
const proto = BootstrapPrototype(realmRec, [
|
||||
['byteLength', [ArrayBufferProto_byteLength]],
|
||||
['slice', ArrayBufferProto_slice, 2],
|
||||
], realmRec.Intrinsics['%Object.prototype%'], 'ArrayBuffer');
|
||||
|
||||
realmRec.Intrinsics['%ArrayBuffer.prototype%'] = proto;
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
import {
|
||||
surroundingAgent,
|
||||
} from '../engine.mjs';
|
||||
import {
|
||||
Type,
|
||||
Value,
|
||||
} from '../value.mjs';
|
||||
import {
|
||||
Assert,
|
||||
CreateArrayFromList,
|
||||
CreateIterResultObject,
|
||||
Get,
|
||||
IsDetachedBuffer,
|
||||
LengthOfArrayLike,
|
||||
ToString,
|
||||
} from '../abstract-ops/all.mjs';
|
||||
import { Q, X } from '../completion.mjs';
|
||||
import { BootstrapPrototype } from './Bootstrap.mjs';
|
||||
|
||||
// #sec-%arrayiteratorprototype%-object
|
||||
function ArrayIteratorPrototype_next(args, { thisValue }) {
|
||||
// 1. Let O be the this value.
|
||||
const O = thisValue;
|
||||
// 2. If Type(O) is not Object, throw a TypeError exception.
|
||||
if (Type(O) !== 'Object') {
|
||||
return surroundingAgent.Throw('TypeError', 'NotATypeObject', 'Array Iterator', O);
|
||||
}
|
||||
// 3. If O does not have all of the internal slots of an Array Iterator Instance (22.1.5.3), throw a TypeError exception.
|
||||
if (!('IteratedArrayLike' in O)
|
||||
|| !('ArrayLikeNextIndex' in O)
|
||||
|| !('ArrayLikeIterationKind' in O)) {
|
||||
return surroundingAgent.Throw('TypeError', 'NotATypeObject', 'Array Iterator', O);
|
||||
}
|
||||
// 4. Let a be O.[[IteratedArrayLike]].
|
||||
const a = O.IteratedArrayLike;
|
||||
// 5. If a is undefined, return CreateIterResultObject(undefined, true).
|
||||
if (a === Value.undefined) {
|
||||
return CreateIterResultObject(Value.undefined, Value.true);
|
||||
}
|
||||
// 6. Let index be O.[[ArrayLikeNextIndex]].
|
||||
const index = O.ArrayLikeNextIndex;
|
||||
// 7. Let itemKind be O.[[ArrayLikeIterationKind]].
|
||||
const itemKind = O.ArrayLikeIterationKind;
|
||||
let len;
|
||||
// 8. If a has a [[TypedArrayName]] internal slot, then
|
||||
if ('TypedArrayName' in a) {
|
||||
// a. If IsDetachedBuffer(a.[[ViewedArrayBuffer]]) is true, throw a TypeError exception.
|
||||
if (IsDetachedBuffer(a.ViewedArrayBuffer) === Value.true) {
|
||||
return surroundingAgent.Throw('TypeError', 'ArrayBufferDetached');
|
||||
}
|
||||
// b. Let len be a.[[ArrayLength]].
|
||||
len = a.ArrayLength;
|
||||
} else { // 9. Else,
|
||||
// a. Let len be ? LengthOfArrayLike(a).
|
||||
len = Q(LengthOfArrayLike(a));
|
||||
}
|
||||
// 10. If index ≥ len, then
|
||||
if (index >= len.numberValue()) {
|
||||
// a. Set O.[[IteratedArrayLike]] to undefined.
|
||||
O.IteratedArrayLike = Value.undefined;
|
||||
// b. Return CreateIterResultObject(undefined, true).
|
||||
return CreateIterResultObject(Value.undefined, Value.true);
|
||||
}
|
||||
// 11. Set O.[[ArrayLikeNextIndex]] to index + 1.
|
||||
O.ArrayLikeNextIndex = index + 1;
|
||||
// 12. If itemKind is key, return CreateIterResultObject(index, false).
|
||||
if (itemKind === 'key') {
|
||||
return CreateIterResultObject(new Value(index), Value.false);
|
||||
}
|
||||
// 13. Let elementKey be ! ToString(index).
|
||||
const elementKey = X(ToString(new Value(index)));
|
||||
// 14. Let elementValue be ? Get(a, elementKey).
|
||||
const elementValue = Q(Get(a, elementKey));
|
||||
// 15. If itemKind is value, let result be elementValue.
|
||||
let result;
|
||||
// 15. If itemKind is value, let result be elementValue.
|
||||
if (itemKind === 'value') {
|
||||
result = elementValue;
|
||||
} else { // 16. Else,
|
||||
// a. Assert: itemKind is key+value.
|
||||
Assert(itemKind === 'key+value');
|
||||
// b. Let result be ! CreateArrayFromList(« index, elementValue »).
|
||||
result = X(CreateArrayFromList([new Value(index), elementValue]));
|
||||
}
|
||||
// 17. Return CreateIterResultObject(result, false).
|
||||
return CreateIterResultObject(result, Value.false);
|
||||
}
|
||||
|
||||
export function BootstrapArrayIteratorPrototype(realmRec) {
|
||||
const proto = BootstrapPrototype(realmRec, [
|
||||
['next', ArrayIteratorPrototype_next, 0],
|
||||
], realmRec.Intrinsics['%IteratorPrototype%'], 'Array Iterator');
|
||||
|
||||
realmRec.Intrinsics['%ArrayIterator.prototype%'] = proto;
|
||||
}
|
||||
@@ -1,591 +0,0 @@
|
||||
import { surroundingAgent } from '../engine.mjs';
|
||||
import {
|
||||
Descriptor,
|
||||
Type,
|
||||
Value,
|
||||
wellKnownSymbols,
|
||||
} from '../value.mjs';
|
||||
import {
|
||||
ArrayCreate,
|
||||
ArraySpeciesCreate,
|
||||
Assert,
|
||||
Call,
|
||||
CreateArrayIterator,
|
||||
CreateDataProperty,
|
||||
CreateDataPropertyOrThrow,
|
||||
DeletePropertyOrThrow,
|
||||
Get,
|
||||
HasProperty,
|
||||
IsArray,
|
||||
IsCallable,
|
||||
IsConcatSpreadable,
|
||||
Set,
|
||||
SortCompare,
|
||||
LengthOfArrayLike,
|
||||
OrdinaryObjectCreate,
|
||||
ToBoolean,
|
||||
ToInteger,
|
||||
ToObject,
|
||||
ToString,
|
||||
} from '../abstract-ops/all.mjs';
|
||||
import { Q, X } from '../completion.mjs';
|
||||
import { assignProps } from './Bootstrap.mjs';
|
||||
import { ArrayProto_sortBody, BootstrapArrayPrototypeShared } from './ArrayPrototypeShared.mjs';
|
||||
|
||||
// 22.1.3.1 #sec-array.prototype.concat
|
||||
function ArrayProto_concat(args, { thisValue }) {
|
||||
const O = Q(ToObject(thisValue));
|
||||
const A = Q(ArraySpeciesCreate(O, new Value(0)));
|
||||
let n = 0;
|
||||
const items = [O, ...args];
|
||||
while (items.length > 0) {
|
||||
const E = items.shift();
|
||||
const spreadable = Q(IsConcatSpreadable(E));
|
||||
if (spreadable === Value.true) {
|
||||
let k = 0;
|
||||
const len = Q(LengthOfArrayLike(E)).numberValue();
|
||||
if (n + len > (2 ** 53) - 1) {
|
||||
return surroundingAgent.Throw('TypeError', 'ArrayPastSafeLength');
|
||||
}
|
||||
while (k < len) {
|
||||
const P = X(ToString(new Value(k)));
|
||||
const exists = Q(HasProperty(E, P));
|
||||
if (exists === Value.true) {
|
||||
const subElement = Q(Get(E, P));
|
||||
const nStr = X(ToString(new Value(n)));
|
||||
Q(CreateDataPropertyOrThrow(A, nStr, subElement));
|
||||
}
|
||||
n += 1;
|
||||
k += 1;
|
||||
}
|
||||
} else {
|
||||
if (n >= (2 ** 53) - 1) {
|
||||
return surroundingAgent.Throw('TypeError', 'ArrayPastSafeLength');
|
||||
}
|
||||
const nStr = X(ToString(new Value(n)));
|
||||
Q(CreateDataPropertyOrThrow(A, nStr, E));
|
||||
n += 1;
|
||||
}
|
||||
}
|
||||
Q(Set(A, new Value('length'), new Value(n), Value.true));
|
||||
return A;
|
||||
}
|
||||
|
||||
// 22.1.3.3 #sec-array.prototype.copywithin
|
||||
function ArrayProto_copyWithin([target = Value.undefined, start = Value.undefined, end = Value.undefined], { thisValue }) {
|
||||
const O = Q(ToObject(thisValue));
|
||||
const len = Q(LengthOfArrayLike(O));
|
||||
const relativeTarget = Q(ToInteger(target));
|
||||
let to;
|
||||
if (relativeTarget.numberValue() < 0) {
|
||||
to = Math.max(len.numberValue() + relativeTarget.numberValue(), 0);
|
||||
} else {
|
||||
to = Math.min(relativeTarget.numberValue(), len.numberValue());
|
||||
}
|
||||
const relativeStart = Q(ToInteger(start));
|
||||
let from;
|
||||
if (relativeStart.numberValue() < 0) {
|
||||
from = Math.max(len.numberValue() + relativeStart.numberValue(), 0);
|
||||
} else {
|
||||
from = Math.min(relativeStart.numberValue(), len.numberValue());
|
||||
}
|
||||
let relativeEnd;
|
||||
if (end === Value.undefined) {
|
||||
relativeEnd = len;
|
||||
} else {
|
||||
relativeEnd = Q(ToInteger(end));
|
||||
}
|
||||
let final;
|
||||
if (relativeEnd.numberValue() < 0) {
|
||||
final = Math.max(len.numberValue() + relativeEnd.numberValue(), 0);
|
||||
} else {
|
||||
final = Math.min(relativeEnd.numberValue(), len.numberValue());
|
||||
}
|
||||
let count = Math.min(final - from, len.numberValue() - to);
|
||||
let direction;
|
||||
if (from < to && to < from + count) {
|
||||
direction = -1;
|
||||
from += count - 1;
|
||||
to += count - 1;
|
||||
} else {
|
||||
direction = 1;
|
||||
}
|
||||
while (count > 0) {
|
||||
const fromKey = X(ToString(new Value(from)));
|
||||
const toKey = X(ToString(new Value(to)));
|
||||
const fromPresent = Q(HasProperty(O, fromKey));
|
||||
if (fromPresent === Value.true) {
|
||||
const fromVal = Q(Get(O, fromKey));
|
||||
Q(Set(O, toKey, fromVal, Value.true));
|
||||
} else {
|
||||
Q(DeletePropertyOrThrow(O, toKey));
|
||||
}
|
||||
from += direction;
|
||||
to += direction;
|
||||
count -= 1;
|
||||
}
|
||||
return O;
|
||||
}
|
||||
|
||||
// 22.1.3.4 #sec-array.prototype.entries
|
||||
function ArrayProto_entries(args, { thisValue }) {
|
||||
const O = Q(ToObject(thisValue));
|
||||
return CreateArrayIterator(O, 'key+value');
|
||||
}
|
||||
|
||||
// 22.1.3.6 #sec-array.prototype.fill
|
||||
function ArrayProto_fill([value = Value.undefined, start = Value.undefined, end = Value.undefined], { thisValue }) {
|
||||
const O = Q(ToObject(thisValue));
|
||||
const len = Q(LengthOfArrayLike(O)).numberValue();
|
||||
const relativeStart = Q(ToInteger(start)).numberValue();
|
||||
let k;
|
||||
if (relativeStart < 0) {
|
||||
k = Math.max(len + relativeStart, 0);
|
||||
} else {
|
||||
k = Math.min(relativeStart, len);
|
||||
}
|
||||
let relativeEnd;
|
||||
if (Type(end) === 'Undefined') {
|
||||
relativeEnd = len;
|
||||
} else {
|
||||
relativeEnd = Q(ToInteger(end)).numberValue();
|
||||
}
|
||||
let final;
|
||||
if (relativeEnd < 0) {
|
||||
final = Math.max(len + relativeEnd, 0);
|
||||
} else {
|
||||
final = Math.min(relativeEnd, len);
|
||||
}
|
||||
while (k < final) {
|
||||
const Pk = X(ToString(new Value(k)));
|
||||
Q(Set(O, Pk, value, Value.true));
|
||||
k += 1;
|
||||
}
|
||||
return O;
|
||||
}
|
||||
|
||||
// 22.1.3.7 #sec-array.prototype.filter
|
||||
function ArrayProto_filter([callbackfn = Value.undefined, thisArg = Value.undefined], { thisValue }) {
|
||||
const O = Q(ToObject(thisValue));
|
||||
const len = Q(LengthOfArrayLike(O)).numberValue();
|
||||
if (IsCallable(callbackfn) === Value.false) {
|
||||
return surroundingAgent.Throw('TypeError', 'NotAFunction', callbackfn);
|
||||
}
|
||||
const A = Q(ArraySpeciesCreate(O, new Value(0)));
|
||||
let k = 0;
|
||||
let to = 0;
|
||||
while (k < len) {
|
||||
const Pk = X(ToString(new Value(k)));
|
||||
const kPresent = Q(HasProperty(O, Pk));
|
||||
if (kPresent === Value.true) {
|
||||
const kValue = Q(Get(O, Pk));
|
||||
const selected = ToBoolean(Q(Call(callbackfn, thisArg, [kValue, new Value(k), O])));
|
||||
if (selected === Value.true) {
|
||||
Q(CreateDataPropertyOrThrow(A, ToString(new Value(to)), kValue));
|
||||
to += 1;
|
||||
}
|
||||
}
|
||||
k += 1;
|
||||
}
|
||||
return A;
|
||||
}
|
||||
|
||||
// 22.1.3.10.1 #sec-flattenintoarray
|
||||
function FlattenIntoArray(target, source, sourceLen, start, depth, mapperFunction, thisArg) {
|
||||
Assert(Type(target) === 'Object');
|
||||
Assert(Type(source) === 'Object');
|
||||
Assert(sourceLen >= 0);
|
||||
Assert(start >= 0);
|
||||
// Assert: _depth_ is an integer Number, *+∞*, 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,
|
||||
})));
|
||||
}
|
||||
|
||||
// Used in `arguments` objects.
|
||||
realmRec.Intrinsics['%Array.prototype.values%'] = X(Get(proto, new Value('values')));
|
||||
|
||||
realmRec.Intrinsics['%Array.prototype%'] = proto;
|
||||
}
|
||||
@@ -1,561 +0,0 @@
|
||||
import {
|
||||
Assert,
|
||||
Call,
|
||||
DeletePropertyOrThrow,
|
||||
Get,
|
||||
HasOwnProperty,
|
||||
HasProperty,
|
||||
Invoke,
|
||||
IsCallable,
|
||||
SameValueZero,
|
||||
Set,
|
||||
StrictEqualityComparison,
|
||||
ToBoolean,
|
||||
ToInteger,
|
||||
ToLength,
|
||||
ToObject,
|
||||
ToString,
|
||||
} from '../abstract-ops/all.mjs';
|
||||
import { Q, X } from '../completion.mjs';
|
||||
import { surroundingAgent } from '../engine.mjs';
|
||||
import { Type, Value } from '../value.mjs';
|
||||
import { assignProps } from './Bootstrap.mjs';
|
||||
|
||||
// Algorithms and methods shared between %Array.prototype% and
|
||||
// %TypedArray.prototype%.
|
||||
|
||||
// 22.1.3.27 #sec-array.prototype.sort
|
||||
// 22.2.3.26 #sec-%typedarray%.prototype.sort
|
||||
//
|
||||
// If internalMethodsRestricted is true, then Asserts are used to ensure that
|
||||
// "The only internal methods of the this object that the algorithm may call
|
||||
// are [[Get]] and [[Set]]," a requirement of %TypedArray%.prototype.sort.
|
||||
export function ArrayProto_sortBody(obj, len, SortCompare, internalMethodsRestricted = false) {
|
||||
len = len.numberValue();
|
||||
|
||||
// Collect all elements. Count how many holes we have for error checking.
|
||||
const collected = [];
|
||||
let holes = 0;
|
||||
for (let k = 0; k < len; k += 1) {
|
||||
const curProp = X(ToString(new Value(k)));
|
||||
const prop = Q(Get(obj, curProp));
|
||||
if (prop === Value.undefined) {
|
||||
Assert(!internalMethodsRestricted);
|
||||
const hasOwn = Q(HasOwnProperty(obj, curProp));
|
||||
if (hasOwn === Value.false) {
|
||||
holes += 1;
|
||||
} else {
|
||||
collected.push(prop);
|
||||
}
|
||||
} else {
|
||||
collected.push(prop);
|
||||
}
|
||||
}
|
||||
if (internalMethodsRestricted) {
|
||||
Assert(holes === 0);
|
||||
}
|
||||
Assert(collected.length + holes === len);
|
||||
|
||||
// Get rid of holes by deleting properties at the end.
|
||||
// See Note 1: Because non-existent property values always compare greater
|
||||
// than undefined property values, and undefined always compares greater
|
||||
// than any other value, undefined property values always sort to the end
|
||||
// of the result, followed by non-existent property values.
|
||||
for (let k = collected.length; k < len; k += 1) {
|
||||
const curProp = X(ToString(new Value(k)));
|
||||
Q(DeletePropertyOrThrow(obj, curProp));
|
||||
}
|
||||
|
||||
// Mergesort.
|
||||
const lBuffer = [];
|
||||
const rBuffer = [];
|
||||
for (let step = 1; step < collected.length; step *= 2) {
|
||||
for (let start = 0; start < collected.length - 1; start += 2 * step) {
|
||||
const sizeLeft = step;
|
||||
const mid = start + sizeLeft;
|
||||
const sizeRight = Math.min(step, collected.length - mid);
|
||||
if (sizeRight < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Merge.
|
||||
for (let l = 0; l < sizeLeft; l += 1) {
|
||||
lBuffer[l] = collected[start + l];
|
||||
}
|
||||
for (let r = 0; r < sizeRight; r += 1) {
|
||||
rBuffer[r] = collected[mid + r];
|
||||
}
|
||||
|
||||
{
|
||||
let l = 0;
|
||||
let r = 0;
|
||||
let o = start;
|
||||
while (l < sizeLeft && r < sizeRight) {
|
||||
const cmp = Q(SortCompare(lBuffer[l], rBuffer[r])).numberValue();
|
||||
if (cmp <= 0) {
|
||||
collected[o] = lBuffer[l];
|
||||
o += 1;
|
||||
l += 1;
|
||||
} else {
|
||||
collected[o] = rBuffer[r];
|
||||
o += 1;
|
||||
r += 1;
|
||||
}
|
||||
}
|
||||
while (l < sizeLeft) {
|
||||
collected[o] = lBuffer[l];
|
||||
o += 1;
|
||||
l += 1;
|
||||
}
|
||||
while (r < sizeRight) {
|
||||
collected[o] = rBuffer[r];
|
||||
o += 1;
|
||||
r += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Copy the sorted results back to the array.
|
||||
for (let k = 0; k < collected.length; k += 1) {
|
||||
const curProp = X(ToString(new Value(k)));
|
||||
Q(Set(obj, curProp, collected[k], Value.true));
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
export function BootstrapArrayPrototypeShared(realmRec, proto, priorToEvaluatingAlgorithm, objectToLength) {
|
||||
// 22.1.3.5 #sec-array.prototype.every
|
||||
// 22.2.3.7 #sec-%typedarray%.prototype.every
|
||||
function ArrayProto_every([callbackFn = Value.undefined, thisArg = Value.undefined], { thisValue }) {
|
||||
Q(priorToEvaluatingAlgorithm(thisValue));
|
||||
const O = Q(ToObject(thisValue));
|
||||
const lenProp = Q(objectToLength(O));
|
||||
const len = Q(ToLength(lenProp));
|
||||
if (IsCallable(callbackFn) === Value.false) {
|
||||
return surroundingAgent.Throw('TypeError', 'NotAFunction', callbackFn);
|
||||
}
|
||||
let k = 0;
|
||||
while (k < len.numberValue()) {
|
||||
const Pk = X(ToString(new Value(k)));
|
||||
const kPresent = Q(HasProperty(O, Pk));
|
||||
if (kPresent === Value.true) {
|
||||
const kValue = Q(Get(O, Pk));
|
||||
const testResult = ToBoolean(Q(Call(callbackFn, thisArg, [kValue, new Value(k), O])));
|
||||
if (testResult === Value.false) {
|
||||
return Value.false;
|
||||
}
|
||||
}
|
||||
k += 1;
|
||||
}
|
||||
return Value.true;
|
||||
}
|
||||
|
||||
// 22.1.3.8 #sec-array.prototype.find
|
||||
// 22.2.3.10 #sec-%typedarray%.prototype.find
|
||||
function ArrayProto_find([predicate = Value.undefined, thisArg = Value.undefined], { thisValue }) {
|
||||
Q(priorToEvaluatingAlgorithm(thisValue));
|
||||
const O = Q(ToObject(thisValue));
|
||||
const lenProp = Q(objectToLength(O));
|
||||
const len = Q(ToLength(lenProp)).numberValue();
|
||||
if (IsCallable(predicate) === Value.false) {
|
||||
return surroundingAgent.Throw('TypeError', 'NotAFunction', predicate);
|
||||
}
|
||||
let k = 0;
|
||||
while (k < len) {
|
||||
const Pk = X(ToString(new Value(k)));
|
||||
const kValue = Q(Get(O, Pk));
|
||||
const testResult = ToBoolean(Q(Call(predicate, thisArg, [kValue, new Value(k), O])));
|
||||
if (testResult === Value.true) {
|
||||
return kValue;
|
||||
}
|
||||
k += 1;
|
||||
}
|
||||
return Value.undefined;
|
||||
}
|
||||
|
||||
// 22.1.3.9 #sec-array.prototype.findindex
|
||||
// 22.2.3.11 #sec-%typedarray%.prototype.findindex
|
||||
function ArrayProto_findIndex([predicate = Value.undefined, thisArg = Value.undefined], { thisValue }) {
|
||||
Q(priorToEvaluatingAlgorithm(thisValue));
|
||||
const O = Q(ToObject(thisValue));
|
||||
const lenProp = Q(objectToLength(O));
|
||||
const len = Q(ToLength(lenProp)).numberValue();
|
||||
if (IsCallable(predicate) === Value.false) {
|
||||
return surroundingAgent.Throw('TypeError', 'NotAFunction', predicate);
|
||||
}
|
||||
let k = 0;
|
||||
while (k < len) {
|
||||
const Pk = X(ToString(new Value(k)));
|
||||
const kValue = Q(Get(O, Pk));
|
||||
const testResult = ToBoolean(Q(Call(predicate, thisArg, [kValue, new Value(k), O])));
|
||||
if (testResult === Value.true) {
|
||||
return new Value(k);
|
||||
}
|
||||
k += 1;
|
||||
}
|
||||
return new Value(-1);
|
||||
}
|
||||
|
||||
// 22.1.3.12 #sec-array.prototype.foreach
|
||||
// 22.2.3.12 #sec-%typedarray%.prototype.foreach
|
||||
function ArrayProto_forEach([callbackfn = Value.undefined, thisArg = Value.undefined], { thisValue }) {
|
||||
Q(priorToEvaluatingAlgorithm(thisValue));
|
||||
const O = Q(ToObject(thisValue));
|
||||
const lenProp = Q(objectToLength(O));
|
||||
const len = Q(ToLength(lenProp)).numberValue();
|
||||
if (IsCallable(callbackfn) === Value.false) {
|
||||
return surroundingAgent.Throw('TypeError', 'NotAFunction', callbackfn);
|
||||
}
|
||||
let k = 0;
|
||||
while (k < len) {
|
||||
const Pk = X(ToString(new Value(k)));
|
||||
const kPresent = Q(HasProperty(O, Pk));
|
||||
if (kPresent === Value.true) {
|
||||
const kValue = Q(Get(O, Pk));
|
||||
Q(Call(callbackfn, thisArg, [kValue, new Value(k), O]));
|
||||
}
|
||||
k += 1;
|
||||
}
|
||||
return Value.undefined;
|
||||
}
|
||||
|
||||
// 22.1.3.13 #sec-array.prototype.includes
|
||||
// 22.2.3.13 #sec-%typedarray%.prototype.includes
|
||||
function ArrayProto_includes([searchElement = Value.undefined, fromIndex = Value.undefined], { thisValue }) {
|
||||
Q(priorToEvaluatingAlgorithm(thisValue));
|
||||
const O = Q(ToObject(thisValue));
|
||||
const lenProp = Q(objectToLength(O));
|
||||
const len = Q(ToLength(lenProp)).numberValue();
|
||||
if (len === 0) {
|
||||
return Value.false;
|
||||
}
|
||||
const n = Q(ToInteger(fromIndex)).numberValue();
|
||||
if (fromIndex === Value.undefined) {
|
||||
Assert(n === 0);
|
||||
}
|
||||
let k;
|
||||
if (n >= 0) {
|
||||
k = n;
|
||||
} else {
|
||||
k = len + n;
|
||||
if (k < 0) {
|
||||
k = 0;
|
||||
}
|
||||
}
|
||||
while (k < len) {
|
||||
const kStr = X(ToString(new Value(k)));
|
||||
const elementK = Q(Get(O, kStr));
|
||||
if (SameValueZero(searchElement, elementK) === Value.true) {
|
||||
return Value.true;
|
||||
}
|
||||
k += 1;
|
||||
}
|
||||
return Value.false;
|
||||
}
|
||||
|
||||
// 22.1.3.14 #sec-array.prototype.indexof
|
||||
// 22.2.3.14 #sec-%typedarray%.prototype.indexof
|
||||
function ArrayProto_indexOf([searchElement = Value.undefined, fromIndex = Value.undefined], { thisValue }) {
|
||||
Q(priorToEvaluatingAlgorithm(thisValue));
|
||||
const O = Q(ToObject(thisValue));
|
||||
const lenProp = Q(objectToLength(O));
|
||||
const len = Q(ToLength(lenProp)).numberValue();
|
||||
if (len === 0) {
|
||||
return new Value(-1);
|
||||
}
|
||||
const n = Q(ToInteger(fromIndex)).numberValue();
|
||||
if (fromIndex === Value.undefined) {
|
||||
Assert(n === 0);
|
||||
}
|
||||
if (n >= len) {
|
||||
return new Value(-1);
|
||||
}
|
||||
let k;
|
||||
if (n >= 0) {
|
||||
k = n;
|
||||
} else {
|
||||
k = len + n;
|
||||
if (k < 0) {
|
||||
k = 0;
|
||||
}
|
||||
}
|
||||
while (k < len) {
|
||||
const kStr = X(ToString(new Value(k)));
|
||||
const kPresent = Q(HasProperty(O, kStr));
|
||||
if (kPresent === Value.true) {
|
||||
const elementK = Q(Get(O, kStr));
|
||||
const same = StrictEqualityComparison(searchElement, elementK);
|
||||
if (same === Value.true) {
|
||||
return new Value(k);
|
||||
}
|
||||
}
|
||||
k += 1;
|
||||
}
|
||||
return new Value(-1);
|
||||
}
|
||||
|
||||
// 22.1.3.15 #sec-array.prototype.join
|
||||
// 22.2.3.15 #sec-%typedarray%.prototype.join
|
||||
function ArrayProto_join([separator = Value.undefined], { thisValue }) {
|
||||
Q(priorToEvaluatingAlgorithm(thisValue));
|
||||
const O = Q(ToObject(thisValue));
|
||||
const lenProp = Q(objectToLength(O));
|
||||
const len = Q(ToLength(lenProp)).numberValue();
|
||||
let sep;
|
||||
if (Type(separator) === 'Undefined') {
|
||||
sep = ',';
|
||||
} else {
|
||||
sep = Q(ToString(separator)).stringValue();
|
||||
}
|
||||
let R = '';
|
||||
let k = 0;
|
||||
while (k < len) {
|
||||
if (k > 0) {
|
||||
R = `${R}${sep}`;
|
||||
}
|
||||
const kStr = X(ToString(new Value(k)));
|
||||
const element = Q(Get(O, kStr));
|
||||
let next;
|
||||
if (Type(element) === 'Undefined' || Type(element) === 'Null') {
|
||||
next = '';
|
||||
} else {
|
||||
next = Q(ToString(element)).stringValue();
|
||||
}
|
||||
R = `${R}${next}`;
|
||||
k += 1;
|
||||
}
|
||||
return new Value(R);
|
||||
}
|
||||
|
||||
// 22.1.3.17 #sec-array.prototype.lastindexof
|
||||
// 22.2.3.17 #sec-%typedarray%.prototype.lastindexof
|
||||
function ArrayProto_lastIndexOf([searchElement = Value.undefined, fromIndex], { thisValue }) {
|
||||
Q(priorToEvaluatingAlgorithm(thisValue));
|
||||
const O = Q(ToObject(thisValue));
|
||||
const lenProp = Q(objectToLength(O));
|
||||
const len = Q(ToLength(lenProp)).numberValue();
|
||||
if (len === 0) {
|
||||
return new Value(-1);
|
||||
}
|
||||
let n;
|
||||
if (fromIndex !== undefined) {
|
||||
n = Q(ToInteger(fromIndex)).numberValue();
|
||||
} else {
|
||||
n = len - 1;
|
||||
}
|
||||
let k;
|
||||
if (n >= 0) {
|
||||
k = Math.min(n, len - 1);
|
||||
} else {
|
||||
k = len + n;
|
||||
}
|
||||
while (k >= 0) {
|
||||
const kStr = X(ToString(new Value(k)));
|
||||
const kPresent = Q(HasProperty(O, kStr));
|
||||
if (kPresent === Value.true) {
|
||||
const elementK = Q(Get(O, kStr));
|
||||
const same = StrictEqualityComparison(searchElement, elementK);
|
||||
if (same === Value.true) {
|
||||
return new Value(k);
|
||||
}
|
||||
}
|
||||
k -= 1;
|
||||
}
|
||||
return new Value(-1);
|
||||
}
|
||||
|
||||
// 22.1.3.21 #sec-array.prototype.reduce
|
||||
// 22.2.3.20 #sec-%typedarray%.prototype.reduce
|
||||
function ArrayProto_reduce([callbackfn = Value.undefined, initialValue], { thisValue }) {
|
||||
Q(priorToEvaluatingAlgorithm(thisValue));
|
||||
const O = Q(ToObject(thisValue));
|
||||
const lenProp = Q(objectToLength(O));
|
||||
const len = Q(ToLength(lenProp)).numberValue();
|
||||
if (IsCallable(callbackfn) === Value.false) {
|
||||
return surroundingAgent.Throw('TypeError', 'NotAFunction', callbackfn);
|
||||
}
|
||||
if (len === 0 && initialValue === undefined) {
|
||||
return surroundingAgent.Throw('TypeError', 'ArrayEmptyReduce');
|
||||
}
|
||||
let k = 0;
|
||||
let accumulator = Value.undefined;
|
||||
if (initialValue !== undefined) {
|
||||
accumulator = initialValue;
|
||||
} else {
|
||||
let kPresent = false;
|
||||
while (kPresent === false && k < len) {
|
||||
const Pk = X(ToString(new Value(k)));
|
||||
kPresent = Q(HasProperty(O, Pk)) === Value.true;
|
||||
if (kPresent === true) {
|
||||
accumulator = Q(Get(O, Pk));
|
||||
}
|
||||
k += 1;
|
||||
}
|
||||
if (kPresent === false) {
|
||||
return surroundingAgent.Throw('TypeError', 'ArrayEmptyReduce');
|
||||
}
|
||||
}
|
||||
while (k < len) {
|
||||
const Pk = X(ToString(new Value(k)));
|
||||
const kPresent = Q(HasProperty(O, Pk));
|
||||
if (kPresent === Value.true) {
|
||||
const kValue = Q(Get(O, Pk));
|
||||
accumulator = Q(Call(callbackfn, Value.undefined, [accumulator, kValue, new Value(k), O]));
|
||||
}
|
||||
k += 1;
|
||||
}
|
||||
return accumulator;
|
||||
}
|
||||
|
||||
// 22.1.3.22 #sec-array.prototype.reduceright
|
||||
// 22.2.3.21 #sec-%typedarray%.prototype.reduceright
|
||||
function ArrayProto_reduceRight([callbackfn = Value.undefined, initialValue], { thisValue }) {
|
||||
Q(priorToEvaluatingAlgorithm(thisValue));
|
||||
const O = Q(ToObject(thisValue));
|
||||
const lenProp = Q(objectToLength(O));
|
||||
const len = Q(ToLength(lenProp)).numberValue();
|
||||
if (IsCallable(callbackfn) === Value.false) {
|
||||
return surroundingAgent.Throw('TypeError', 'NotAFunction', callbackfn);
|
||||
}
|
||||
if (len === 0 && initialValue === undefined) {
|
||||
return surroundingAgent.Throw('TypeError', 'ArrayEmptyReduce');
|
||||
}
|
||||
let k = len - 1;
|
||||
let accumulator = Value.undefined;
|
||||
if (initialValue !== undefined) {
|
||||
accumulator = initialValue;
|
||||
} else {
|
||||
let kPresent = false;
|
||||
while (kPresent === false && k >= 0) {
|
||||
const Pk = X(ToString(new Value(k)));
|
||||
kPresent = Q(HasProperty(O, Pk)) === Value.true;
|
||||
if (kPresent === true) {
|
||||
accumulator = Q(Get(O, Pk));
|
||||
}
|
||||
k -= 1;
|
||||
}
|
||||
if (kPresent === false) {
|
||||
return surroundingAgent.Throw('TypeError', 'ArrayEmptyReduce');
|
||||
}
|
||||
}
|
||||
while (k >= 0) {
|
||||
const Pk = X(ToString(new Value(k)));
|
||||
const kPresent = Q(HasProperty(O, Pk));
|
||||
if (kPresent === Value.true) {
|
||||
const kValue = Q(Get(O, Pk));
|
||||
accumulator = Q(Call(callbackfn, Value.undefined, [accumulator, kValue, new Value(k), O]));
|
||||
}
|
||||
k -= 1;
|
||||
}
|
||||
return accumulator;
|
||||
}
|
||||
|
||||
// 22.1.3.23 #sec-array.prototype.reverse
|
||||
// 22.2.3.22 #sec-%typedarray%.prototype.reverse
|
||||
function ArrayProto_reverse(args, { thisValue }) {
|
||||
Q(priorToEvaluatingAlgorithm(thisValue));
|
||||
const O = Q(ToObject(thisValue));
|
||||
const lenProp = Q(objectToLength(O));
|
||||
const len = Q(ToLength(lenProp)).numberValue();
|
||||
const middle = Math.floor(len / 2);
|
||||
let lower = 0;
|
||||
while (lower !== middle) {
|
||||
const upper = len - lower - 1;
|
||||
const upperP = X(ToString(new Value(upper)));
|
||||
const lowerP = X(ToString(new Value(lower)));
|
||||
const lowerExists = Q(HasProperty(O, lowerP));
|
||||
let lowerValue;
|
||||
let upperValue;
|
||||
if (lowerExists === Value.true) {
|
||||
lowerValue = Q(Get(O, lowerP));
|
||||
}
|
||||
const upperExists = Q(HasProperty(O, upperP));
|
||||
if (upperExists === Value.true) {
|
||||
upperValue = Q(Get(O, upperP));
|
||||
}
|
||||
if (lowerExists === Value.true && upperExists === Value.true) {
|
||||
Q(Set(O, lowerP, upperValue, Value.true));
|
||||
Q(Set(O, upperP, lowerValue, Value.true));
|
||||
} else if (lowerExists === Value.false && upperExists === Value.true) {
|
||||
Q(Set(O, lowerP, upperValue, Value.true));
|
||||
Q(DeletePropertyOrThrow(O, upperP));
|
||||
} else if (lowerExists === Value.true && upperExists === Value.false) {
|
||||
Q(DeletePropertyOrThrow(O, lowerP));
|
||||
Q(Set(O, upperP, lowerValue, Value.true));
|
||||
} else {
|
||||
// no further action is required
|
||||
}
|
||||
lower += 1;
|
||||
}
|
||||
return O;
|
||||
}
|
||||
|
||||
// 22.1.3.26 #sec-array.prototype.some
|
||||
// 22.2.3.25 #sec-%typedarray%.prototype.some
|
||||
function ArrayProto_some([callbackfn = Value.undefined, thisArg = Value.undefined], { thisValue }) {
|
||||
Q(priorToEvaluatingAlgorithm(thisValue));
|
||||
const O = Q(ToObject(thisValue));
|
||||
const lenProp = Q(objectToLength(O));
|
||||
const len = Q(ToLength(lenProp)).numberValue();
|
||||
if (IsCallable(callbackfn) === Value.false) {
|
||||
return surroundingAgent.Throw('TypeError', 'NotAFunction', callbackfn);
|
||||
}
|
||||
let k = 0;
|
||||
while (k < len) {
|
||||
const Pk = X(ToString(new Value(k)));
|
||||
const kPresent = Q(HasProperty(O, Pk));
|
||||
if (kPresent === Value.true) {
|
||||
const kValue = Q(Get(O, Pk));
|
||||
const testResult = ToBoolean(Q(Call(callbackfn, thisArg, [kValue, new Value(k), O])));
|
||||
if (testResult === Value.true) {
|
||||
return Value.true;
|
||||
}
|
||||
}
|
||||
k += 1;
|
||||
}
|
||||
return Value.false;
|
||||
}
|
||||
|
||||
// 22.1.3.29 #sec-array.prototype.tolocalestring
|
||||
// 22.2.3.28 #sec-%typedarray%.prototype.tolocalestring
|
||||
function ArrayProto_toLocaleString(args, { thisValue }) {
|
||||
Q(priorToEvaluatingAlgorithm(thisValue));
|
||||
const array = Q(ToObject(thisValue));
|
||||
const lenProp = Q(objectToLength(array));
|
||||
const len = Q(ToLength(lenProp)).numberValue();
|
||||
const separator = ', ';
|
||||
let R = '';
|
||||
let k = 0;
|
||||
while (k < len) {
|
||||
if (k > 0) {
|
||||
R = `${R}${separator}`;
|
||||
}
|
||||
const kStr = X(ToString(new Value(k)));
|
||||
const nextElement = Q(Get(array, kStr));
|
||||
if (nextElement !== Value.undefined && nextElement !== Value.null) {
|
||||
const S = Q(ToString(Q(Invoke(nextElement, new Value('toLocaleString'))))).stringValue();
|
||||
R = `${R}${S}`;
|
||||
}
|
||||
k += 1;
|
||||
}
|
||||
return new Value(R);
|
||||
}
|
||||
|
||||
assignProps(realmRec, proto, [
|
||||
['every', ArrayProto_every, 1],
|
||||
['find', ArrayProto_find, 1],
|
||||
['findIndex', ArrayProto_findIndex, 1],
|
||||
['forEach', ArrayProto_forEach, 1],
|
||||
['includes', ArrayProto_includes, 1],
|
||||
['indexOf', ArrayProto_indexOf, 1],
|
||||
['join', ArrayProto_join, 1],
|
||||
['lastIndexOf', ArrayProto_lastIndexOf, 1],
|
||||
['reduce', ArrayProto_reduce, 1],
|
||||
['reduceRight', ArrayProto_reduceRight, 1],
|
||||
['reverse', ArrayProto_reverse, 0],
|
||||
['some', ArrayProto_some, 1],
|
||||
['toLocaleString', ArrayProto_toLocaleString, 0],
|
||||
]);
|
||||
}
|
||||
@@ -1,140 +0,0 @@
|
||||
import { surroundingAgent } from '../engine.mjs';
|
||||
import {
|
||||
AsyncFromSyncIteratorContinuation,
|
||||
Call,
|
||||
CreateIterResultObject,
|
||||
GetMethod,
|
||||
IteratorNext,
|
||||
NewPromiseCapability,
|
||||
Assert,
|
||||
} from '../abstract-ops/all.mjs';
|
||||
import { Type, Value } from '../value.mjs';
|
||||
import { IfAbruptRejectPromise, X } from '../completion.mjs';
|
||||
import { BootstrapPrototype } from './Bootstrap.mjs';
|
||||
|
||||
// #sec-%asyncfromsynciteratorprototype%.next
|
||||
function AsyncFromSyncIteratorPrototype_next([value], { thisValue }) {
|
||||
// 1. Let O be the this value.
|
||||
const O = thisValue;
|
||||
// 2. Assert: Type(O) is Object and O has a [[SyncIteratorRecord]] internal slot.
|
||||
Assert(Type(O) === 'Object' && 'SyncIteratorRecord' in O);
|
||||
// 3. Let promiseCapability be ! NewPromiseCapability(%Promise%).
|
||||
const promiseCapability = X(NewPromiseCapability(surroundingAgent.intrinsic('%Promise%')));
|
||||
// 4. Let syncIteratorRecord be O.[[SyncIteratorRecord]].
|
||||
const syncIteratorRecord = O.SyncIteratorRecord;
|
||||
// 5. If value is present, then
|
||||
let result;
|
||||
if (value !== undefined) {
|
||||
// a. Let result be IteratorNext(syncIteratorRecord, value).
|
||||
result = IteratorNext(syncIteratorRecord, value);
|
||||
} else { // 6. Else,
|
||||
// a. Let result be IteratorNext(syncIteratorRecord).
|
||||
result = IteratorNext(syncIteratorRecord);
|
||||
}
|
||||
// 7. IfAbruptRejectPromise(result, promiseCapability).
|
||||
IfAbruptRejectPromise(result, promiseCapability);
|
||||
// 8. Return ! AsyncFromSyncIteratorContinuation(result, promiseCapability).
|
||||
return X(AsyncFromSyncIteratorContinuation(result, promiseCapability));
|
||||
}
|
||||
|
||||
// #sec-%asyncfromsynciteratorprototype%.return
|
||||
function AsyncFromSyncIteratorPrototype_return([value], { thisValue }) {
|
||||
// 1. Let O be the this value.
|
||||
const O = thisValue;
|
||||
// 2. Assert: Type(O) is Object and O has a [[SyncIteratorRecord]] internal slot.
|
||||
Assert(Type(O) === 'Object' && 'SyncIteratorRecord' in O);
|
||||
// 3. Let promiseCapability be ! NewPromiseCapability(%Promise%).
|
||||
const promiseCapability = X(NewPromiseCapability(surroundingAgent.intrinsic('%Promise%')));
|
||||
// 4. Let syncIterator be O.[[SyncIteratorRecord]].[[Iterator]].
|
||||
const syncIterator = O.SyncIteratorRecord.Iterator;
|
||||
// 5. Let return be GetMethod(syncIterator, "return").
|
||||
const ret = GetMethod(syncIterator, new Value('return'));
|
||||
// 6. IfAbruptRejectPromise(return, promiseCapability).
|
||||
IfAbruptRejectPromise(ret, promiseCapability);
|
||||
// 7. If return is undefined, then
|
||||
if (ret === Value.undefined) {
|
||||
// a. Let iterResult be ! CreateIterResultObject(value, true).
|
||||
const iterResult = X(CreateIterResultObject(value, Value.true));
|
||||
// b. Perform ! Call(promiseCapability.[[Resolve]], undefined, « iterResult »).
|
||||
X(Call(promiseCapability.Resolve, Value.undefined, [iterResult]));
|
||||
// c. Return promiseCapability.[[Promise]].
|
||||
return promiseCapability.Promise;
|
||||
}
|
||||
// 8. If value is present, then
|
||||
let result;
|
||||
if (value !== undefined) {
|
||||
// a. Let result be Call(return, syncIterator, « value »).
|
||||
result = Call(ret, syncIterator, [value]);
|
||||
} else { // 9. Else,
|
||||
// a. Let result be Call(return, syncIterator).
|
||||
result = Call(ret, syncIterator);
|
||||
}
|
||||
// 10. IfAbruptRejectPromise(result, promiseCapability).
|
||||
IfAbruptRejectPromise(result, promiseCapability);
|
||||
// 11. If Type(result) is not Object, then
|
||||
if (Type(result) !== 'Object') {
|
||||
// a. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).
|
||||
X(Call(promiseCapability.Reject, Value.undefined, [
|
||||
surroundingAgent.Throw('TypeError', 'NotAnObject', result).Value,
|
||||
]));
|
||||
// b. Return promiseCapability.[[Promise]].
|
||||
return promiseCapability.Promise;
|
||||
}
|
||||
// 12. Return ! AsyncFromSyncIteratorContinuation(result, promiseCapability).
|
||||
return X(AsyncFromSyncIteratorContinuation(result, promiseCapability));
|
||||
}
|
||||
|
||||
// #sec-%asyncfromsynciteratorprototype%.throw
|
||||
function AsyncFromSyncIteratorPrototype_throw([value], { thisValue }) {
|
||||
// 1. Let O be this value.
|
||||
const O = thisValue;
|
||||
// 2. Assert: Type(O) is Object and O has a [[SyncIteratorRecord]] internal slot.
|
||||
Assert(Type(O) === 'Object' && 'SyncIteratorRecord' in O);
|
||||
// 3. Let promiseCapability be ! NewPromiseCapability(%Promise%).
|
||||
const promiseCapability = X(NewPromiseCapability(surroundingAgent.intrinsic('%Promise%')));
|
||||
// 4. Let syncIterator be O.[[SyncIteratorRecord]].[[Iterator]].
|
||||
const syncIterator = O.SyncIteratorRecord.Iterator;
|
||||
// 5. Let throw be GetMethod(syncIterator, "throw").
|
||||
const thr = GetMethod(syncIterator, new Value('throw'));
|
||||
// 6. IfAbruptRejectPromise(throw, promiseCapability).
|
||||
IfAbruptRejectPromise(thr, promiseCapability);
|
||||
// 7. If throw is undefined, then
|
||||
if (thr === Value.undefined) {
|
||||
// a. Perform ! Call(promiseCapability.[[Reject]], undefined, « value »).
|
||||
X(Call(promiseCapability.Reject, Value.undefined, [value]));
|
||||
// b. Return promiseCapability.[[Promise]].
|
||||
return promiseCapability.Promise;
|
||||
}
|
||||
// 8. If value is present, then
|
||||
let result;
|
||||
if (value !== undefined) {
|
||||
// a. Let result be Call(throw, syncIterator, « value »).
|
||||
result = Call(thr, syncIterator, [value]);
|
||||
} else { // 9. Else,
|
||||
// a. Let result be Call(throw, syncIterator).
|
||||
result = Call(thr, syncIterator);
|
||||
}
|
||||
// 10. IfAbruptRejectPromise(result, promiseCapability).
|
||||
IfAbruptRejectPromise(result, promiseCapability);
|
||||
// 11. If Type(result) is not Object, then
|
||||
if (Type(result) !== 'Object') {
|
||||
// a. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).
|
||||
X(Call(promiseCapability.Reject, Value.undefined, [
|
||||
surroundingAgent.Throw('TypeError', 'NotAnObject', result).Value,
|
||||
]));
|
||||
// b. Return promiseCapability.[[Promise]].
|
||||
return promiseCapability.Promise;
|
||||
}
|
||||
// 12. Return ! AsyncFromSyncIteratorContinuation(result, promiseCapability).
|
||||
return X(AsyncFromSyncIteratorContinuation(result, promiseCapability));
|
||||
}
|
||||
|
||||
export function BootstrapAsyncFromSyncIteratorPrototype(realmRec) {
|
||||
const proto = BootstrapPrototype(realmRec, [
|
||||
['next', AsyncFromSyncIteratorPrototype_next, 0],
|
||||
['return', AsyncFromSyncIteratorPrototype_return, 0],
|
||||
['throw', AsyncFromSyncIteratorPrototype_throw, 0],
|
||||
], realmRec.Intrinsics['%AsyncIteratorPrototype%']);
|
||||
|
||||
realmRec.Intrinsics['%AsyncFromSyncIteratorPrototype%'] = proto;
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
import { surroundingAgent } from '../engine.mjs';
|
||||
import { Q } from '../completion.mjs';
|
||||
import { CreateDynamicFunction } from '../runtime-semantics/all.mjs';
|
||||
import { Descriptor, Value } from '../value.mjs';
|
||||
import { BootstrapConstructor } from './Bootstrap.mjs';
|
||||
|
||||
// #sec-async-function-constructor-arguments
|
||||
function AsyncFunctionConstructor(args, { NewTarget }) {
|
||||
// 1. Let C be the active function object.
|
||||
const C = surroundingAgent.activeFunctionObject;
|
||||
// 2. Let args be the argumentsList that was passed to this function by [[Call]] or [[Construct]].
|
||||
// 3. Return CreateDynamicFunction(C, NewTarget, async, args).
|
||||
return Q(CreateDynamicFunction(C, NewTarget, 'async', args));
|
||||
}
|
||||
|
||||
export function BootstrapAsyncFunction(realmRec) {
|
||||
const cons = BootstrapConstructor(realmRec, AsyncFunctionConstructor, 'AsyncFunction', 1, realmRec.Intrinsics['%AsyncFunction.prototype%'], []);
|
||||
|
||||
cons.DefineOwnProperty(new Value('prototype'), Descriptor({
|
||||
Writable: Value.false,
|
||||
Enumerable: Value.false,
|
||||
Configurable: Value.false,
|
||||
}));
|
||||
|
||||
cons.Prototype = realmRec.Intrinsics['%Function%'];
|
||||
|
||||
realmRec.Intrinsics['%AsyncFunction%'] = cons;
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
import { BootstrapPrototype } from './Bootstrap.mjs';
|
||||
|
||||
export function BootstrapAsyncFunctionPrototype(realmRec) {
|
||||
const proto = BootstrapPrototype(realmRec, [], realmRec.Intrinsics['%Function.prototype%'], 'AsyncFunction');
|
||||
|
||||
realmRec.Intrinsics['%AsyncFunction.prototype%'] = proto;
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
import { X } from '../completion.mjs';
|
||||
import { Descriptor, Value } from '../value.mjs';
|
||||
import { BootstrapPrototype } from './Bootstrap.mjs';
|
||||
|
||||
export function BootstrapAsyncGenerator(realmRec) {
|
||||
const proto = BootstrapPrototype(realmRec, [
|
||||
['prototype', realmRec.Intrinsics['%AsyncGenerator.prototype%'], undefined, { Writable: Value.false }],
|
||||
], realmRec.Intrinsics['%Function.prototype%'], 'AsyncGeneratorFunction');
|
||||
|
||||
X(realmRec.Intrinsics['%AsyncGenerator.prototype%'].DefineOwnProperty(new Value('constructor'), Descriptor({
|
||||
Value: proto,
|
||||
Writable: Value.false,
|
||||
Enumerable: Value.false,
|
||||
Configurable: Value.true,
|
||||
})));
|
||||
|
||||
realmRec.Intrinsics['%AsyncGeneratorFunction.prototype%'] = proto;
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
import { surroundingAgent } from '../engine.mjs';
|
||||
import { Q, X } from '../completion.mjs';
|
||||
import { CreateDynamicFunction } from '../runtime-semantics/all.mjs';
|
||||
import { Descriptor, Value } from '../value.mjs';
|
||||
import { BootstrapConstructor } from './Bootstrap.mjs';
|
||||
|
||||
// #sec-asyncgeneratorfunction
|
||||
function AsyncGeneratorFunctionConstructor(args, { NewTarget }) {
|
||||
// 1. Let C be the active function object.
|
||||
const C = surroundingAgent.activeFunctionObject;
|
||||
// 2. Let args be the argumentsList that was passed to this function by [[Call]] or [[Construct]].
|
||||
// 3. Return ? CreateDynamicFunction(C, NewTarget, asyncGenerator, args).
|
||||
return Q(CreateDynamicFunction(C, NewTarget, 'asyncGenerator', args));
|
||||
}
|
||||
|
||||
export function BootstrapAsyncGeneratorFunction(realmRec) {
|
||||
const cons = BootstrapConstructor(realmRec, AsyncGeneratorFunctionConstructor, 'AsyncGeneratorFunction', 1, realmRec.Intrinsics['%AsyncGeneratorFunction.prototype%'], []);
|
||||
|
||||
X(cons.DefineOwnProperty(new Value('prototype'), Descriptor({
|
||||
Writable: Value.false,
|
||||
Enumerable: Value.false,
|
||||
Configurable: Value.false,
|
||||
})));
|
||||
|
||||
X(realmRec.Intrinsics['%AsyncGeneratorFunction.prototype%'].DefineOwnProperty(new Value('constructor'), Descriptor({
|
||||
Writable: Value.false,
|
||||
Enumerable: Value.false,
|
||||
Configurable: Value.true,
|
||||
})));
|
||||
|
||||
cons.Prototype = realmRec.Intrinsics['%Function%'];
|
||||
|
||||
realmRec.Intrinsics['%AsyncGeneratorFunction%'] = cons;
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
import {
|
||||
X,
|
||||
Completion,
|
||||
NormalCompletion,
|
||||
ThrowCompletion,
|
||||
} from '../completion.mjs';
|
||||
import { Value } from '../value.mjs';
|
||||
import { AsyncGeneratorEnqueue } from '../abstract-ops/all.mjs';
|
||||
import { BootstrapPrototype } from './Bootstrap.mjs';
|
||||
|
||||
// #sec-asyncgenerator-prototype-next
|
||||
function AsyncGeneratorPrototype_next([value = Value.undefined], { thisValue }) {
|
||||
// 1. Let generator be the this value.
|
||||
const generator = thisValue;
|
||||
// 2. Let completion be NormalCompletion(value).
|
||||
const completion = NormalCompletion(value);
|
||||
// 3. Return ! AsyncGeneratorEnqueue(generator, completion).
|
||||
return X(AsyncGeneratorEnqueue(generator, completion));
|
||||
}
|
||||
|
||||
// #sec-asyncgenerator-prototype-return
|
||||
function AsyncGeneratorPrototype_return([value = Value.undefined], { thisValue }) {
|
||||
// 1. Let generator be the this value.
|
||||
const generator = thisValue;
|
||||
// 2. Let completion be Completion { [[Type]]: return, [[Value]]: value, [[Target]]: empty }.
|
||||
const completion = new Completion({ Type: 'return', Value: value, Target: undefined });
|
||||
// 3. Return ! AsyncGeneratorEnqueue(generator, completion).
|
||||
return X(AsyncGeneratorEnqueue(generator, completion));
|
||||
}
|
||||
|
||||
// #sec-asyncgenerator-prototype-throw
|
||||
function AsyncGeneratorPrototype_throw([exception = Value.undefined], { thisValue }) {
|
||||
// 1. Let generator be the this value.
|
||||
const generator = thisValue;
|
||||
// 2. Let completion be ThrowCompletion(exception).
|
||||
const completion = ThrowCompletion(exception);
|
||||
// 3. Return ! AsyncGeneratorEnqueue(generator, completion).
|
||||
return X(AsyncGeneratorEnqueue(generator, completion));
|
||||
}
|
||||
|
||||
export function BootstrapAsyncGeneratorPrototype(realmRec) {
|
||||
const proto = BootstrapPrototype(realmRec, [
|
||||
['next', AsyncGeneratorPrototype_next, 1],
|
||||
['return', AsyncGeneratorPrototype_return, 1],
|
||||
['throw', AsyncGeneratorPrototype_throw, 1],
|
||||
], realmRec.Intrinsics['%AsyncIteratorPrototype%'], 'AsyncGenerator');
|
||||
|
||||
realmRec.Intrinsics['%AsyncGenerator.prototype%'] = proto;
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
import { wellKnownSymbols } from '../value.mjs';
|
||||
import { BootstrapPrototype } from './Bootstrap.mjs';
|
||||
|
||||
// #sec-asynciteratorprototype-asynciterator
|
||||
function AsyncIteratorPrototype_asyncIterator(args, { thisValue }) {
|
||||
// 1. Return the this value.
|
||||
return thisValue;
|
||||
}
|
||||
|
||||
export function BootstrapAsyncIteratorPrototype(realmRec) {
|
||||
const proto = BootstrapPrototype(realmRec, [
|
||||
[wellKnownSymbols.asyncIterator, AsyncIteratorPrototype_asyncIterator, 0],
|
||||
], realmRec.Intrinsics['%Object.prototype%']);
|
||||
|
||||
realmRec.Intrinsics['%AsyncIteratorPrototype%'] = proto;
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
import { surroundingAgent } from '../engine.mjs';
|
||||
import { Type, Value } from '../value.mjs';
|
||||
import { ToPrimitive, ToBigInt, ToIndex } from '../abstract-ops/all.mjs';
|
||||
import { NumberToBigInt } from '../runtime-semantics/all.mjs';
|
||||
import { Q } from '../completion.mjs';
|
||||
import { BootstrapConstructor } from './Bootstrap.mjs';
|
||||
|
||||
// #sec-bigint-constructor
|
||||
function BigIntConstructor([value], { NewTarget }) {
|
||||
// 1. If NewTarget is not undefined, throw a TypeError exception.
|
||||
if (NewTarget !== Value.undefined) {
|
||||
return surroundingAgent.Throw('TypeError', 'NotAConstructor', 'BigInt');
|
||||
}
|
||||
// 2. Let prim be ? ToPrimitive(value, hint Number).
|
||||
const prim = Q(ToPrimitive(value, 'Number'));
|
||||
// 3. If Type(prim) is Number, return ? NumberToBigInt(prim).
|
||||
// 4. Otherwise, return ? ToBigInt(value).
|
||||
if (Type(prim) === 'Number') {
|
||||
return Q(NumberToBigInt(prim));
|
||||
} else {
|
||||
return Q(ToBigInt(value));
|
||||
}
|
||||
}
|
||||
|
||||
// #sec-bigint.asintn
|
||||
function BigInt_asIntN([bits = Value.undefined, bigint = Value.undefined]) {
|
||||
// 1. Set bits to ? ToIndex(bits).
|
||||
bits = Q(ToIndex(bits));
|
||||
// 2. Set bigint to ? ToBigInt(bigint).
|
||||
bigint = Q(ToBigInt(bigint));
|
||||
// 3. Let mod be the BigInt value that represents bigint modulo 2bits.
|
||||
// 4. If mod ≥ 2^bits - 1, return mod - 2^bits; otherwise, return mod.
|
||||
return new Value(BigInt.asIntN(bits.numberValue(), bigint.bigintValue()));
|
||||
}
|
||||
|
||||
// #sec-bigint.asuintn
|
||||
function BigInt_asUintN([bits = Value.undefined, bigint = Value.undefined]) {
|
||||
// 1. Set bits to ? ToIndex(bits).
|
||||
bits = Q(ToIndex(bits));
|
||||
// 2. Set bigint to ? ToBigInt(bigint).
|
||||
bigint = Q(ToBigInt(bigint));
|
||||
// 3. Return the BigInt value that represents bigint modulo 2^bits.
|
||||
return new Value(BigInt.asUintN(bits.numberValue(), bigint.bigintValue()));
|
||||
}
|
||||
|
||||
export function BootstrapBigInt(realmRec) {
|
||||
const bigintConstructor = BootstrapConstructor(realmRec, BigIntConstructor, 'BigInt', 1, realmRec.Intrinsics['%BigInt.prototype%'], [
|
||||
['asIntN', BigInt_asIntN, 2],
|
||||
['asUintN', BigInt_asUintN, 2],
|
||||
]);
|
||||
|
||||
realmRec.Intrinsics['%BigInt%'] = bigintConstructor;
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
import { surroundingAgent } from '../engine.mjs';
|
||||
import { Type, Value } from '../value.mjs';
|
||||
import { Assert, ToInteger, ToString } from '../abstract-ops/all.mjs';
|
||||
import { Q, X } from '../completion.mjs';
|
||||
import { BootstrapPrototype } from './Bootstrap.mjs';
|
||||
|
||||
// #sec-thisbigintvalue
|
||||
function thisBigIntValue(value) {
|
||||
// 1. If Type(value) is BigInt, return value.
|
||||
if (Type(value) === 'BigInt') {
|
||||
return value;
|
||||
}
|
||||
// 2. If Type(value) is Object and value has a [[BigIntData]] internal slot, then
|
||||
if (Type(value) === 'Object' && 'BigIntData' in value) {
|
||||
// a. Assert: Type(value.[[BigIntData]]) is BigInt.
|
||||
Assert(Type(value.BigIntData) === 'BigInt');
|
||||
// b. Return value.[[BigIntData]].
|
||||
return value.BigIntData;
|
||||
}
|
||||
// 3. Throw a TypeError exception.
|
||||
return surroundingAgent.Throw('TypeError', 'NotATypeObject', 'BigInt', value);
|
||||
}
|
||||
|
||||
// #sec-bigint.prototype.tolocalestring
|
||||
function BigIntProto_toLocalString(args, { thisValue }) {
|
||||
return BigIntProto_toString(args, { thisValue });
|
||||
}
|
||||
|
||||
// #sec-bigint.prototype.tostring
|
||||
function BigIntProto_toString([radix], { thisValue }) {
|
||||
// 1. Let x be ? thisBigIntValue(this value).
|
||||
const x = Q(thisBigIntValue(thisValue));
|
||||
// 2. If radix is not present, let radixNumber be 10.
|
||||
let radixNumber;
|
||||
if (radix === undefined) {
|
||||
radixNumber = 10;
|
||||
} else if (radix === Value.undefined) {
|
||||
// 3. Else if radix is undefined, let radixNumber be 10.
|
||||
radixNumber = 10;
|
||||
} else {
|
||||
// 4. Else, let radixNumber be ? ToInteger(radix).
|
||||
radixNumber = Q(ToInteger(radix)).numberValue();
|
||||
}
|
||||
// 5. If radixNumber < 2 or radixNumber > 36, throw a RangeError exception.
|
||||
if (radixNumber < 2 || radixNumber > 36) {
|
||||
return surroundingAgent.Throw('RangeError', 'InvalidRadix');
|
||||
}
|
||||
// 6. If radixNumber = 10, return ! ToString(x).
|
||||
if (radixNumber === 10) {
|
||||
return X(ToString(x));
|
||||
}
|
||||
// 7. Return the String representation of this Number value using the radix specified by
|
||||
// radixNumber. Letters a-z are used for digits with values 10 through 35. The precise
|
||||
// algorithm is implementation-dependent, however the algorithm should be a
|
||||
// generalization of that specified in 6.1.6.2.23.
|
||||
// TODO: Implementation stringification
|
||||
return new Value(x.bigintValue().toString(radixNumber));
|
||||
}
|
||||
|
||||
// #sec-bigint.prototype.tostring
|
||||
function BigIntProto_valueOf(args, { thisValue }) {
|
||||
// Return ? thisBigIntValue(this value).
|
||||
return Q(thisBigIntValue(thisValue));
|
||||
}
|
||||
|
||||
export function BootstrapBigIntPrototype(realmRec) {
|
||||
const proto = BootstrapPrototype(realmRec, [
|
||||
['toLocaleString', BigIntProto_toLocalString, 0],
|
||||
['toString', BigIntProto_toString, 0],
|
||||
['valueOf', BigIntProto_valueOf, 0],
|
||||
], realmRec.Intrinsics['%Object.prototype%'], 'BigInt');
|
||||
|
||||
realmRec.Intrinsics['%BigInt.prototype%'] = proto;
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
import {
|
||||
OrdinaryCreateFromConstructor,
|
||||
ToBoolean,
|
||||
} from '../abstract-ops/all.mjs';
|
||||
import { Value } from '../value.mjs';
|
||||
import { Q, X } from '../completion.mjs';
|
||||
import { BootstrapConstructor } from './Bootstrap.mjs';
|
||||
|
||||
// #sec-boolean-constructor-boolean-value
|
||||
function BooleanConstructor([value = Value.undefined], { NewTarget }) {
|
||||
// 1. Let b be ! ToBoolean(value).
|
||||
const b = X(ToBoolean(value));
|
||||
// 2. If NewTarget is undefined, return b.
|
||||
if (NewTarget === Value.undefined) {
|
||||
return b;
|
||||
}
|
||||
// 3. Let O be ? OrdinaryCreateFromConstructor(NewTarget, "%Boolean.prototype%", « [[BooleanData]] »).
|
||||
const O = Q(OrdinaryCreateFromConstructor(NewTarget, '%Boolean.prototype%', ['BooleanData']));
|
||||
// 4. Set O.[[BooleanData]] to b.
|
||||
O.BooleanData = b;
|
||||
// 5. Return O.
|
||||
return O;
|
||||
}
|
||||
|
||||
export function BootstrapBoolean(realmRec) {
|
||||
const cons = BootstrapConstructor(
|
||||
realmRec, BooleanConstructor, 'Boolean', 1,
|
||||
realmRec.Intrinsics['%Boolean.prototype%'], [],
|
||||
);
|
||||
|
||||
realmRec.Intrinsics['%Boolean%'] = cons;
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
import {
|
||||
Type,
|
||||
Value,
|
||||
} from '../value.mjs';
|
||||
import {
|
||||
surroundingAgent,
|
||||
} from '../engine.mjs';
|
||||
import { Assert } from '../abstract-ops/all.mjs';
|
||||
import { Q } from '../completion.mjs';
|
||||
import { BootstrapPrototype } from './Bootstrap.mjs';
|
||||
|
||||
|
||||
function thisBooleanValue(value) {
|
||||
if (Type(value) === 'Boolean') {
|
||||
return value;
|
||||
}
|
||||
|
||||
if (Type(value) === 'Object' && 'BooleanData' in value) {
|
||||
const b = value.BooleanData;
|
||||
Assert(Type(b) === 'Boolean');
|
||||
return b;
|
||||
}
|
||||
|
||||
return surroundingAgent.Throw('TypeError', 'NotATypeObject', 'Boolean', value);
|
||||
}
|
||||
|
||||
// #sec-boolean.prototype.tostring
|
||||
function BooleanProto_toString(argList, { thisValue }) {
|
||||
// 1. Let b be ? thisBooleanValue(this value).
|
||||
const b = Q(thisBooleanValue(thisValue));
|
||||
// 2. If b is true, return "true"; else return "false".
|
||||
if (b === Value.true) {
|
||||
return new Value('true');
|
||||
}
|
||||
return new Value('false');
|
||||
}
|
||||
|
||||
// #sec-boolean.prototype.valueof
|
||||
function BooleanProto_valueOf(argList, { thisValue }) {
|
||||
// 1. Return ? thisBooleanValue(this value).
|
||||
return Q(thisBooleanValue(thisValue));
|
||||
}
|
||||
|
||||
export function BootstrapBooleanPrototype(realmRec) {
|
||||
const proto = BootstrapPrototype(realmRec, [
|
||||
['toString', BooleanProto_toString, 0],
|
||||
['valueOf', BooleanProto_valueOf, 0],
|
||||
], realmRec.Intrinsics['%Object.prototype%']);
|
||||
|
||||
proto.BooleanData = Value.false;
|
||||
|
||||
realmRec.Intrinsics['%Boolean.prototype%'] = proto;
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
import {
|
||||
Assert,
|
||||
CreateBuiltinFunction,
|
||||
OrdinaryObjectCreate,
|
||||
SetFunctionLength,
|
||||
SetFunctionName,
|
||||
} from '../abstract-ops/all.mjs';
|
||||
import {
|
||||
Descriptor,
|
||||
Value,
|
||||
wellKnownSymbols,
|
||||
} from '../value.mjs';
|
||||
import { X } from '../completion.mjs';
|
||||
|
||||
// 17 #sec-ecmascript-standard-built-in-objects
|
||||
export function assignProps(realmRec, obj, props) {
|
||||
for (const item of props) {
|
||||
if (item === undefined) {
|
||||
continue;
|
||||
}
|
||||
const [n, v, len, descriptor] = item;
|
||||
const name = n instanceof Value ? n : new Value(n);
|
||||
if (Array.isArray(v)) {
|
||||
// Every accessor property described in clauses 18 through 26 and in
|
||||
// Annex B.2 has the attributes { [[Enumerable]]: false,
|
||||
// [[Configurable]]: true } unless otherwise specified. If only a get
|
||||
// accessor function is described, the set accessor function is the
|
||||
// default value, undefined. If only a set accessor is described the get
|
||||
// accessor is the default value, undefined.
|
||||
let [
|
||||
getter = Value.undefined,
|
||||
setter = Value.undefined,
|
||||
] = v;
|
||||
if (typeof getter === 'function') {
|
||||
getter = CreateBuiltinFunction(getter, [], realmRec);
|
||||
X(SetFunctionName(getter, name, new Value('get')));
|
||||
X(SetFunctionLength(getter, new Value(0)));
|
||||
}
|
||||
if (typeof setter === 'function') {
|
||||
setter = CreateBuiltinFunction(setter, [], realmRec);
|
||||
X(SetFunctionName(setter, name, new Value('set')));
|
||||
X(SetFunctionLength(setter, new Value(1)));
|
||||
}
|
||||
X(obj.DefineOwnProperty(name, Descriptor({
|
||||
Get: getter,
|
||||
Set: setter,
|
||||
Enumerable: Value.false,
|
||||
Configurable: Value.true,
|
||||
...descriptor,
|
||||
})));
|
||||
} else {
|
||||
// Every other data property described in clauses 18 through 26 and in
|
||||
// Annex B.2 has the attributes { [[Writable]]: true, [[Enumerable]]:
|
||||
// false, [[Configurable]]: true } unless otherwise specified.
|
||||
let value;
|
||||
if (typeof v === 'function') {
|
||||
Assert(typeof len === 'number');
|
||||
value = CreateBuiltinFunction(v, [], realmRec);
|
||||
X(SetFunctionName(value, name));
|
||||
X(SetFunctionLength(value, new Value(len)));
|
||||
} else {
|
||||
value = v;
|
||||
}
|
||||
obj.properties.set(name, Descriptor({
|
||||
Value: value,
|
||||
Writable: Value.true,
|
||||
Enumerable: Value.false,
|
||||
Configurable: Value.true,
|
||||
...descriptor,
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function BootstrapPrototype(realmRec, props, Prototype, stringTag) {
|
||||
Assert(Prototype !== undefined);
|
||||
const proto = OrdinaryObjectCreate(Prototype);
|
||||
|
||||
assignProps(realmRec, proto, props);
|
||||
|
||||
if (stringTag !== undefined) {
|
||||
X(proto.DefineOwnProperty(wellKnownSymbols.toStringTag, Descriptor({
|
||||
Value: new Value(stringTag),
|
||||
Writable: Value.false,
|
||||
Enumerable: Value.false,
|
||||
Configurable: Value.true,
|
||||
})));
|
||||
}
|
||||
|
||||
return proto;
|
||||
}
|
||||
|
||||
export function BootstrapConstructor(realmRec, Constructor, name, length, Prototype, props = []) {
|
||||
const cons = CreateBuiltinFunction(Constructor, [], realmRec, undefined, Value.true);
|
||||
|
||||
SetFunctionName(cons, new Value(name));
|
||||
SetFunctionLength(cons, new Value(length));
|
||||
|
||||
X(cons.DefineOwnProperty(new Value('prototype'), Descriptor({
|
||||
Value: Prototype,
|
||||
Writable: Value.false,
|
||||
Enumerable: Value.false,
|
||||
Configurable: Value.false,
|
||||
})));
|
||||
|
||||
X(Prototype.DefineOwnProperty(new Value('constructor'), Descriptor({
|
||||
Value: cons,
|
||||
Writable: Value.true,
|
||||
Enumerable: Value.false,
|
||||
Configurable: Value.true,
|
||||
})));
|
||||
|
||||
assignProps(realmRec, cons, props);
|
||||
|
||||
return cons;
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
import { surroundingAgent } from '../engine.mjs';
|
||||
import {
|
||||
IsDetachedBuffer,
|
||||
OrdinaryCreateFromConstructor,
|
||||
ToIndex,
|
||||
RequireInternalSlot,
|
||||
} from '../abstract-ops/all.mjs';
|
||||
import { Value } from '../value.mjs';
|
||||
import { Q } from '../completion.mjs';
|
||||
import { BootstrapConstructor } from './Bootstrap.mjs';
|
||||
|
||||
// #sec-dataview-constructor
|
||||
function DataViewConstructor([buffer = Value.undefined, byteOffset = Value.undefined, byteLength = Value.undefined], { NewTarget }) {
|
||||
// 1. If NewTarget is undefined, throw a TypeError exception.
|
||||
if (NewTarget === Value.undefined) {
|
||||
return surroundingAgent.Throw('TypeError', 'ConstructorNonCallable', this);
|
||||
}
|
||||
// 2. Perform ? RequireInternalSlot(buffer, [[ArrayBufferData]]).
|
||||
Q(RequireInternalSlot(buffer, 'ArrayBufferData'));
|
||||
// 3. Let offset be ? ToIndex(byteOffset).
|
||||
const offset = Q(ToIndex(byteOffset)).numberValue();
|
||||
// 4. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
|
||||
if (IsDetachedBuffer(buffer) === Value.true) {
|
||||
return surroundingAgent.Throw('TypeError', 'ArrayBufferDetached');
|
||||
}
|
||||
// 5. Let bufferByteLength be buffer.[[ArrayBufferByteLength]].
|
||||
const bufferByteLength = buffer.ArrayBufferByteLength.numberValue();
|
||||
// 6. If offset > bufferByteLength, throw a RangeError exception.
|
||||
if (offset > bufferByteLength) {
|
||||
return surroundingAgent.Throw('RangeError', 'DataViewOOB');
|
||||
}
|
||||
let viewByteLength;
|
||||
// 7. If byteLength is undefined, then
|
||||
if (byteLength === Value.undefined) {
|
||||
// a. Let viewByteLength be bufferByteLength - offset.
|
||||
viewByteLength = bufferByteLength - offset;
|
||||
} else {
|
||||
// a. Let viewByteLength be ? ToIndex(byteLength).
|
||||
viewByteLength = Q(ToIndex(byteLength)).numberValue();
|
||||
// b. If offset + viewByteLength > bufferByteLength, throw a RangeError exception.
|
||||
if (offset + viewByteLength > bufferByteLength) {
|
||||
return surroundingAgent.Throw('RangeError', 'DataViewOOB');
|
||||
}
|
||||
}
|
||||
// 9. Let O be ? OrdinaryCreateFromConstructor(NewTarget, "%DataView.prototype%", « [[DataView]], [[ViewedArrayBuffer]], [[ByteLength]], [[ByteOffset]] »).
|
||||
const O = Q(OrdinaryCreateFromConstructor(NewTarget, '%DataView.prototype%', ['DataView', 'ViewedArrayBuffer', 'ByteLength', 'ByteOffset']));
|
||||
// 10. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
|
||||
if (IsDetachedBuffer(buffer) === Value.true) {
|
||||
return surroundingAgent.Throw('TypeError', 'ArrayBufferDetached');
|
||||
}
|
||||
// 11. Set O.[[ViewedArrayBuffer]] to buffer.
|
||||
O.ViewedArrayBuffer = buffer;
|
||||
// 12. Set O.[[ByteLength]] to viewByteLength.
|
||||
O.ByteLength = new Value(viewByteLength);
|
||||
// 13. Set O.[[ByteOffset]] to offset.
|
||||
O.ByteOffset = new Value(offset);
|
||||
// 14. Return O.
|
||||
return O;
|
||||
}
|
||||
|
||||
export function BootstrapDataView(realmRec) {
|
||||
const dvConstructor = BootstrapConstructor(realmRec, DataViewConstructor, 'DataView', 1, realmRec.Intrinsics['%DataView.prototype%'], []);
|
||||
|
||||
realmRec.Intrinsics['%DataView%'] = dvConstructor;
|
||||
}
|
||||
@@ -1,267 +0,0 @@
|
||||
import {
|
||||
Assert,
|
||||
GetViewValue,
|
||||
SetViewValue,
|
||||
IsDetachedBuffer,
|
||||
RequireInternalSlot,
|
||||
} from '../abstract-ops/all.mjs';
|
||||
import { Q } from '../completion.mjs';
|
||||
import { surroundingAgent } from '../engine.mjs';
|
||||
import { Value } from '../value.mjs';
|
||||
import { BootstrapPrototype } from './Bootstrap.mjs';
|
||||
|
||||
// #sec-get-dataview.prototype.buffer
|
||||
function DataViewProto_buffer(args, { thisValue }) {
|
||||
// 1. Let O be the this value.
|
||||
const O = thisValue;
|
||||
// 2. Perform ? RequireInternalSlot(O, [[DataView]]).
|
||||
Q(RequireInternalSlot(O, 'DataView'));
|
||||
// 3. Assert: O has a [[ViewedArrayBuffer]] internal slot.
|
||||
Assert('ViewedArrayBuffer' in O);
|
||||
// 4. Let buffer be O.[[ViewedArrayBuffer]].
|
||||
const buffer = O.ViewedArrayBuffer;
|
||||
// 5. Return buffer.
|
||||
return buffer;
|
||||
}
|
||||
|
||||
// #sec-get-dataview.prototype.bytelength
|
||||
function DataViewProto_byteLength(args, { thisValue }) {
|
||||
// 1. Let O be the this value.
|
||||
const O = thisValue;
|
||||
// 2. Perform ? RequireInternalSlot(O, [[DataView]]).
|
||||
Q(RequireInternalSlot(O, 'DataView'));
|
||||
// 3. Assert: O has a [[ViewedArrayBuffer]] internal slot.
|
||||
Assert('ViewedArrayBuffer' in O);
|
||||
// 4. Let buffer be O.[[ViewedArrayBuffer]].
|
||||
const buffer = O.ViewedArrayBuffer;
|
||||
// 5. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
|
||||
if (IsDetachedBuffer(buffer) === Value.true) {
|
||||
return surroundingAgent.Throw('TypeError', 'ArrayBufferDetached');
|
||||
}
|
||||
// 6. Let size be O.[[ByteLength]].
|
||||
const size = O.ByteLength;
|
||||
// 7. Return size.
|
||||
return size;
|
||||
}
|
||||
|
||||
// #sec-get-dataview.prototype.byteoffset
|
||||
function DataViewProto_byteOffset(args, { thisValue }) {
|
||||
// 1. Let O be the this value.
|
||||
const O = thisValue;
|
||||
// 2. Perform ? RequireInternalSlot(O, [[DataView]]).
|
||||
Q(RequireInternalSlot(O, 'DataView'));
|
||||
// 3. Assert: O has a [[ViewedArrayBuffer]] internal slot.
|
||||
Assert('ViewedArrayBuffer' in O);
|
||||
// 4. Let buffer be O.[[ViewedArrayBuffer]].
|
||||
const buffer = O.ViewedArrayBuffer;
|
||||
// 5. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
|
||||
if (IsDetachedBuffer(buffer) === Value.true) {
|
||||
return surroundingAgent.Throw('TypeError', 'ArrayBufferDetached');
|
||||
}
|
||||
// 6. Let offset be O.[[ByteOffset]].
|
||||
const offset = O.ByteOffset;
|
||||
// 7. Return offset.
|
||||
return offset;
|
||||
}
|
||||
|
||||
// #sec-dataview.prototype.getbigint64
|
||||
function DataViewProto_getBigInt64([byteOffset = Value.undefined, littleEndian = Value.undefined], { thisValue }) {
|
||||
// 1. Let v be the this value.
|
||||
const v = thisValue;
|
||||
// 2. Return ? GetViewValue(v, byteOffset, littleEndian, BigInt64).
|
||||
return Q(GetViewValue(v, byteOffset, littleEndian, 'BigInt64'));
|
||||
}
|
||||
|
||||
// #sec-dataview.prototype.getbiguint64
|
||||
function DataViewProto_getBigUint64([byteOffset = Value.undefined, littleEndian = Value.undefined], { thisValue }) {
|
||||
// 1. Let v be the this value.
|
||||
const v = thisValue;
|
||||
// 2. Return ? GetViewValue(v, byteOffset, littleEndian, BigUint64).
|
||||
return Q(GetViewValue(v, byteOffset, littleEndian, 'BigUint64'));
|
||||
}
|
||||
|
||||
// 24.3.4.5 #sec-dataview.prototype.getfloat32
|
||||
function DataViewProto_getFloat32([byteOffset = Value.undefined, littleEndian], { thisValue }) {
|
||||
const v = thisValue;
|
||||
if (littleEndian === undefined) {
|
||||
littleEndian = Value.false;
|
||||
}
|
||||
return Q(GetViewValue(v, byteOffset, littleEndian, 'Float32'));
|
||||
}
|
||||
|
||||
// 24.3.4.6 #sec-dataview.prototype.getfloat64
|
||||
function DataViewProto_getFloat64([byteOffset = Value.undefined, littleEndian], { thisValue }) {
|
||||
const v = thisValue;
|
||||
if (littleEndian === undefined) {
|
||||
littleEndian = Value.false;
|
||||
}
|
||||
return Q(GetViewValue(v, byteOffset, littleEndian, 'Float64'));
|
||||
}
|
||||
|
||||
// 24.3.4.7 #sec-dataview.prototype.getint8
|
||||
function DataViewProto_getInt8([byteOffset = Value.undefined], { thisValue }) {
|
||||
const v = thisValue;
|
||||
return Q(GetViewValue(v, byteOffset, Value.true, 'Int8'));
|
||||
}
|
||||
|
||||
// 24.3.4.8 #sec-dataview.prototype.getint16
|
||||
function DataViewProto_getInt16([byteOffset = Value.undefined, littleEndian], { thisValue }) {
|
||||
const v = thisValue;
|
||||
if (littleEndian === undefined) {
|
||||
littleEndian = Value.false;
|
||||
}
|
||||
return Q(GetViewValue(v, byteOffset, littleEndian, 'Int16'));
|
||||
}
|
||||
|
||||
// 24.3.4.9 #sec-dataview.prototype.getint32
|
||||
function DataViewProto_getInt32([byteOffset = Value.undefined, littleEndian], { thisValue }) {
|
||||
const v = thisValue;
|
||||
if (littleEndian === undefined) {
|
||||
littleEndian = Value.false;
|
||||
}
|
||||
return Q(GetViewValue(v, byteOffset, littleEndian, 'Int32'));
|
||||
}
|
||||
|
||||
// 24.3.4.10 #sec-dataview.prototype.getuint8
|
||||
function DataViewProto_getUint8([byteOffset = Value.undefined], { thisValue }) {
|
||||
const v = thisValue;
|
||||
return Q(GetViewValue(v, byteOffset, Value.true, 'Uint8'));
|
||||
}
|
||||
|
||||
// 24.3.4.11 #sec-dataview.prototype.getuint16
|
||||
function DataViewProto_getUint16([byteOffset = Value.undefined, littleEndian], { thisValue }) {
|
||||
const v = thisValue;
|
||||
if (littleEndian === undefined) {
|
||||
littleEndian = Value.false;
|
||||
}
|
||||
return Q(GetViewValue(v, byteOffset, littleEndian, 'Uint16'));
|
||||
}
|
||||
|
||||
// 24.3.4.12 #sec-dataview.prototype.getuint32
|
||||
function DataViewProto_getUint32([byteOffset = Value.undefined, littleEndian], { thisValue }) {
|
||||
const v = thisValue;
|
||||
if (littleEndian === undefined) {
|
||||
littleEndian = Value.false;
|
||||
}
|
||||
return Q(GetViewValue(v, byteOffset, littleEndian, 'Uint32'));
|
||||
}
|
||||
|
||||
// #sec-dataview.prototype.setbigint64
|
||||
function DataViewProto_setBigInt64([byteOffset = Value.undefined, value = Value.undefined, littleEndian], { thisValue }) {
|
||||
// 1. Let v be the this value.
|
||||
const v = thisValue;
|
||||
// 2. If littleEndian is not present, set littleEndian to undefined.
|
||||
if (littleEndian === undefined) {
|
||||
littleEndian = Value.undefined;
|
||||
}
|
||||
// 3. Return ? SetViewValue(v, byteOffset, littleEndian, BigInt64, value).
|
||||
return Q(SetViewValue(v, byteOffset, littleEndian, 'BigInt64', value));
|
||||
}
|
||||
|
||||
// #sec-dataview.prototype.setbiguint64
|
||||
function DataViewProto_setBigUint64([byteOffset = Value.undefined, value = Value.undefined, littleEndian], { thisValue }) {
|
||||
// 1. Let v be the this value.
|
||||
const v = thisValue;
|
||||
// 2. If littleEndian is not present, set littleEndian to undefined.
|
||||
if (littleEndian === undefined) {
|
||||
littleEndian = Value.undefined;
|
||||
}
|
||||
// 3. Return ? SetViewValue(v, byteOffset, littleEndian, BigUint64, value).
|
||||
return Q(SetViewValue(v, byteOffset, littleEndian, 'BigUint64', value));
|
||||
}
|
||||
|
||||
// 24.3.4.13 #sec-dataview.prototype.setfloat32
|
||||
function DataViewProto_setFloat32([byteOffset = Value.undefined, value = Value.undefined, littleEndian], { thisValue }) {
|
||||
const v = thisValue;
|
||||
if (littleEndian === undefined) {
|
||||
littleEndian = Value.false;
|
||||
}
|
||||
return Q(SetViewValue(v, byteOffset, littleEndian, 'Float32', value));
|
||||
}
|
||||
|
||||
// 24.3.4.14 #sec-dataview.prototype.setfloat64
|
||||
function DataViewProto_setFloat64([byteOffset = Value.undefined, value = Value.undefined, littleEndian], { thisValue }) {
|
||||
const v = thisValue;
|
||||
if (littleEndian === undefined) {
|
||||
littleEndian = Value.false;
|
||||
}
|
||||
return Q(SetViewValue(v, byteOffset, littleEndian, 'Float64', value));
|
||||
}
|
||||
|
||||
// 24.3.4.15 #sec-dataview.prototype.setint8
|
||||
function DataViewProto_setInt8([byteOffset = Value.undefined, value = Value.undefined], { thisValue }) {
|
||||
const v = thisValue;
|
||||
return Q(SetViewValue(v, byteOffset, Value.true, 'Int8', value));
|
||||
}
|
||||
|
||||
// 24.3.4.16 #sec-dataview.prototype.setint16
|
||||
function DataViewProto_setInt16([byteOffset = Value.undefined, value = Value.undefined, littleEndian], { thisValue }) {
|
||||
const v = thisValue;
|
||||
if (littleEndian === undefined) {
|
||||
littleEndian = Value.false;
|
||||
}
|
||||
return Q(SetViewValue(v, byteOffset, littleEndian, 'Int16', value));
|
||||
}
|
||||
|
||||
// 24.3.4.17 #sec-dataview.prototype.setint32
|
||||
function DataViewProto_setInt32([byteOffset = Value.undefined, value = Value.undefined, littleEndian], { thisValue }) {
|
||||
const v = thisValue;
|
||||
if (littleEndian === undefined) {
|
||||
littleEndian = Value.false;
|
||||
}
|
||||
return Q(SetViewValue(v, byteOffset, littleEndian, 'Int32', value));
|
||||
}
|
||||
|
||||
// 24.3.4.18 #sec-dataview.prototype.setuint8
|
||||
function DataViewProto_setUint8([byteOffset = Value.undefined, value = Value.undefined], { thisValue }) {
|
||||
const v = thisValue;
|
||||
return Q(SetViewValue(v, byteOffset, Value.true, 'Uint8', value));
|
||||
}
|
||||
|
||||
// 24.3.4.19 #sec-dataview.prototype.setuint16
|
||||
function DataViewProto_setUint16([byteOffset = Value.undefined, value = Value.undefined, littleEndian], { thisValue }) {
|
||||
const v = thisValue;
|
||||
if (littleEndian === undefined) {
|
||||
littleEndian = Value.false;
|
||||
}
|
||||
return Q(SetViewValue(v, byteOffset, littleEndian, 'Uint16', value));
|
||||
}
|
||||
|
||||
// 24.3.4.20 #sec-dataview.prototype.setuint32
|
||||
function DataViewProto_setUint32([byteOffset = Value.undefined, value = Value.undefined, littleEndian], { thisValue }) {
|
||||
const v = thisValue;
|
||||
if (littleEndian === undefined) {
|
||||
littleEndian = Value.false;
|
||||
}
|
||||
return Q(SetViewValue(v, byteOffset, littleEndian, 'Uint32', value));
|
||||
}
|
||||
|
||||
export function BootstrapDataViewPrototype(realmRec) {
|
||||
const proto = BootstrapPrototype(realmRec, [
|
||||
['buffer', [DataViewProto_buffer]],
|
||||
['byteLength', [DataViewProto_byteLength]],
|
||||
['byteOffset', [DataViewProto_byteOffset]],
|
||||
['getBigInt64', DataViewProto_getBigInt64, 1],
|
||||
['getBigUint64', DataViewProto_getBigUint64, 1],
|
||||
['getFloat32', DataViewProto_getFloat32, 1],
|
||||
['getFloat64', DataViewProto_getFloat64, 1],
|
||||
['getInt8', DataViewProto_getInt8, 1],
|
||||
['getInt16', DataViewProto_getInt16, 1],
|
||||
['getInt32', DataViewProto_getInt32, 1],
|
||||
['getUint8', DataViewProto_getUint8, 1],
|
||||
['getUint16', DataViewProto_getUint16, 1],
|
||||
['getUint32', DataViewProto_getUint32, 1],
|
||||
['setBigInt64', DataViewProto_setBigInt64, 2],
|
||||
['setBigUint64', DataViewProto_setBigUint64, 2],
|
||||
['setFloat32', DataViewProto_setFloat32, 2],
|
||||
['setFloat64', DataViewProto_setFloat64, 2],
|
||||
['setInt8', DataViewProto_setInt8, 2],
|
||||
['setInt16', DataViewProto_setInt16, 2],
|
||||
['setInt32', DataViewProto_setInt32, 2],
|
||||
['setUint8', DataViewProto_setUint8, 2],
|
||||
['setUint16', DataViewProto_setUint16, 2],
|
||||
['setUint32', DataViewProto_setUint32, 2],
|
||||
], realmRec.Intrinsics['%Object.prototype%'], 'DataView');
|
||||
|
||||
realmRec.Intrinsics['%DataView.prototype%'] = proto;
|
||||
}
|
||||
@@ -1,204 +0,0 @@
|
||||
import {
|
||||
Assert,
|
||||
OrdinaryCreateFromConstructor,
|
||||
ToPrimitive,
|
||||
ToNumber,
|
||||
ToInteger,
|
||||
ToString,
|
||||
MakeDate,
|
||||
MakeDay,
|
||||
MakeTime,
|
||||
UTC,
|
||||
TimeClip,
|
||||
} from '../abstract-ops/all.mjs';
|
||||
import { Value, Type } from '../value.mjs';
|
||||
import {
|
||||
AbruptCompletion,
|
||||
Q, X,
|
||||
} from '../completion.mjs';
|
||||
import { BootstrapConstructor } from './Bootstrap.mjs';
|
||||
import { ToDateString, thisTimeValue } from './DatePrototype.mjs';
|
||||
|
||||
// #sec-date-constructor
|
||||
function DateConstructor(args, { NewTarget }) {
|
||||
const numberOfArgs = args.length;
|
||||
if (numberOfArgs >= 2) {
|
||||
// 20.3.2.1 #sec-date-year-month-date-hours-minutes-seconds-ms
|
||||
const [year, month, date, hours, minutes, seconds, ms] = args;
|
||||
Assert(numberOfArgs >= 2);
|
||||
if (NewTarget === Value.undefined) {
|
||||
const now = Date.now();
|
||||
return ToDateString(new Value(now));
|
||||
} else {
|
||||
const y = Q(ToNumber(year));
|
||||
const m = Q(ToNumber(month));
|
||||
let dt;
|
||||
if (date !== undefined) {
|
||||
dt = Q(ToNumber(date));
|
||||
} else {
|
||||
dt = new Value(1);
|
||||
}
|
||||
let h;
|
||||
if (hours !== undefined) {
|
||||
h = Q(ToNumber(hours));
|
||||
} else {
|
||||
h = new Value(0);
|
||||
}
|
||||
let min;
|
||||
if (minutes !== undefined) {
|
||||
min = Q(ToNumber(minutes));
|
||||
} else {
|
||||
min = new Value(0);
|
||||
}
|
||||
let s;
|
||||
if (seconds !== undefined) {
|
||||
s = Q(ToNumber(seconds));
|
||||
} else {
|
||||
s = new Value(0);
|
||||
}
|
||||
let milli;
|
||||
if (ms !== undefined) {
|
||||
milli = Q(ToNumber(ms));
|
||||
} else {
|
||||
milli = new Value(0);
|
||||
}
|
||||
let yr;
|
||||
if (y.isNaN()) {
|
||||
yr = new Value(NaN);
|
||||
} else {
|
||||
const yi = X(ToInteger(y)).numberValue();
|
||||
if (yi >= 0 && yi <= 99) {
|
||||
yr = new Value(1900 + yi);
|
||||
} else {
|
||||
yr = y;
|
||||
}
|
||||
}
|
||||
const finalDate = MakeDate(MakeDay(yr, m, dt), MakeTime(h, min, s, milli));
|
||||
const O = Q(OrdinaryCreateFromConstructor(NewTarget, '%Date.prototype%', ['DateValue']));
|
||||
O.DateValue = TimeClip(UTC(finalDate));
|
||||
return O;
|
||||
}
|
||||
} else if (numberOfArgs === 1) {
|
||||
const [value] = args;
|
||||
// 20.3.2.2 #sec-date-value
|
||||
Assert(numberOfArgs === 1);
|
||||
if (NewTarget === Value.undefined) {
|
||||
const now = Date.now();
|
||||
return ToDateString(new Value(now));
|
||||
} else {
|
||||
let tv;
|
||||
if (Type(value) === 'Object' && 'DateValue' in value) {
|
||||
tv = thisTimeValue(value);
|
||||
} else {
|
||||
const v = Q(ToPrimitive(value));
|
||||
if (Type(v) === 'String') {
|
||||
// Assert: The next step never returns an abrupt completion because Type(v) is String.
|
||||
tv = parseDate(v);
|
||||
} else {
|
||||
tv = Q(ToNumber(v));
|
||||
}
|
||||
}
|
||||
const O = Q(OrdinaryCreateFromConstructor(NewTarget, '%Date.prototype%', ['DateValue']));
|
||||
O.DateValue = TimeClip(tv);
|
||||
return O;
|
||||
}
|
||||
} else {
|
||||
// 20.3.2.3 #sec-date-constructor-date
|
||||
Assert(numberOfArgs === 0);
|
||||
if (NewTarget === Value.undefined) {
|
||||
const now = Date.now();
|
||||
return ToDateString(new Value(now));
|
||||
} else {
|
||||
const O = Q(OrdinaryCreateFromConstructor(NewTarget, '%Date.prototype%', ['DateValue']));
|
||||
O.DateValue = new Value(Date.now());
|
||||
return O;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 20.3.3.1 #sec-date.now
|
||||
function Date_now() {
|
||||
const now = Date.now();
|
||||
return new Value(now);
|
||||
}
|
||||
|
||||
// 20.3.3.2 #sec-date.parse
|
||||
function Date_parse([string = Value.undefined]) {
|
||||
const str = ToString(string);
|
||||
if (str instanceof AbruptCompletion) {
|
||||
return str;
|
||||
}
|
||||
return parseDate(str);
|
||||
}
|
||||
|
||||
// 20.3.3.4 #sec-date.utc
|
||||
function Date_UTC([year = Value.undefined, month, date, hours, minutes, seconds, ms]) {
|
||||
const y = Q(ToNumber(year));
|
||||
let m;
|
||||
if (month !== undefined) {
|
||||
m = Q(ToNumber(month));
|
||||
} else {
|
||||
m = new Value(0);
|
||||
}
|
||||
let dt;
|
||||
if (date !== undefined) {
|
||||
dt = Q(ToNumber(date));
|
||||
} else {
|
||||
dt = new Value(1);
|
||||
}
|
||||
let h;
|
||||
if (hours !== undefined) {
|
||||
h = Q(ToNumber(hours));
|
||||
} else {
|
||||
h = new Value(0);
|
||||
}
|
||||
let min;
|
||||
if (minutes !== undefined) {
|
||||
min = Q(ToNumber(minutes));
|
||||
} else {
|
||||
min = new Value(0);
|
||||
}
|
||||
let s;
|
||||
if (seconds !== undefined) {
|
||||
s = Q(ToNumber(seconds));
|
||||
} else {
|
||||
s = new Value(0);
|
||||
}
|
||||
let milli;
|
||||
if (ms !== undefined) {
|
||||
milli = Q(ToNumber(ms));
|
||||
} else {
|
||||
milli = new Value(0);
|
||||
}
|
||||
|
||||
let yr;
|
||||
if (y.isNaN()) {
|
||||
yr = new Value(NaN);
|
||||
} else {
|
||||
const yi = X(ToInteger(y)).numberValue();
|
||||
if (yi >= 0 && yi <= 99) {
|
||||
yr = new Value(1900 + yi);
|
||||
} else {
|
||||
yr = y;
|
||||
}
|
||||
}
|
||||
|
||||
return TimeClip(MakeDate(MakeDay(yr, m, dt), MakeTime(h, min, s, milli)));
|
||||
}
|
||||
|
||||
function parseDate(dateTimeString) {
|
||||
// 20.3.1.15 #sec-date-time-string-format
|
||||
// TODO: implement parsing without the host.
|
||||
const parsed = Date.parse(dateTimeString.stringValue());
|
||||
return new Value(parsed);
|
||||
}
|
||||
|
||||
export function BootstrapDate(realmRec) {
|
||||
const cons = BootstrapConstructor(realmRec, DateConstructor, 'Date', 7, realmRec.Intrinsics['%Date.prototype%'], [
|
||||
['now', Date_now, 0],
|
||||
['parse', Date_parse, 1],
|
||||
['UTC', Date_UTC, 7],
|
||||
]);
|
||||
|
||||
realmRec.Intrinsics['%Date%'] = cons;
|
||||
}
|
||||
@@ -1,705 +0,0 @@
|
||||
import { surroundingAgent } from '../engine.mjs';
|
||||
import {
|
||||
Assert,
|
||||
DateFromTime,
|
||||
Day,
|
||||
HourFromTime,
|
||||
Invoke,
|
||||
LocalTime,
|
||||
LocalTZA,
|
||||
MakeDate,
|
||||
MakeDay,
|
||||
MakeTime,
|
||||
MinFromTime,
|
||||
MonthFromTime,
|
||||
msFromTime,
|
||||
msPerMinute,
|
||||
OrdinaryToPrimitive,
|
||||
SecFromTime,
|
||||
TimeClip,
|
||||
TimeWithinDay,
|
||||
ToNumber,
|
||||
ToPrimitive,
|
||||
ToObject,
|
||||
UTC,
|
||||
WeekDay,
|
||||
YearFromTime,
|
||||
} from '../abstract-ops/all.mjs';
|
||||
import {
|
||||
Type,
|
||||
Value,
|
||||
wellKnownSymbols,
|
||||
} from '../value.mjs';
|
||||
import { Q, X } from '../completion.mjs';
|
||||
import { StringPad } from '../runtime-semantics/all.mjs';
|
||||
import { BootstrapPrototype } from './Bootstrap.mjs';
|
||||
|
||||
|
||||
export function thisTimeValue(value) {
|
||||
if (Type(value) === 'Object' && 'DateValue' in value) {
|
||||
return value.DateValue;
|
||||
}
|
||||
return surroundingAgent.Throw('TypeError', 'NotATypeObject', 'Date', value);
|
||||
}
|
||||
|
||||
// 20.3.4.2 #sec-date.prototype.getdate
|
||||
function DateProto_getDate(args, { thisValue }) {
|
||||
const t = Q(thisTimeValue(thisValue));
|
||||
if (t.isNaN()) {
|
||||
return new Value(NaN);
|
||||
}
|
||||
return DateFromTime(LocalTime(t));
|
||||
}
|
||||
|
||||
// 20.3.4.3 #sec-date.prototype.getday
|
||||
function DateProto_getDay(args, { thisValue }) {
|
||||
const t = Q(thisTimeValue(thisValue));
|
||||
if (t.isNaN()) {
|
||||
return new Value(NaN);
|
||||
}
|
||||
return WeekDay(LocalTime(t));
|
||||
}
|
||||
|
||||
// 20.3.4.4 #sec-date.prototype.getfullyear
|
||||
function DateProto_getFullYear(args, { thisValue }) {
|
||||
const t = Q(thisTimeValue(thisValue));
|
||||
if (t.isNaN()) {
|
||||
return new Value(NaN);
|
||||
}
|
||||
return YearFromTime(LocalTime(t));
|
||||
}
|
||||
|
||||
// 20.3.4.5 #sec-date.prototype.gethours
|
||||
function DateProto_getHours(args, { thisValue }) {
|
||||
const t = Q(thisTimeValue(thisValue));
|
||||
if (t.isNaN()) {
|
||||
return new Value(NaN);
|
||||
}
|
||||
return HourFromTime(LocalTime(t));
|
||||
}
|
||||
|
||||
// 20.3.4.6 #sec-date.prototype.getmilliseconds
|
||||
function DateProto_getMilliseconds(args, { thisValue }) {
|
||||
const t = Q(thisTimeValue(thisValue));
|
||||
if (t.isNaN()) {
|
||||
return new Value(NaN);
|
||||
}
|
||||
return msFromTime(LocalTime(t));
|
||||
}
|
||||
|
||||
// 20.3.4.7 #sec-date.prototype.getminutes
|
||||
function DateProto_getMinutes(args, { thisValue }) {
|
||||
const t = Q(thisTimeValue(thisValue));
|
||||
if (t.isNaN()) {
|
||||
return new Value(NaN);
|
||||
}
|
||||
return MinFromTime(LocalTime(t));
|
||||
}
|
||||
|
||||
// 20.3.4.8 #sec-date.prototype.getmonth
|
||||
function DateProto_getMonth(args, { thisValue }) {
|
||||
const t = Q(thisTimeValue(thisValue));
|
||||
if (t.isNaN()) {
|
||||
return new Value(NaN);
|
||||
}
|
||||
return MonthFromTime(LocalTime(t));
|
||||
}
|
||||
|
||||
// 20.3.4.9 #sec-date.prototype.getseconds
|
||||
function DateProto_getSeconds(args, { thisValue }) {
|
||||
const t = Q(thisTimeValue(thisValue));
|
||||
if (t.isNaN()) {
|
||||
return new Value(NaN);
|
||||
}
|
||||
return SecFromTime(LocalTime(t));
|
||||
}
|
||||
|
||||
// 20.3.4.10 #sec-date.prototype.gettime
|
||||
function DateProto_getTime(args, { thisValue }) {
|
||||
return Q(thisTimeValue(thisValue));
|
||||
}
|
||||
|
||||
// 20.3.4.11 #sec-date.prototype.gettimezoneoffset
|
||||
function DateProto_getTimezoneOffset(args, { thisValue }) {
|
||||
const t = Q(thisTimeValue(thisValue));
|
||||
if (t.isNaN()) {
|
||||
return new Value(NaN);
|
||||
}
|
||||
return new Value((t.numberValue() - LocalTime(t).numberValue()) / msPerMinute);
|
||||
}
|
||||
|
||||
// 20.3.4.12 #sec-date.prototype.getutcdate
|
||||
function DateProto_getUTCDate(args, { thisValue }) {
|
||||
const t = Q(thisTimeValue(thisValue));
|
||||
if (t.isNaN()) {
|
||||
return new Value(NaN);
|
||||
}
|
||||
return DateFromTime(t);
|
||||
}
|
||||
|
||||
// 20.3.4.13 #sec-date.prototype.getutcday
|
||||
function DateProto_getUTCDay(args, { thisValue }) {
|
||||
const t = Q(thisTimeValue(thisValue));
|
||||
if (t.isNaN()) {
|
||||
return new Value(NaN);
|
||||
}
|
||||
return WeekDay(t);
|
||||
}
|
||||
|
||||
// 20.3.4.14 #sec-date.prototype.getutcfullyear
|
||||
function DateProto_getUTCFullYear(args, { thisValue }) {
|
||||
const t = Q(thisTimeValue(thisValue));
|
||||
if (t.isNaN()) {
|
||||
return new Value(NaN);
|
||||
}
|
||||
return YearFromTime(t);
|
||||
}
|
||||
|
||||
// 20.3.4.15 #sec-date.prototype.getutchours
|
||||
function DateProto_getUTCHours(args, { thisValue }) {
|
||||
const t = Q(thisTimeValue(thisValue));
|
||||
if (t.isNaN()) {
|
||||
return new Value(NaN);
|
||||
}
|
||||
return HourFromTime(t);
|
||||
}
|
||||
|
||||
// 20.3.4.16 #sec-date.prototype.getutcmilliseconds
|
||||
function DateProto_getUTCMilliseconds(args, { thisValue }) {
|
||||
const t = Q(thisTimeValue(thisValue));
|
||||
if (t.isNaN()) {
|
||||
return new Value(NaN);
|
||||
}
|
||||
return msFromTime(t);
|
||||
}
|
||||
|
||||
// 20.3.4.17 #sec-date.prototype.getutcminutes
|
||||
function DateProto_getUTCMinutes(args, { thisValue }) {
|
||||
const t = Q(thisTimeValue(thisValue));
|
||||
if (t.isNaN()) {
|
||||
return new Value(NaN);
|
||||
}
|
||||
return MinFromTime(t);
|
||||
}
|
||||
|
||||
// 20.3.4.18 #sec-date.prototype.getutcmonth
|
||||
function DateProto_getUTCMonth(args, { thisValue }) {
|
||||
const t = Q(thisTimeValue(thisValue));
|
||||
if (t.isNaN()) {
|
||||
return new Value(NaN);
|
||||
}
|
||||
return MonthFromTime(t);
|
||||
}
|
||||
|
||||
// 20.3.4.19 #sec-date.prototype.getutcseconds
|
||||
function DateProto_getUTCSeconds(args, { thisValue }) {
|
||||
const t = Q(thisTimeValue(thisValue));
|
||||
if (t.isNaN()) {
|
||||
return new Value(NaN);
|
||||
}
|
||||
return SecFromTime(t);
|
||||
}
|
||||
|
||||
// 20.3.4.20 #sec-date.prototype.setdate
|
||||
function DateProto_setDate([date = Value.undefined], { thisValue }) {
|
||||
const t = LocalTime(Q(thisTimeValue(thisValue)));
|
||||
const dt = Q(ToNumber(date));
|
||||
const newDate = MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), dt), TimeWithinDay(t));
|
||||
const u = TimeClip(UTC(newDate));
|
||||
thisValue.DateValue = u;
|
||||
return u;
|
||||
}
|
||||
|
||||
// 20.3.4.21 #sec-date.prototype.setfullyear
|
||||
function DateProto_setFullYear([year = Value.undefined, month, date], { thisValue }) {
|
||||
let t = Q(thisTimeValue(thisValue));
|
||||
t = t.isNaN() ? new Value(0) : LocalTime(t);
|
||||
const y = Q(ToNumber(year));
|
||||
let m;
|
||||
if (month !== undefined) {
|
||||
m = Q(ToNumber(month));
|
||||
} else {
|
||||
m = MonthFromTime(t);
|
||||
}
|
||||
let dt;
|
||||
if (date !== undefined) {
|
||||
dt = Q(ToNumber(date));
|
||||
} else {
|
||||
dt = DateFromTime(t);
|
||||
}
|
||||
const newDate = MakeDate(MakeDay(y, m, dt), TimeWithinDay(t));
|
||||
const u = TimeClip(UTC(newDate));
|
||||
thisValue.DateValue = u;
|
||||
return u;
|
||||
}
|
||||
|
||||
// 20.3.4.22 #sec-date.prototype.sethours
|
||||
function DateProto_setHours([hour = Value.undefined, min, sec, ms], { thisValue }) {
|
||||
const t = LocalTime(Q(thisTimeValue(thisValue)));
|
||||
const h = Q(ToNumber(hour));
|
||||
let m;
|
||||
if (min !== undefined) {
|
||||
m = Q(ToNumber(min));
|
||||
} else {
|
||||
m = MinFromTime(t);
|
||||
}
|
||||
let s;
|
||||
if (sec !== undefined) {
|
||||
s = Q(ToNumber(sec));
|
||||
} else {
|
||||
s = SecFromTime(t);
|
||||
}
|
||||
let milli;
|
||||
if (ms !== undefined) {
|
||||
milli = Q(ToNumber(ms));
|
||||
} else {
|
||||
milli = msFromTime(t);
|
||||
}
|
||||
const date = MakeDate(Day(t), MakeTime(h, m, s, milli));
|
||||
const u = TimeClip(UTC(date));
|
||||
thisValue.DateValue = u;
|
||||
return u;
|
||||
}
|
||||
|
||||
// 20.3.4.23 #sec-date.prototype.setmilliseconds
|
||||
function DateProto_setMilliseconds([ms = Value.undefined], { thisValue }) {
|
||||
const t = LocalTime(Q(thisTimeValue(thisValue)));
|
||||
ms = Q(ToNumber(ms));
|
||||
const time = MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms);
|
||||
const u = TimeClip(UTC(MakeDate(Day(t), time)));
|
||||
thisValue.DateValue = u;
|
||||
return u;
|
||||
}
|
||||
|
||||
// 20.3.4.24 #sec-date.prototype.setminutes
|
||||
function DateProto_setMinutes([min = Value.undefined, sec, ms], { thisValue }) {
|
||||
// 1. Let t be LocalTime(? thisTimeValue(this value)).
|
||||
const t = LocalTime(Q(thisTimeValue(thisValue)));
|
||||
// 2. Let m be ? ToNumber(min).
|
||||
const m = Q(ToNumber(min));
|
||||
let s;
|
||||
// 3. If sec is not present, let s be SecFromTime(t); otherwise, let s be ? ToNumber(sec).
|
||||
if (sec !== undefined) {
|
||||
s = Q(ToNumber(sec));
|
||||
} else {
|
||||
s = SecFromTime(t);
|
||||
}
|
||||
let milli;
|
||||
// 4. If ms is not present, let milli be msFromTime(t); otherwise, let milli be ? ToNumber(ms).
|
||||
if (ms !== undefined) {
|
||||
milli = Q(ToNumber(ms));
|
||||
} else {
|
||||
milli = msFromTime(t);
|
||||
}
|
||||
// 5. Let date be MakeDate(Day(t), MakeTime(HourFromTime(t), m, s, milli)).
|
||||
const date = MakeDate(Day(t), MakeTime(HourFromTime(t), m, s, milli));
|
||||
// 6. Let u be TimeClip(UTC(date)).
|
||||
const u = TimeClip(UTC(date));
|
||||
// 7. Set the [[DateValue]] internal slot of this Date object to u.
|
||||
thisValue.DateValue = u;
|
||||
// 8. Return u.
|
||||
return u;
|
||||
}
|
||||
|
||||
// 20.3.4.25 #sec-date.prototype.setmonth
|
||||
function DateProto_setMonth([month = Value.undefined, date], { thisValue }) {
|
||||
const t = LocalTime(Q(thisTimeValue(thisValue)));
|
||||
const m = Q(ToNumber(month));
|
||||
let dt;
|
||||
if (date !== undefined) {
|
||||
dt = Q(ToNumber(date));
|
||||
} else {
|
||||
dt = DateFromTime(t);
|
||||
}
|
||||
const newDate = MakeDate(MakeDay(YearFromTime(t), m, dt), TimeWithinDay(t));
|
||||
const u = TimeClip(UTC(newDate));
|
||||
thisValue.DateValue = u;
|
||||
return u;
|
||||
}
|
||||
|
||||
// 20.3.4.26 #sec-date.prototype.setseconds
|
||||
function DateProto_setSeconds([sec = Value.undefined, ms], { thisValue }) {
|
||||
const t = LocalTime(Q(thisTimeValue(thisValue)));
|
||||
const s = Q(ToNumber(sec));
|
||||
let milli;
|
||||
if (ms !== undefined) {
|
||||
milli = Q(ToNumber(ms));
|
||||
} else {
|
||||
milli = msFromTime(t);
|
||||
}
|
||||
const date = MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), s, milli));
|
||||
const u = TimeClip(UTC(date));
|
||||
thisValue.DateValue = u;
|
||||
return u;
|
||||
}
|
||||
|
||||
// 20.3.4.27 #sec-date.prototype.settime
|
||||
function DateProto_setTime([time = Value.undefined], { thisValue }) {
|
||||
Q(thisTimeValue(thisValue));
|
||||
const t = Q(ToNumber(time));
|
||||
const v = TimeClip(t);
|
||||
thisValue.DateValue = v;
|
||||
return v;
|
||||
}
|
||||
|
||||
// 20.3.4.28 #sec-date.prototype.setutcdate
|
||||
function DateProto_setUTCDate([date = Value.undefined], { thisValue }) {
|
||||
const t = Q(thisTimeValue(thisValue));
|
||||
const dt = Q(ToNumber(date));
|
||||
const newDate = MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), dt), TimeWithinDay(t));
|
||||
const v = TimeClip(newDate);
|
||||
thisValue.DateValue = v;
|
||||
return v;
|
||||
}
|
||||
|
||||
// 20.3.4.29 #sec-date.prototype.setutcfullyear
|
||||
function DateProto_setUTCFullYear([year = Value.undefined, month, date], { thisValue }) {
|
||||
let t = Q(thisTimeValue(thisValue));
|
||||
if (t.isNaN()) {
|
||||
t = new Value(0);
|
||||
}
|
||||
const y = Q(ToNumber(year));
|
||||
let m;
|
||||
if (month !== undefined) {
|
||||
m = Q(ToNumber(month));
|
||||
} else {
|
||||
m = MonthFromTime(t);
|
||||
}
|
||||
let dt;
|
||||
if (date !== undefined) {
|
||||
dt = Q(ToNumber(date));
|
||||
} else {
|
||||
dt = DateFromTime(t);
|
||||
}
|
||||
const newDate = MakeDate(MakeDay(y, m, dt), TimeWithinDay(t));
|
||||
const v = TimeClip(newDate);
|
||||
thisValue.DateValue = v;
|
||||
return v;
|
||||
}
|
||||
|
||||
// 20.3.4.30 #sec-date.prototype.setutchours
|
||||
function DateProto_setUTCHours([hour = Value.undefined, min, sec, ms], { thisValue }) {
|
||||
const t = Q(thisTimeValue(thisValue));
|
||||
const h = Q(ToNumber(hour));
|
||||
let m;
|
||||
if (min !== undefined) {
|
||||
m = Q(ToNumber(min));
|
||||
} else {
|
||||
m = MinFromTime(t);
|
||||
}
|
||||
let s;
|
||||
if (sec !== undefined) {
|
||||
s = Q(ToNumber(sec));
|
||||
} else {
|
||||
s = SecFromTime(t);
|
||||
}
|
||||
let milli;
|
||||
if (ms !== undefined) {
|
||||
milli = Q(ToNumber(ms));
|
||||
} else {
|
||||
milli = msFromTime(t);
|
||||
}
|
||||
const newDate = MakeDate(Day(t), MakeTime(h, m, s, milli));
|
||||
const v = TimeClip(newDate);
|
||||
thisValue.DateValue = v;
|
||||
return v;
|
||||
}
|
||||
|
||||
// 20.3.4.31 #sec-date.prototype.setutcmilliseconds
|
||||
function DateProto_setUTCMilliseconds([ms = Value.undefined], { thisValue }) {
|
||||
const t = Q(thisTimeValue(thisValue));
|
||||
const milli = Q(ToNumber(ms));
|
||||
const time = MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), milli);
|
||||
const v = TimeClip(MakeDate(Day(t), time));
|
||||
thisValue.DateValue = v;
|
||||
return v;
|
||||
}
|
||||
|
||||
// 20.3.4.32 #sec-date.prototype.setutcminutes
|
||||
function DateProto_setUTCMinutes([min = Value.undefined, sec, ms], { thisValue }) {
|
||||
const t = Q(thisTimeValue(thisValue));
|
||||
const m = Q(ToNumber(min));
|
||||
let s;
|
||||
if (sec !== undefined) {
|
||||
s = Q(ToNumber(sec));
|
||||
} else {
|
||||
s = SecFromTime(t);
|
||||
}
|
||||
let milli;
|
||||
if (ms !== undefined) {
|
||||
milli = Q(ToNumber(ms));
|
||||
} else {
|
||||
milli = msFromTime(t);
|
||||
}
|
||||
const date = MakeDate(Day(t), MakeTime(HourFromTime(t), m, s, milli));
|
||||
const v = TimeClip(date);
|
||||
thisValue.DateValue = v;
|
||||
return v;
|
||||
}
|
||||
|
||||
// 20.3.4.33 #sec-date.prototype.setutcmonth
|
||||
function DateProto_setUTCMonth([month = Value.undefined, date], { thisValue }) {
|
||||
const t = Q(thisTimeValue(thisValue));
|
||||
const m = Q(ToNumber(month));
|
||||
let dt;
|
||||
if (date !== undefined) {
|
||||
dt = Q(ToNumber(date));
|
||||
} else {
|
||||
dt = DateFromTime(t);
|
||||
}
|
||||
const newDate = MakeDate(MakeDay(YearFromTime(t), m, dt), TimeWithinDay(t));
|
||||
const v = TimeClip(newDate);
|
||||
thisValue.DateValue = v;
|
||||
return v;
|
||||
}
|
||||
|
||||
// 20.3.4.34 #sec-date.prototype.setutcseconds
|
||||
function DateProto_setUTCSeconds([sec = Value.undefined, ms], { thisValue }) {
|
||||
const t = Q(thisTimeValue(thisValue));
|
||||
const s = Q(ToNumber(sec));
|
||||
let milli;
|
||||
if (ms !== undefined) {
|
||||
milli = Q(ToNumber(ms));
|
||||
} else {
|
||||
milli = msFromTime(t);
|
||||
}
|
||||
const date = MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), s, milli));
|
||||
const v = TimeClip(date);
|
||||
thisValue.DateValue = v;
|
||||
return v;
|
||||
}
|
||||
|
||||
// 20.3.4.35 #sec-date.prototype.todatestring
|
||||
function DateProto_toDateString(args, { thisValue }) {
|
||||
const O = thisValue;
|
||||
if (Type(O) !== 'Object') {
|
||||
return surroundingAgent.Throw('TypeError', 'NotATypeObject', 'Date', O);
|
||||
}
|
||||
const tv = Q(thisTimeValue(O));
|
||||
if (tv.isNaN()) {
|
||||
return new Value('Invalid Date');
|
||||
}
|
||||
const t = LocalTime(tv);
|
||||
return DateString(t);
|
||||
}
|
||||
|
||||
// 20.3.4.36 #sec-date.prototype.toisostring
|
||||
function DateProto_toISOString(args, { thisValue }) {
|
||||
const t = Q(thisTimeValue(thisValue));
|
||||
if (!Number.isFinite(t.numberValue())) {
|
||||
return surroundingAgent.Throw('RangeError', 'DateInvalidTime');
|
||||
}
|
||||
const year = YearFromTime(t).numberValue();
|
||||
const month = MonthFromTime(t).numberValue() + 1;
|
||||
const date = DateFromTime(t).numberValue();
|
||||
const hour = HourFromTime(t).numberValue();
|
||||
const min = MinFromTime(t).numberValue();
|
||||
const sec = SecFromTime(t).numberValue();
|
||||
const ms = msFromTime(t).numberValue();
|
||||
|
||||
// TODO: figure out if there can be invalid years.
|
||||
let YYYY = String(year);
|
||||
if (year < 0 || year > 9999) {
|
||||
YYYY = year < 0 ? `${String(year).padStart(6, '0')}` : `+${String(year).padStart(6, '0')}`;
|
||||
}
|
||||
const MM = String(month).padStart(2, '0');
|
||||
const DD = String(date).padStart(2, '0');
|
||||
const HH = String(hour).padStart(2, '0');
|
||||
const mm = String(min).padStart(2, '0');
|
||||
const ss = String(sec).padStart(2, '0');
|
||||
const sss = String(ms).padStart(3, '0');
|
||||
const format = `${YYYY}-${MM}-${DD}T${HH}:${mm}:${ss}.${sss}Z`;
|
||||
return new Value(format);
|
||||
}
|
||||
|
||||
// 20.3.4.37 #sec-date.prototype.tojson
|
||||
function DateProto_toJSON(args, { thisValue }) {
|
||||
const O = Q(ToObject(thisValue));
|
||||
const tv = Q(ToPrimitive(O, 'Number'));
|
||||
if (Type(tv) === 'Number' && !Number.isFinite(tv.numberValue())) {
|
||||
return Value.null;
|
||||
}
|
||||
return Q(Invoke(O, new Value('toISOString')));
|
||||
}
|
||||
|
||||
// 20.3.4.38 #sec-date.prototype.tolocaledatestring
|
||||
function DateProto_toLocaleDateString() {
|
||||
// TODO: implement this function.
|
||||
return surroundingAgent.Throw('Error', 'Raw', 'Date.prototype.toLocaleDateString is not implemented');
|
||||
}
|
||||
|
||||
// 20.3.4.39 #sec-date.prototype.tolocalestring
|
||||
function DateProto_toLocaleString() {
|
||||
// TODO: implement this function.
|
||||
return surroundingAgent.Throw('Error', 'Raw', 'Date.prototype.toLocaleString is not implemented');
|
||||
}
|
||||
|
||||
// 20.3.4.40 #sec-date.prototype.tolocaletimestring
|
||||
function DateProto_toLocaleTimeString() {
|
||||
// TODO: implement this function.
|
||||
return surroundingAgent.Throw('Error', 'Raw', 'Date.prototype.toLocaleTimeString is not implemented');
|
||||
}
|
||||
|
||||
// 20.3.4.41 #sec-date.prototype.tostring
|
||||
function DateProto_toString(args, { thisValue }) {
|
||||
const tv = Q(thisTimeValue(thisValue));
|
||||
return ToDateString(tv);
|
||||
}
|
||||
|
||||
// 20.3.4.41.1 #sec-timestring
|
||||
function TimeString(tv) {
|
||||
Assert(Type(tv) === 'Number');
|
||||
Assert(!tv.isNaN());
|
||||
const hour = String(HourFromTime(tv).numberValue()).padStart(2, '0');
|
||||
const minute = String(MinFromTime(tv).numberValue()).padStart(2, '0');
|
||||
const second = String(SecFromTime(tv).numberValue()).padStart(2, '0');
|
||||
return new Value(`${hour}:${minute}:${second} GMT`);
|
||||
}
|
||||
|
||||
// Table 46 #sec-todatestring-day-names
|
||||
const daysOfTheWeek = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
|
||||
// Table 47 #sec-todatestring-month-names
|
||||
const monthsOfTheYear = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
|
||||
|
||||
// 20.3.4.41.2 #sec-datestring
|
||||
function DateString(tv) {
|
||||
Assert(Type(tv) === 'Number');
|
||||
Assert(!tv.isNaN());
|
||||
const weekday = daysOfTheWeek[WeekDay(tv).numberValue()];
|
||||
const month = monthsOfTheYear[MonthFromTime(tv).numberValue()];
|
||||
const day = String(DateFromTime(tv).numberValue()).padStart(2, '0');
|
||||
const yv = YearFromTime(tv).numberValue();
|
||||
const yearSign = yv >= 0 ? '' : '-';
|
||||
const year = new Value(String(Math.abs(yv)));
|
||||
const paddedYear = X(StringPad(year, new Value(4), new Value('0'), 'start')).stringValue();
|
||||
return new Value(`${weekday} ${month} ${day} ${yearSign}${paddedYear}`);
|
||||
}
|
||||
|
||||
// 20.3.4.41.3 #sec-timezoneestring
|
||||
export function TimeZoneString(tv) {
|
||||
Assert(Type(tv) === 'Number');
|
||||
Assert(!tv.isNaN());
|
||||
const offset = LocalTZA(tv, true);
|
||||
const offsetSign = offset >= 0 ? '+' : '-';
|
||||
const offsetMin = String(MinFromTime(new Value(Math.abs(offset))).numberValue()).padStart(2, '0');
|
||||
const offsetHour = String(HourFromTime(new Value(Math.abs(offset))).numberValue()).padStart(2, '0');
|
||||
const tzName = '';
|
||||
return new Value(`${offsetSign}${offsetHour}${offsetMin}${tzName}`);
|
||||
}
|
||||
|
||||
// 20.3.4.41.4 #sec-todatestring
|
||||
export function ToDateString(tv) {
|
||||
Assert(Type(tv) === 'Number');
|
||||
if (tv.isNaN()) {
|
||||
return new Value('Invalid Date');
|
||||
}
|
||||
const t = LocalTime(tv);
|
||||
return new Value(`${DateString(t).stringValue()} ${TimeString(t).stringValue()}${TimeZoneString(t).stringValue()}`);
|
||||
}
|
||||
|
||||
// 20.3.4.42 #sec-date.prototype.totimestring
|
||||
function DateProto_toTimeString(args, { thisValue }) {
|
||||
const O = thisValue;
|
||||
if (Type(O) !== 'Object') {
|
||||
return surroundingAgent.Throw('TypeError', 'NotATypeObject', 'Date', O);
|
||||
}
|
||||
const tv = Q(thisTimeValue(O));
|
||||
if (tv.isNaN()) {
|
||||
return new Value('Invalid Date');
|
||||
}
|
||||
const t = LocalTime(tv);
|
||||
return new Value(`${TimeString(t).stringValue()}${TimeZoneString(tv).stringValue()}`);
|
||||
}
|
||||
|
||||
// 20.3.4.43 #sec-date.prototype.toutcstring
|
||||
function DateProto_toUTCString(args, { thisValue }) {
|
||||
const O = thisValue;
|
||||
if (Type(O) !== 'Object') {
|
||||
return surroundingAgent.Throw('TypeError', 'NotATypeObject', 'Date', O);
|
||||
}
|
||||
const tv = Q(thisTimeValue(O));
|
||||
if (tv.isNaN()) {
|
||||
return new Value('Invalid Date');
|
||||
}
|
||||
const weekday = daysOfTheWeek[WeekDay(tv).numberValue()];
|
||||
const month = monthsOfTheYear[MonthFromTime(tv).numberValue()];
|
||||
const day = String(DateFromTime(tv).numberValue()).padStart(2, '0');
|
||||
const yv = YearFromTime(tv).numberValue();
|
||||
const yearSign = yv >= 0 ? '' : '-';
|
||||
const year = new Value(String(Math.abs(yv)));
|
||||
const paddedYear = X(StringPad(year, new Value(4), new Value('0'), 'start')).stringValue();
|
||||
return new Value(`${weekday}, ${day} ${month} ${yearSign}${paddedYear} ${TimeString(tv).stringValue()}`);
|
||||
}
|
||||
|
||||
// 20.3.4.44 #sec-date.prototype.valueof
|
||||
function DateProto_valueOf(args, { thisValue }) {
|
||||
return Q(thisTimeValue(thisValue));
|
||||
}
|
||||
|
||||
// 20.3.4.45 #sec-date.prototype-@@toprimitive
|
||||
function DateProto_toPrimitive([hint = Value.undefined], { thisValue }) {
|
||||
const O = thisValue;
|
||||
if (Type(O) !== 'Object') {
|
||||
return surroundingAgent.Throw('TypeError', 'NotATypeObject', 'Date', O);
|
||||
}
|
||||
let tryFirst;
|
||||
if (Type(hint) === 'String' && (hint.stringValue() === 'string' || hint.stringValue() === 'default')) {
|
||||
tryFirst = new Value('string');
|
||||
} else if (Type(hint) === 'String' && hint.stringValue() === 'number') {
|
||||
tryFirst = new Value('number');
|
||||
} else {
|
||||
return surroundingAgent.Throw('TypeError', 'InvalidHint', hint);
|
||||
}
|
||||
return Q(OrdinaryToPrimitive(O, tryFirst));
|
||||
}
|
||||
|
||||
export function BootstrapDatePrototype(realmRec) {
|
||||
const proto = BootstrapPrototype(realmRec, [
|
||||
['getDate', DateProto_getDate, 0],
|
||||
['getDay', DateProto_getDay, 0],
|
||||
['getFullYear', DateProto_getFullYear, 0],
|
||||
['getHours', DateProto_getHours, 0],
|
||||
['getMilliseconds', DateProto_getMilliseconds, 0],
|
||||
['getMinutes', DateProto_getMinutes, 0],
|
||||
['getMonth', DateProto_getMonth, 0],
|
||||
['getSeconds', DateProto_getSeconds, 0],
|
||||
['getTime', DateProto_getTime, 0],
|
||||
['getTimezoneOffset', DateProto_getTimezoneOffset, 0],
|
||||
['getUTCDate', DateProto_getUTCDate, 0],
|
||||
['getUTCDay', DateProto_getUTCDay, 0],
|
||||
['getUTCFullYear', DateProto_getUTCFullYear, 0],
|
||||
['getUTCHours', DateProto_getUTCHours, 0],
|
||||
['getUTCMilliseconds', DateProto_getUTCMilliseconds, 0],
|
||||
['getUTCMinutes', DateProto_getUTCMinutes, 0],
|
||||
['getUTCMonth', DateProto_getUTCMonth, 0],
|
||||
['getUTCSeconds', DateProto_getUTCSeconds, 0],
|
||||
['setDate', DateProto_setDate, 1],
|
||||
['setFullYear', DateProto_setFullYear, 3],
|
||||
['setHours', DateProto_setHours, 4],
|
||||
['setMilliseconds', DateProto_setMilliseconds, 1],
|
||||
['setMinutes', DateProto_setMinutes, 3],
|
||||
['setMonth', DateProto_setMonth, 2],
|
||||
['setSeconds', DateProto_setSeconds, 2],
|
||||
['setTime', DateProto_setTime, 1],
|
||||
['setUTCDate', DateProto_setUTCDate, 1],
|
||||
['setUTCFullYear', DateProto_setUTCFullYear, 3],
|
||||
['setUTCHours', DateProto_setUTCHours, 4],
|
||||
['setUTCMilliseconds', DateProto_setUTCMilliseconds, 1],
|
||||
['setUTCMinutes', DateProto_setUTCMinutes, 3],
|
||||
['setUTCMonth', DateProto_setUTCMonth, 2],
|
||||
['setUTCSeconds', DateProto_setUTCSeconds, 2],
|
||||
['toDateString', DateProto_toDateString, 0],
|
||||
['toISOString', DateProto_toISOString, 0],
|
||||
['toJSON', DateProto_toJSON, 1],
|
||||
['toLocaleDateString', DateProto_toLocaleDateString, 0],
|
||||
['toLocaleString', DateProto_toLocaleString, 0],
|
||||
['toLocaleTimeString', DateProto_toLocaleTimeString, 0],
|
||||
['toString', DateProto_toString, 0],
|
||||
['toTimeString', DateProto_toTimeString, 0],
|
||||
['toUTCString', DateProto_toUTCString, 0],
|
||||
['valueOf', DateProto_valueOf, 0],
|
||||
[wellKnownSymbols.toPrimitive, DateProto_toPrimitive, 1, { Writable: Value.false, Enumerable: Value.false, Configurable: Value.true }],
|
||||
], realmRec.Intrinsics['%Object.prototype%']);
|
||||
|
||||
realmRec.Intrinsics['%Date.prototype%'] = proto;
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
import {
|
||||
DefinePropertyOrThrow,
|
||||
OrdinaryCreateFromConstructor,
|
||||
ToString,
|
||||
} from '../abstract-ops/all.mjs';
|
||||
import {
|
||||
Descriptor,
|
||||
Value,
|
||||
} from '../value.mjs';
|
||||
import { Q, X } from '../completion.mjs';
|
||||
import { surroundingAgent } from '../engine.mjs';
|
||||
import { captureStack } from '../helpers.mjs';
|
||||
import { BootstrapConstructor } from './Bootstrap.mjs';
|
||||
|
||||
// #sec-error-constructor
|
||||
function ErrorConstructor([message = Value.undefined], { NewTarget }) {
|
||||
// 1. If NewTarget is undefined, let newTarget be the active function object; else let newTarget be NewTarget.
|
||||
let newTarget;
|
||||
if (NewTarget === Value.undefined) {
|
||||
newTarget = surroundingAgent.activeFunctionObject;
|
||||
} else {
|
||||
newTarget = NewTarget;
|
||||
}
|
||||
// 2. Let O be ? OrdinaryCreateFromConstructor(newTarget, "%Error.prototype%", « [[ErrorData]] »).
|
||||
const O = Q(OrdinaryCreateFromConstructor(newTarget, '%Error.prototype%', ['ErrorData']));
|
||||
// 3. If message is not undefined, then
|
||||
if (message !== Value.undefined) {
|
||||
// a. Let msg be ? ToString(message).
|
||||
const msg = Q(ToString(message));
|
||||
// b. Let msgDesc be the PropertyDescriptor { [[Value]]: msg, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true }.
|
||||
const msgDesc = Descriptor({
|
||||
Value: msg,
|
||||
Writable: Value.true,
|
||||
Enumerable: Value.false,
|
||||
Configurable: Value.true,
|
||||
});
|
||||
// c. Perform ! DefinePropertyOrThrow(O, "message", msgDesc).
|
||||
X(DefinePropertyOrThrow(O, new Value('message'), msgDesc));
|
||||
}
|
||||
|
||||
X(captureStack(O)); // NON-SPEC
|
||||
|
||||
// 4. Return O.
|
||||
return O;
|
||||
}
|
||||
|
||||
export function BootstrapError(realmRec) {
|
||||
const error = BootstrapConstructor(realmRec, ErrorConstructor, 'Error', 1, realmRec.Intrinsics['%Error.prototype%'], []);
|
||||
|
||||
realmRec.Intrinsics['%Error%'] = error;
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
import {
|
||||
surroundingAgent,
|
||||
} from '../engine.mjs';
|
||||
import {
|
||||
Type,
|
||||
Value,
|
||||
} from '../value.mjs';
|
||||
import {
|
||||
Get,
|
||||
ToString,
|
||||
} from '../abstract-ops/all.mjs';
|
||||
import { Q } from '../completion.mjs';
|
||||
import { BootstrapPrototype } from './Bootstrap.mjs';
|
||||
|
||||
// #sec-error.prototype.tostring
|
||||
function ErrorProto_toString(args, { thisValue }) {
|
||||
// 1. Let O be this value.
|
||||
const O = thisValue;
|
||||
// 2. If Type(O) is not Object, throw a TypeError exception.
|
||||
if (Type(O) !== 'Object') {
|
||||
return surroundingAgent.Throw('TypeError', 'NotAnObject', O);
|
||||
}
|
||||
// 3. Let name be ? Get(O, "name").
|
||||
let name = Q(Get(O, new Value('name')));
|
||||
// 4. If name is undefined, set name to "Error"; otherwise set name to ? ToString(name).
|
||||
if (name === Value.undefined) {
|
||||
name = new Value('Error');
|
||||
} else {
|
||||
name = Q(ToString(name));
|
||||
}
|
||||
// 5. Let msg be ? Get(O, "message").
|
||||
let msg = Q(Get(O, new Value('message')));
|
||||
// 6. If msg is undefined, set msg to the empty String; otherwise set msg to ? ToString(msg).
|
||||
if (msg === Value.undefined) {
|
||||
msg = new Value('');
|
||||
} else {
|
||||
msg = Q(ToString(msg));
|
||||
}
|
||||
// 7. If name is the empty String, return msg.
|
||||
if (name.stringValue() === '') {
|
||||
return msg;
|
||||
}
|
||||
// 8. If msg is the empty String, return name.
|
||||
if (msg.stringValue() === '') {
|
||||
return name;
|
||||
}
|
||||
// 9. Return the string-concatenation of name, the code unit 0x003A (COLON), the code unit 0x0020 (SPACE), and msg.
|
||||
return new Value(`${name.stringValue()}: ${msg.stringValue()}`);
|
||||
}
|
||||
|
||||
export function BootstrapErrorPrototype(realmRec) {
|
||||
const proto = BootstrapPrototype(realmRec, [
|
||||
['toString', ErrorProto_toString, 0],
|
||||
['message', new Value('')],
|
||||
['name', new Value('Error')],
|
||||
], realmRec.Intrinsics['%Object.prototype%']);
|
||||
|
||||
realmRec.Intrinsics['%Error.prototype%'] = proto;
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
import { surroundingAgent } from '../engine.mjs';
|
||||
import { Value } from '../value.mjs';
|
||||
import { IsCallable, OrdinaryCreateFromConstructor } from '../abstract-ops/all.mjs';
|
||||
import { Q } from '../completion.mjs';
|
||||
import { BootstrapConstructor } from './Bootstrap.mjs';
|
||||
|
||||
// #sec-finalization-registry-cleanup-callback
|
||||
function FinalizationRegistryConstructor([cleanupCallback = Value.undefined], { NewTarget }) {
|
||||
// 1. If NewTarget is undefined, throw a TypeError exception.
|
||||
if (NewTarget === Value.undefined) {
|
||||
return surroundingAgent.Throw('TypeError', 'NotAFunction', 'FinalizationRegistry');
|
||||
}
|
||||
// 2. If IsCallable(cleanupCallback) is false, throw a TypeError exception.
|
||||
if (IsCallable(cleanupCallback) === Value.false) {
|
||||
return surroundingAgent.Throw('TypeError', 'NotAFunction', cleanupCallback);
|
||||
}
|
||||
// 3. Let finalizationGroup be ? OrdinaryCreateFromConstructor(NewTarget, "%FinalizationRegistryPrototype%", « [[Realm]], [[CleanupCallback]], [[Cells]] »).
|
||||
const finalizationGroup = Q(OrdinaryCreateFromConstructor(NewTarget, '%FinalizationRegistry.prototype%', [
|
||||
'Realm',
|
||||
'CleanupCallback',
|
||||
'Cells',
|
||||
]));
|
||||
// 4. Let fn be the active function object.
|
||||
const fn = surroundingAgent.activeFunctionObject;
|
||||
// 5. Set finalizationGroup.[[Realm]] to fn.[[Realm]].
|
||||
finalizationGroup.Realm = fn.Realm;
|
||||
// 6. Set finalizationGroup.[[CleanupCallback]] to cleanupCallback.
|
||||
finalizationGroup.CleanupCallback = cleanupCallback;
|
||||
// 7. Set finalizationGroup.[[Cells]] to be an empty List.
|
||||
finalizationGroup.Cells = [];
|
||||
// 8. Return finalizationGroup.
|
||||
return finalizationGroup;
|
||||
}
|
||||
|
||||
export function BootstrapFinalizationRegistry(realmRec) {
|
||||
const cons = BootstrapConstructor(
|
||||
realmRec, FinalizationRegistryConstructor, 'FinalizationRegistry', 1,
|
||||
realmRec.Intrinsics['%FinalizationRegistry.prototype%'], [],
|
||||
);
|
||||
|
||||
realmRec.Intrinsics['%FinalizationRegistry%'] = cons;
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
import { surroundingAgent } from '../engine.mjs';
|
||||
import { Value, Type } from '../value.mjs';
|
||||
import {
|
||||
CleanupFinalizationRegistry,
|
||||
IsCallable,
|
||||
RequireInternalSlot,
|
||||
SameValue,
|
||||
} from '../abstract-ops/all.mjs';
|
||||
import { Q } from '../completion.mjs';
|
||||
import { BootstrapPrototype } from './Bootstrap.mjs';
|
||||
|
||||
// #sec-finalization-registry.prototype.cleanupSome
|
||||
function FinalizationRegistryProto_cleanupSome([callback = Value.undefined], { thisValue }) {
|
||||
// 1. Let finalizationRegistry be the this value.
|
||||
const finalizationRegistry = thisValue;
|
||||
// 2. Perform ? RequireInternalSlot(finalizationRegistry, [[Cells]]).
|
||||
Q(RequireInternalSlot(finalizationRegistry, 'Cells'));
|
||||
// 3. If callback is present and IsCallable(callback) is false, throw a TypeError exception.
|
||||
if (callback !== Value.undefined && IsCallable(callback) === Value.false) {
|
||||
return surroundingAgent.Throw('TypeError', 'NotAFunction', callback);
|
||||
}
|
||||
// 4. Perform ? CleanupFinalizationRegistry(finalizationRegistry, callback).
|
||||
Q(CleanupFinalizationRegistry(finalizationRegistry, callback));
|
||||
// 5. Return *undefined*.
|
||||
return Value.undefined;
|
||||
}
|
||||
|
||||
// #sec-finalization-registry.prototype.register
|
||||
function FinalizationRegistryProto_register([target = Value.undefined, heldValue = Value.undefined, unregisterToken = Value.undefined], { thisValue }) {
|
||||
// 1. Let finalizationRegistry be the this value.
|
||||
const finalizationRegistry = thisValue;
|
||||
// 2. Perform ? RequireInternalSlot(finalizationRegistry, [[Cells]]).
|
||||
Q(RequireInternalSlot(finalizationRegistry, 'Cells'));
|
||||
// 3. If Type(target) is not Object, throw a TypeError exception.
|
||||
if (Type(target) !== 'Object') {
|
||||
return surroundingAgent.Throw('TypeError', 'NotAnObject', target);
|
||||
}
|
||||
// 4. If SameValue(target, heldValue), throw a TypeError exception.
|
||||
if (SameValue(target, heldValue) === Value.true) {
|
||||
return surroundingAgent.Throw('TypeError', 'TargetMatchesHeldValue', heldValue);
|
||||
}
|
||||
// 5. If Type(unregisterToken) is not Object,
|
||||
if (Type(unregisterToken) !== 'Object') {
|
||||
// a. If unregisterToken is not undefined, throw a TypeError exception.
|
||||
if (unregisterToken !== Value.undefined) {
|
||||
return surroundingAgent.Throw('TypeError', 'NotAnObject', unregisterToken);
|
||||
}
|
||||
// b. Set unregisterToken to empty.
|
||||
unregisterToken = undefined;
|
||||
}
|
||||
// 6. Let cell be the Record { [[WeakRefTarget]] : target, [[HeldValue]]: heldValue, [[UnregisterToken]]: unregisterToken }.
|
||||
const cell = {
|
||||
WeakRefTarget: target,
|
||||
HeldValue: heldValue,
|
||||
UnregisterToken: unregisterToken,
|
||||
};
|
||||
// 7. Append cell to finalizationRegistry.[[Cells]].
|
||||
finalizationRegistry.Cells.push(cell);
|
||||
// 8. Return undefined.
|
||||
return Value.undefined;
|
||||
}
|
||||
|
||||
// #sec-finalization-registry.prototype.unregister
|
||||
function FinalizationRegistryProto_unregister([unregisterToken = Value.undefined], { thisValue }) {
|
||||
// 1. Let finalizationRegistry be the this value.
|
||||
const finalizationRegistry = thisValue;
|
||||
// 2. Perform ? RequireInternalSlot(finalizationRegistry, [[Cells]]).
|
||||
Q(RequireInternalSlot(finalizationRegistry, 'Cells'));
|
||||
// 3. If Type(unregisterToken) is not Object, throw a TypeError exception.
|
||||
if (Type(unregisterToken) !== 'Object') {
|
||||
return surroundingAgent.Throw('TypeError', 'NotAnObject', unregisterToken);
|
||||
}
|
||||
// 4. Let removed be false.
|
||||
let removed = Value.false;
|
||||
// 5. For each Record { [[WeakRefTarget]], [[HeldValue]], [[UnregisterToken]] } cell that is an element of finalizationRegistry.[[Cells]], do
|
||||
finalizationRegistry.Cells = finalizationRegistry.Cells.filter((cell) => {
|
||||
let r = true;
|
||||
// a. If cell.[[UnregisterToken]] is not empty and SameValue(cell.[[UnregisterToken]], unregisterToken) is true, then
|
||||
if (cell.UnregisterToken !== undefined && SameValue(cell.UnregisterToken, unregisterToken) === Value.true) {
|
||||
// i. Remove cell from finalizationRegistry.Cells.
|
||||
r = false;
|
||||
// ii. Set removed to true.
|
||||
removed = Value.true;
|
||||
}
|
||||
return r;
|
||||
});
|
||||
// 6. Return removed.
|
||||
return removed;
|
||||
}
|
||||
|
||||
export function BootstrapFinalizationRegistryPrototype(realmRec) {
|
||||
const proto = BootstrapPrototype(realmRec, [
|
||||
surroundingAgent.feature('cleanup-some')
|
||||
? ['cleanupSome', FinalizationRegistryProto_cleanupSome, 0]
|
||||
: undefined,
|
||||
['register', FinalizationRegistryProto_register, 2],
|
||||
['unregister', FinalizationRegistryProto_unregister, 1],
|
||||
], realmRec.Intrinsics['%Object.prototype%'], 'FinalizationRegistry');
|
||||
|
||||
realmRec.Intrinsics['%FinalizationRegistry.prototype%'] = proto;
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
import { Value, Type } from '../value.mjs';
|
||||
import { surroundingAgent } from '../engine.mjs';
|
||||
import {
|
||||
Assert,
|
||||
SameValue,
|
||||
OrdinaryObjectCreate,
|
||||
CreateIterResultObject,
|
||||
} from '../abstract-ops/all.mjs';
|
||||
import { Q } from '../completion.mjs';
|
||||
import { BootstrapPrototype } from './Bootstrap.mjs';
|
||||
|
||||
// #sec-createforiniterator
|
||||
export function CreateForInIterator(object) {
|
||||
// 1. Assert: Type(object) is Object.
|
||||
Assert(Type(object) === 'Object');
|
||||
// 2. Let iterator be ObjectCreate(%ForInIteratorPrototype%, « [[Object]], [[ObjectWasVisited]], [[VisitedKeys]], [[RemainingKeys]] »).
|
||||
const iterator = OrdinaryObjectCreate(surroundingAgent.intrinsic('%ForInIteratorPrototype%'), [
|
||||
'Object',
|
||||
'ObjectWasVisited',
|
||||
'VisitedKeys',
|
||||
'RemainingKeys',
|
||||
]);
|
||||
// 3. Set iterator.[[Object]] to object.
|
||||
iterator.Object = object;
|
||||
// 4. Set iterator.[[ObjectWasVisited]] to false.
|
||||
iterator.ObjectWasVisited = Value.false;
|
||||
// 5. Set iterator.[[VisitedKeys]] to a new empty List.
|
||||
iterator.VisitedKeys = [];
|
||||
// 6. Set iterator.[[RemainingKeys]] to a new empty List.
|
||||
iterator.RemainingKeys = [];
|
||||
// 7. Return iterator.
|
||||
return iterator;
|
||||
}
|
||||
|
||||
// #sec-%foriniteratorprototype%.next
|
||||
function ForInIteratorPrototype_next(args, { thisValue }) {
|
||||
// 1. Let O be this value.
|
||||
const O = thisValue;
|
||||
// 2. Assert: Type(O) is Object.
|
||||
Assert(Type(O) === 'Object');
|
||||
// 3. Assert: O has all the internal slot sof a For-In Iterator Instance.
|
||||
Assert('Object' in O && 'ObjectWasVisited' in O && 'VisitedKeys' in O && 'RemainingKeys in O');
|
||||
// 4. Let object be O.[[Object]].
|
||||
let object = O.Object;
|
||||
// 5. Let visited be O.[[VisitedKeys]].
|
||||
const visited = O.VisitedKeys;
|
||||
// 6. Let remaining be O.[[RemainingKeys]].
|
||||
const remaining = O.RemainingKeys;
|
||||
// 7. Repeat,
|
||||
while (true) {
|
||||
// a. If O.[[ObjectWasVisited]] is false, then
|
||||
if (O.ObjectWasVisited === Value.false) {
|
||||
// i. Let keys be ? object.[[OwnPropertyKeys]]().
|
||||
const keys = Q(object.OwnPropertyKeys());
|
||||
// ii. for each key of keys in List order, do
|
||||
for (const key of keys) {
|
||||
// 1. If Type(key) is String, then
|
||||
if (Type(key) === 'String') {
|
||||
// a. Append key to remaining.
|
||||
remaining.push(key);
|
||||
}
|
||||
}
|
||||
// iii. Set O.ObjectWasVisited to true.
|
||||
O.ObjectWasVisited = Value.true;
|
||||
}
|
||||
// b. Repeat, while remaining is not empty,
|
||||
while (remaining.length > 0) {
|
||||
// i. Remove the first element from remaining and let r be the value of the element.
|
||||
const r = remaining.shift();
|
||||
// ii. If there does not exist an element v of visisted such that SameValue(r, v) is true, then
|
||||
if (!visited.find((v) => SameValue(r, v) === Value.true)) {
|
||||
// 1. Let desc be ? object.[[GetOwnProperty]](r).
|
||||
const desc = Q(object.GetOwnProperty(r));
|
||||
// 2. If desc is not undefined, then,
|
||||
if (desc !== Value.undefined) {
|
||||
// a. Append r to visited.
|
||||
visited.push(r);
|
||||
// b. If desc.[[Enumerable]] is true, return CreateIterResultObject(r, false).
|
||||
if (desc.Enumerable === Value.true) {
|
||||
return CreateIterResultObject(r, Value.false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// c. Set object to ? object.[[GetPrototypeOf]]().
|
||||
object = Q(object.GetPrototypeOf());
|
||||
// d. Set O.Object to object.
|
||||
O.Object = object;
|
||||
// e. Set O.ObjectWasVisited to false.
|
||||
O.ObjectWasVisited = Value.false;
|
||||
// f. If object is null, return CreateIterResultObject(undefined, true).
|
||||
if (object === Value.null) {
|
||||
return CreateIterResultObject(Value.undefined, Value.true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function BootstrapForInIteratorPrototype(realmRec) {
|
||||
const proto = BootstrapPrototype(realmRec, [
|
||||
['next', ForInIteratorPrototype_next, 0],
|
||||
], realmRec.Intrinsics['%IteratorPrototype%']);
|
||||
|
||||
realmRec.Intrinsics['%ForInIteratorPrototype%'] = proto;
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
import { surroundingAgent } from '../engine.mjs';
|
||||
import { Q } from '../completion.mjs';
|
||||
import { CreateDynamicFunction } from '../runtime-semantics/all.mjs';
|
||||
import { BootstrapConstructor } from './Bootstrap.mjs';
|
||||
|
||||
// #sec-function-p1-p2-pn-body
|
||||
function FunctionConstructor(args, { NewTarget }) {
|
||||
// 1. Let C be the active function object.
|
||||
const C = surroundingAgent.activeFunctionObject;
|
||||
// 2. Let args be the argumentsList that was passed to this function by [[Call]] or [[Construct]].
|
||||
// 3. Return ? CreateDynamicFunction(C, NewTarget, normal, args).
|
||||
return Q(CreateDynamicFunction(C, NewTarget, 'normal', args));
|
||||
}
|
||||
|
||||
export function BootstrapFunction(realmRec) {
|
||||
const cons = BootstrapConstructor(realmRec, FunctionConstructor, 'Function', 1, realmRec.Intrinsics['%Function.prototype%'], []);
|
||||
realmRec.Intrinsics['%Function%'] = cons;
|
||||
}
|
||||
@@ -1,240 +0,0 @@
|
||||
import {
|
||||
surroundingAgent,
|
||||
HostHasSourceTextAvailable,
|
||||
} from '../engine.mjs';
|
||||
import {
|
||||
Assert,
|
||||
Call,
|
||||
Construct,
|
||||
CreateListFromArrayLike,
|
||||
Get,
|
||||
HasOwnProperty,
|
||||
IsCallable,
|
||||
IsConstructor,
|
||||
OrdinaryHasInstance,
|
||||
PrepareForTailCall,
|
||||
SameValue,
|
||||
SetFunctionLength,
|
||||
SetFunctionName,
|
||||
ToInteger,
|
||||
CreateBuiltinFunction,
|
||||
MakeBasicObject,
|
||||
} from '../abstract-ops/all.mjs';
|
||||
import {
|
||||
Type,
|
||||
Value,
|
||||
wellKnownSymbols,
|
||||
} from '../value.mjs';
|
||||
import { Q, X } from '../completion.mjs';
|
||||
import { assignProps } from './Bootstrap.mjs';
|
||||
|
||||
// #sec-properties-of-the-function-prototype-object
|
||||
function FunctionProto(_args, _meta) {
|
||||
// * accepts any arguments and returns undefined when invoked.
|
||||
return Value.undefined;
|
||||
}
|
||||
|
||||
// #sec-function.prototype.apply
|
||||
function FunctionProto_apply([thisArg = Value.undefined, argArray = Value.undefined], { thisValue }) {
|
||||
// 1. Let func be the this value.
|
||||
const func = thisValue;
|
||||
// 2. If IsCallable(func) is false, throw a TypeError exception.
|
||||
if (IsCallable(func) === Value.false) {
|
||||
return surroundingAgent.Throw('TypeError', 'NotAFunction', func);
|
||||
}
|
||||
// 3. If argArray is undefined or null, then
|
||||
if (argArray === Value.undefined || argArray === Value.null) {
|
||||
// a. Perform PrepareForTailCall().
|
||||
PrepareForTailCall();
|
||||
// b. Return ? Call(func, thisArg).
|
||||
return Q(Call(func, thisArg));
|
||||
}
|
||||
// 4. Let argList be ? CreateListFromArrayLike(argArray).
|
||||
const argList = Q(CreateListFromArrayLike(argArray));
|
||||
// 5. Perform PrepareForTailCall().
|
||||
PrepareForTailCall();
|
||||
// 6. Return ? Call(func, thisArg, argList).
|
||||
return Q(Call(func, thisArg, argList));
|
||||
}
|
||||
|
||||
function BoundFunctionExoticObjectCall(thisArgument, argumentsList) {
|
||||
const F = this;
|
||||
|
||||
const target = F.BoundTargetFunction;
|
||||
const boundThis = F.BoundThis;
|
||||
const boundArgs = F.BoundArguments;
|
||||
const args = [...boundArgs, ...argumentsList];
|
||||
return Q(Call(target, boundThis, args));
|
||||
}
|
||||
|
||||
function BoundFunctionExoticObjectConstruct(argumentsList, newTarget) {
|
||||
const F = this;
|
||||
|
||||
const target = F.BoundTargetFunction;
|
||||
Assert(IsConstructor(target) === Value.true);
|
||||
const boundArgs = F.BoundArguments;
|
||||
const args = [...boundArgs, ...argumentsList];
|
||||
if (SameValue(F, newTarget) === Value.true) {
|
||||
newTarget = target;
|
||||
}
|
||||
return Q(Construct(target, args, newTarget));
|
||||
}
|
||||
|
||||
// #sec-boundfunctioncreate
|
||||
function BoundFunctionCreate(targetFunction, boundThis, boundArgs) {
|
||||
// 1. Assert: Type(targetFunction) is Object.
|
||||
Assert(Type(targetFunction) === 'Object');
|
||||
// 2. Let proto be ? targetFunction.[[GetPrototypeOf]]().
|
||||
const proto = Q(targetFunction.GetPrototypeOf());
|
||||
// 3. Let internalSlotsList be the internal slots listed in Table 30, plus [[Prototype]] and [[Extensible]].
|
||||
const internalSlotsList = [
|
||||
'BoundTargetFunction',
|
||||
'BoundThis',
|
||||
'BoundArguments',
|
||||
'Prototype',
|
||||
'Extensible',
|
||||
];
|
||||
// 4. Let obj be ! MakeBasicObject(internalSlotsList).
|
||||
const obj = X(MakeBasicObject(internalSlotsList));
|
||||
// 5. Set obj.[[Prototype]] to proto.
|
||||
obj.Prototype = proto;
|
||||
// 6. Set obj.[[Call]] as described in 9.4.1.1.
|
||||
obj.Call = BoundFunctionExoticObjectCall;
|
||||
// 7. If IsConstructor(targetFunction) is true, then
|
||||
if (IsConstructor(targetFunction) === Value.true) {
|
||||
// a. Set obj.[[Construct]] as described in 9.4.1.2.
|
||||
obj.Construct = BoundFunctionExoticObjectConstruct;
|
||||
}
|
||||
// 8. Set obj.[[BoundTargetFunction]] to targetFunction.
|
||||
obj.BoundTargetFunction = targetFunction;
|
||||
// 9. Set obj.[[BoundThis]] to boundThis.
|
||||
obj.BoundThis = boundThis;
|
||||
// 10. Set obj.[[BoundArguments]] to boundArguments.
|
||||
obj.BoundArguments = boundArgs;
|
||||
// 11. Return obj.
|
||||
return obj;
|
||||
}
|
||||
|
||||
// #sec-function.prototype.bind
|
||||
function FunctionProto_bind([thisArg = Value.undefined, ...args], { thisValue }) {
|
||||
// 1. Let Target be the this value.
|
||||
const Target = thisValue;
|
||||
// 2. If IsCallable(Target) is false, throw a TypeError exception.
|
||||
if (IsCallable(Target) === Value.false) {
|
||||
return surroundingAgent.Throw('TypeError', 'NotAFunction', Target);
|
||||
}
|
||||
// 3. Let args be a new (possibly empty) List consisting of all of the argument values provided after thisArg in order.
|
||||
// 4. Let F be ? BoundFunctionCreate(Target, thisArg, args).
|
||||
const F = Q(BoundFunctionCreate(Target, thisArg, args));
|
||||
// 5. Let targetHasLength be ? HasOwnProperty(Target, "length").
|
||||
const targetHasLength = Q(HasOwnProperty(Target, new Value('length')));
|
||||
// 6. If targetHasLength is true, then
|
||||
let L;
|
||||
if (targetHasLength === Value.true) {
|
||||
// a. Let targetLen be ? Get(Target, "length").
|
||||
let targetLen = Q(Get(Target, new Value('length')));
|
||||
// b. If Type(targetLen) is not Number, let L be 0.
|
||||
if (Type(targetLen) !== 'Number') {
|
||||
L = 0;
|
||||
} else { // c. Else,
|
||||
// i. Set targetLen to ! ToInteger(targetLen).
|
||||
targetLen = Q(ToInteger(targetLen)).numberValue();
|
||||
// ii. Let L be the larger of 0 and the result of targetLen minus the number of elements of args.
|
||||
L = Math.max(0, targetLen - args.length);
|
||||
}
|
||||
} else {
|
||||
// 7. ELse, let L be 0.
|
||||
L = 0;
|
||||
}
|
||||
// 8. Perform ! SetFunctionLength(F, L).
|
||||
X(SetFunctionLength(F, new Value(L)));
|
||||
// 9. Let targetName be ? Get(Target, "name").
|
||||
let targetName = Q(Get(Target, new Value('name')));
|
||||
// 10. If Type(targetName) is not String, set targetName to the empty String.
|
||||
if (Type(targetName) !== 'String') {
|
||||
targetName = new Value('');
|
||||
}
|
||||
// 11. Perform SetFunctionName(F, targetName, "bound").
|
||||
SetFunctionName(F, targetName, new Value('bound'));
|
||||
// 12. Return F.
|
||||
return F;
|
||||
}
|
||||
|
||||
// #sec-function.prototype.call
|
||||
function FunctionProto_call([thisArg = Value.undefined, ...args], { thisValue }) {
|
||||
// 1. Let func be the this value.
|
||||
const func = thisValue;
|
||||
// 2. If IsCallable(func) is false, throw a TypeError exception.
|
||||
if (IsCallable(func) === Value.false) {
|
||||
return surroundingAgent.Throw('TypeError', 'NotAFunction', func);
|
||||
}
|
||||
// 3. Let argList be a new empty List.
|
||||
const argList = [];
|
||||
// 4. If this method was called with more than one argument, then in left to right order, starting with the second argument, append each argument as the last element of argList.
|
||||
for (const arg of args) {
|
||||
argList.push(arg);
|
||||
}
|
||||
// 5. Perform PrepareForTailCall().
|
||||
PrepareForTailCall();
|
||||
// 6. Return ? Call(func, thisArg, argList).
|
||||
return Q(Call(func, thisArg, argList));
|
||||
}
|
||||
|
||||
// #sec-function.prototype.tostring
|
||||
function FunctionProto_toString(args, { thisValue }) {
|
||||
// 1. Let func be the this value.
|
||||
const func = thisValue;
|
||||
// 2. If func is a built-in function object, then return an implementation-defined
|
||||
// String source code representation of func. The representation must have the
|
||||
// syntax of a NativeFunction. Additionally, if func has an [[InitialName]] internal
|
||||
// slot and func.[[InitialName]] is a String, the portion of the returned String
|
||||
// that would be matched by `NativeFunctionAccessor? PropertyName` must be the
|
||||
// value of func.[[InitialName]].
|
||||
if ('nativeFunction' in func) {
|
||||
if (func.InitialName !== Value.null) {
|
||||
return new Value(`function ${func.InitialName.stringValue()}() { [native code] }`);
|
||||
}
|
||||
return new Value('function() { [native code] }');
|
||||
}
|
||||
// 3. If Type(func) is Object and func has a [[SourceText]] internal slot and func.[[SourceText]]
|
||||
// is a sequence of Unicode code points and ! HostHasSourceTextAvailable(func) is true, then
|
||||
if (Type(func) === 'Object'
|
||||
&& 'SourceText' in func
|
||||
&& X(HostHasSourceTextAvailable(func)) === Value.true) {
|
||||
// Return ! UTF16Encode(func.[[SourceText]]).
|
||||
return new Value(func.SourceText);
|
||||
}
|
||||
// 4. If Type(func) is Object and IsCallable(func) is true, then return an implementation
|
||||
// dependent String source code representation of func. The representation must have
|
||||
// the syntax of a NativeFunction.
|
||||
if (Type(func) === 'Object' && IsCallable(func) === Value.true) {
|
||||
return new Value('function() { [native code] }');
|
||||
}
|
||||
// 5. Throw a TypeError exception.
|
||||
return surroundingAgent.Throw('TypeError', 'NotAFunction', func);
|
||||
}
|
||||
|
||||
// #sec-function.prototype-@@hasinstance
|
||||
function FunctionProto_hasInstance([V = Value.undefined], { thisValue }) {
|
||||
// 1. Let F be this value.
|
||||
const F = thisValue;
|
||||
// 2. Return ? OrdinaryHasInstance(F, V).
|
||||
return Q(OrdinaryHasInstance(F, V));
|
||||
}
|
||||
|
||||
export function BootstrapFunctionPrototype(realmRec) {
|
||||
const proto = CreateBuiltinFunction(FunctionProto, [], realmRec, realmRec.Intrinsics['%Object.prototype%']);
|
||||
realmRec.Intrinsics['%Function.prototype%'] = proto;
|
||||
|
||||
SetFunctionLength(proto, new Value(0));
|
||||
SetFunctionName(proto, new Value(''));
|
||||
|
||||
const readonly = { Writable: Value.false, Configurable: Value.false };
|
||||
assignProps(realmRec, proto, [
|
||||
['apply', FunctionProto_apply, 2],
|
||||
['bind', FunctionProto_bind, 1],
|
||||
['call', FunctionProto_call, 1],
|
||||
['toString', FunctionProto_toString, 0],
|
||||
[wellKnownSymbols.hasInstance, FunctionProto_hasInstance, 1, readonly],
|
||||
]);
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
import { Descriptor, Value } from '../value.mjs';
|
||||
import { DefinePropertyOrThrow } from '../abstract-ops/all.mjs';
|
||||
import { X } from '../completion.mjs';
|
||||
import { BootstrapPrototype } from './Bootstrap.mjs';
|
||||
|
||||
export function BootstrapGenerator(realmRec) {
|
||||
const generatorPrototype = realmRec.Intrinsics['%Generator.prototype%'];
|
||||
|
||||
const generator = BootstrapPrototype(realmRec, [
|
||||
['prototype', generatorPrototype, undefined, { Writable: Value.false }],
|
||||
], realmRec.Intrinsics['%Function.prototype%'], 'GeneratorFunction');
|
||||
|
||||
X(DefinePropertyOrThrow(generatorPrototype, new Value('constructor'), Descriptor({
|
||||
Value: generator,
|
||||
Writable: Value.false,
|
||||
Enumerable: Value.false,
|
||||
Configurable: Value.true,
|
||||
})));
|
||||
|
||||
realmRec.Intrinsics['%Generator%'] = generator;
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
import { surroundingAgent } from '../engine.mjs';
|
||||
import { Descriptor, Value } from '../value.mjs';
|
||||
import { DefinePropertyOrThrow } from '../abstract-ops/all.mjs';
|
||||
import { Q, X } from '../completion.mjs';
|
||||
import { CreateDynamicFunction } from '../runtime-semantics/all.mjs';
|
||||
import { BootstrapConstructor } from './Bootstrap.mjs';
|
||||
|
||||
// #sec-generatorfunction
|
||||
function GeneratorFunctionConstructor(args, { NewTarget }) {
|
||||
// 1. Let C be the active function object.
|
||||
const C = surroundingAgent.activeFunctionObject;
|
||||
// 2. Let args be the argumentsList that was passed to this function by [[Call]] or [[Construct]].
|
||||
// 3. Return ? CreateDynamicFunction(C, NewTarget, generator, args).
|
||||
return Q(CreateDynamicFunction(C, NewTarget, 'generator', args));
|
||||
}
|
||||
|
||||
export function BootstrapGeneratorFunction(realmRec) {
|
||||
const generator = realmRec.Intrinsics['%Generator%'];
|
||||
|
||||
const cons = BootstrapConstructor(realmRec, GeneratorFunctionConstructor, 'GeneratorFunction', 1, generator, []);
|
||||
X(DefinePropertyOrThrow(cons, new Value('prototype'), Descriptor({
|
||||
Writable: Value.false,
|
||||
Configurable: Value.false,
|
||||
})));
|
||||
X(DefinePropertyOrThrow(generator, new Value('constructor'), Descriptor({
|
||||
Writable: Value.false,
|
||||
})));
|
||||
|
||||
realmRec.Intrinsics['%GeneratorFunction%'] = cons;
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
import {
|
||||
GeneratorResume,
|
||||
GeneratorResumeAbrupt,
|
||||
} from '../abstract-ops/all.mjs';
|
||||
import {
|
||||
Completion,
|
||||
ThrowCompletion,
|
||||
Q,
|
||||
} from '../completion.mjs';
|
||||
import { Value } from '../value.mjs';
|
||||
import { BootstrapPrototype } from './Bootstrap.mjs';
|
||||
|
||||
// #sec-generator.prototype.next
|
||||
function GeneratorProto_next([value = Value.undefined], { thisValue }) {
|
||||
// 1. Let g be the this value.
|
||||
const g = thisValue;
|
||||
// 2. Return ? GeneratorResume(g, value).
|
||||
return Q(GeneratorResume(g, value));
|
||||
}
|
||||
|
||||
// #sec-generator.prototype.return
|
||||
function GeneratorProto_return([value = Value.undefined], { thisValue }) {
|
||||
// 1. Let g be the this value.
|
||||
const g = thisValue;
|
||||
// 2. Let C be Completion { [[Type]]: return, [[Value]]: value, [[Target]]: empty }.
|
||||
const C = new Completion({ Type: 'return', Value: value, Target: undefined });
|
||||
// 3. Return ? GeneratorResumeAbrupt(g, C).
|
||||
return Q(GeneratorResumeAbrupt(g, C));
|
||||
}
|
||||
|
||||
// #sec-generator.prototype.throw
|
||||
function GeneratorProto_throw([exception = Value.undefined], { thisValue }) {
|
||||
// 1. Let g be the this value.
|
||||
const g = thisValue;
|
||||
// 2. Let C be ThrowCompletion(exception).
|
||||
const C = ThrowCompletion(exception);
|
||||
// 3. Return ? GeneratorResumeAbrupt(g, C).
|
||||
return Q(GeneratorResumeAbrupt(g, C));
|
||||
}
|
||||
|
||||
export function BootstrapGeneratorPrototype(realmRec) {
|
||||
const generatorPrototype = BootstrapPrototype(realmRec, [
|
||||
['next', GeneratorProto_next, 1],
|
||||
['return', GeneratorProto_return, 1],
|
||||
['throw', GeneratorProto_throw, 1],
|
||||
], realmRec.Intrinsics['%IteratorPrototype%'], 'Generator');
|
||||
|
||||
realmRec.Intrinsics['%Generator.prototype%'] = generatorPrototype;
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
import { wellKnownSymbols } from '../value.mjs';
|
||||
import { BootstrapPrototype } from './Bootstrap.mjs';
|
||||
|
||||
// #sec-%iteratorprototype%-@@iterator
|
||||
function IteratorPrototype_iterator(args, { thisValue }) {
|
||||
// 1. Return this value.
|
||||
return thisValue;
|
||||
}
|
||||
|
||||
export function BootstrapIteratorPrototype(realmRec) {
|
||||
const proto = BootstrapPrototype(realmRec, [
|
||||
[wellKnownSymbols.iterator, IteratorPrototype_iterator, 0],
|
||||
], realmRec.Intrinsics['%Object.prototype%']);
|
||||
|
||||
realmRec.Intrinsics['%IteratorPrototype%'] = proto;
|
||||
}
|
||||
@@ -1,548 +0,0 @@
|
||||
import { surroundingAgent } from '../engine.mjs';
|
||||
import {
|
||||
BooleanValue,
|
||||
NullValue,
|
||||
NumberValue,
|
||||
ObjectValue,
|
||||
JSStringValue,
|
||||
Type,
|
||||
Value,
|
||||
} from '../value.mjs';
|
||||
import {
|
||||
Assert,
|
||||
Call,
|
||||
CreateDataProperty,
|
||||
CreateDataPropertyOrThrow,
|
||||
EnumerableOwnPropertyNames,
|
||||
Get,
|
||||
GetV,
|
||||
IsArray,
|
||||
IsCallable,
|
||||
OrdinaryObjectCreate,
|
||||
LengthOfArrayLike,
|
||||
ToInteger,
|
||||
ToNumber,
|
||||
ToString,
|
||||
} from '../abstract-ops/all.mjs';
|
||||
import {
|
||||
isLeadingSurrogate,
|
||||
isTrailingSurrogate,
|
||||
} from '../parser/Lexer.mjs';
|
||||
import {
|
||||
CodePointToUTF16CodeUnits,
|
||||
} from '../static-semantics/all.mjs';
|
||||
import {
|
||||
NormalCompletion,
|
||||
Q, X,
|
||||
} from '../completion.mjs';
|
||||
import { ValueSet } from '../helpers.mjs';
|
||||
import { evaluateScript } from '../api.mjs';
|
||||
import { BootstrapPrototype } from './Bootstrap.mjs';
|
||||
|
||||
const WHITESPACE = [' ', '\t', '\r', '\n'];
|
||||
const NUMERIC = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
|
||||
const VALID_HEX = [...NUMERIC, 'A', 'B', 'C', 'D', 'E', 'F', 'a', 'b', 'c', 'd', 'e', 'f'];
|
||||
const ESCAPABLE = ['"', '\\', '/', 'b', 'f', 'n', 'r', 't'];
|
||||
|
||||
class JSONValidator {
|
||||
constructor(input) {
|
||||
this.input = input;
|
||||
this.pos = 0;
|
||||
this.char = input.charAt(0);
|
||||
}
|
||||
|
||||
validate() {
|
||||
X(this.eatWhitespace());
|
||||
Q(this.parseValue());
|
||||
if (this.pos < this.input.length) {
|
||||
return surroundingAgent.Throw('SyntaxError', 'JSONUnexpectedToken');
|
||||
}
|
||||
return NormalCompletion(undefined);
|
||||
}
|
||||
|
||||
advance() {
|
||||
this.pos += 1;
|
||||
if (this.pos === this.input.length) {
|
||||
this.char = null;
|
||||
} else if (this.pos > this.input.length) {
|
||||
return surroundingAgent.Throw('SyntaxError', 'JSONUnexpectedToken');
|
||||
} else {
|
||||
this.char = this.input.charAt(this.pos);
|
||||
}
|
||||
return this.char;
|
||||
}
|
||||
|
||||
eatWhitespace() {
|
||||
while (this.eat(WHITESPACE)) {
|
||||
// nothing
|
||||
}
|
||||
}
|
||||
|
||||
eat(c) {
|
||||
if (Array.isArray(c) && c.includes(this.char)) {
|
||||
X(this.advance());
|
||||
return true;
|
||||
} else if (this.char === c) {
|
||||
X(this.advance());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
expect(c) {
|
||||
const { char } = this;
|
||||
if (!this.eat(c)) {
|
||||
return surroundingAgent.Throw('SyntaxError', 'JSONExpected', c, this.char);
|
||||
}
|
||||
return char;
|
||||
}
|
||||
|
||||
parseValue() {
|
||||
switch (this.char) {
|
||||
case '"':
|
||||
return Q(this.parseString());
|
||||
case '{':
|
||||
return Q(this.parseObject());
|
||||
case '[':
|
||||
return Q(this.parseArray());
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
case '-':
|
||||
return Q(this.parseNumber());
|
||||
case 'f':
|
||||
X(this.expect('f'));
|
||||
Q(this.expect('a'));
|
||||
Q(this.expect('l'));
|
||||
Q(this.expect('s'));
|
||||
Q(this.expect('e'));
|
||||
return X(this.eatWhitespace());
|
||||
case 't':
|
||||
X(this.expect('t'));
|
||||
Q(this.expect('r'));
|
||||
Q(this.expect('u'));
|
||||
Q(this.expect('e'));
|
||||
return X(this.eatWhitespace());
|
||||
case 'n':
|
||||
X(this.expect('n'));
|
||||
Q(this.expect('u'));
|
||||
Q(this.expect('l'));
|
||||
Q(this.expect('l'));
|
||||
return X(this.eatWhitespace());
|
||||
default:
|
||||
return surroundingAgent.Throw('SyntaxError', 'JSONUnexpectedChar', this.char);
|
||||
}
|
||||
}
|
||||
|
||||
parseString() {
|
||||
Q(this.expect('"'));
|
||||
while (!this.eat('"')) {
|
||||
if (this.eat('\\')) {
|
||||
if (!this.eat(ESCAPABLE)) {
|
||||
Q(this.expect('u'));
|
||||
Q(this.expect(VALID_HEX));
|
||||
Q(this.expect(VALID_HEX));
|
||||
Q(this.expect(VALID_HEX));
|
||||
Q(this.expect(VALID_HEX));
|
||||
}
|
||||
} else {
|
||||
if (this.char < ' ') {
|
||||
return surroundingAgent.Throw('SyntaxError', 'JSONUnexpectedChar', this.char);
|
||||
}
|
||||
Q(this.advance());
|
||||
}
|
||||
}
|
||||
return X(this.eatWhitespace());
|
||||
}
|
||||
|
||||
parseNumber() {
|
||||
this.eat('-');
|
||||
if (!this.eat('0')) {
|
||||
Q(this.expect(NUMERIC));
|
||||
while (this.eat(NUMERIC)) {
|
||||
// nothing
|
||||
}
|
||||
}
|
||||
if (this.eat('.')) {
|
||||
Q(this.expect(NUMERIC));
|
||||
while (this.eat(NUMERIC)) {
|
||||
// nothing
|
||||
}
|
||||
}
|
||||
if (this.eat(['e', 'E'])) {
|
||||
this.eat(['-', '+']);
|
||||
Q(this.expect(NUMERIC));
|
||||
while (this.eat(NUMERIC)) {
|
||||
// nothing
|
||||
}
|
||||
}
|
||||
X(this.eatWhitespace());
|
||||
}
|
||||
|
||||
parseObject() {
|
||||
Q(this.expect('{'));
|
||||
X(this.eatWhitespace());
|
||||
let first = true;
|
||||
while (!this.eat('}')) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
Q(this.expect(','));
|
||||
X(this.eatWhitespace());
|
||||
}
|
||||
Q(this.parseString());
|
||||
X(this.eatWhitespace());
|
||||
Q(this.expect(':'));
|
||||
X(this.eatWhitespace());
|
||||
Q(this.parseValue());
|
||||
X(this.eatWhitespace());
|
||||
}
|
||||
X(this.eatWhitespace());
|
||||
}
|
||||
|
||||
parseArray() {
|
||||
Q(this.expect('['));
|
||||
X(this.eatWhitespace());
|
||||
let first = true;
|
||||
while (!this.eat(']')) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
Q(this.expect(','));
|
||||
X(this.eatWhitespace());
|
||||
}
|
||||
Q(this.parseValue());
|
||||
X(this.eatWhitespace());
|
||||
}
|
||||
X(this.eatWhitespace());
|
||||
}
|
||||
|
||||
static validate(input) {
|
||||
const v = new JSONValidator(input);
|
||||
return v.validate();
|
||||
}
|
||||
}
|
||||
|
||||
function InternalizeJSONProperty(holder, name, reviver) {
|
||||
const val = Q(Get(holder, name));
|
||||
if (Type(val) === 'Object') {
|
||||
const isArray = Q(IsArray(val));
|
||||
if (isArray === Value.true) {
|
||||
let I = 0;
|
||||
const len = Q(LengthOfArrayLike(val)).numberValue();
|
||||
while (I < len) {
|
||||
const Istr = X(ToString(new Value(I)));
|
||||
const newElement = Q(InternalizeJSONProperty(val, Istr, reviver));
|
||||
if (Type(newElement) === 'Undefined') {
|
||||
Q(val.Delete(Istr));
|
||||
} else {
|
||||
Q(CreateDataProperty(val, Istr, newElement));
|
||||
}
|
||||
I += 1;
|
||||
}
|
||||
} else {
|
||||
const keys = Q(EnumerableOwnPropertyNames(val, 'key'));
|
||||
for (const P of keys) {
|
||||
const newElement = Q(InternalizeJSONProperty(val, P, reviver));
|
||||
if (Type(newElement) === 'Undefined') {
|
||||
Q(val.Delete(P));
|
||||
} else {
|
||||
Q(CreateDataProperty(val, P, newElement));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return Q(Call(reviver, holder, [name, val]));
|
||||
}
|
||||
|
||||
// #sec-json.parse
|
||||
function JSON_parse([text = Value.undefined, reviver = Value.undefined]) {
|
||||
// 1. Let jsonString be ? ToString(text).
|
||||
const jsonString = Q(ToString(text));
|
||||
// 2. Parse ! UTF16DecodeString(jsonString) as a JSON text as specified in ECMA-404.
|
||||
// Throw a SyntaxError exception if it is not a valid JSON text as defined in that specification.
|
||||
Q(JSONValidator.validate(jsonString.stringValue()));
|
||||
// 3. Let scriptString be the string-concatenation of "(", jsonString, and ");".
|
||||
const scriptString = `(${jsonString.stringValue()});`;
|
||||
// 4. Let completion be the result of parsing and evaluating
|
||||
// ! UTF16DecodeString(scriptString) as if it was the source text of an ECMAScript Script. The
|
||||
// extended PropertyDefinitionEvaluation semantics defined in B.3.1 must not be used during the evaluation.
|
||||
const completion = evaluateScript(scriptString, surroundingAgent.currentRealmRecord);
|
||||
// 5. Let unfiltered be completion.[[Value]].
|
||||
const unfiltered = completion.Value;
|
||||
// 6. Assert: unfiltered is either a String, Number, Boolean, Null, or an Object that is defined by either an ArrayLiteral or an ObjectLiteral.
|
||||
Assert(unfiltered instanceof JSStringValue
|
||||
|| unfiltered instanceof NumberValue
|
||||
|| unfiltered instanceof BooleanValue
|
||||
|| unfiltered instanceof NullValue
|
||||
|| unfiltered instanceof ObjectValue);
|
||||
// 7. If IsCallable(reviver) is true, then
|
||||
if (IsCallable(reviver) === Value.true) {
|
||||
// a. Let root be OrdinaryObjectCreate(%Object.prototype%).
|
||||
const root = OrdinaryObjectCreate(surroundingAgent.intrinsic('%Object.prototype%'));
|
||||
// b. Let rootName be the empty String.
|
||||
const rootName = new Value('');
|
||||
// c. Perform ! CreateDataPropertyOrThrow(root, rootName, unfiltered).
|
||||
X(CreateDataPropertyOrThrow(root, rootName, unfiltered));
|
||||
// d. Return ? InternalizeJSONProperty(root, rootName, reviver).
|
||||
return Q(InternalizeJSONProperty(root, rootName, reviver));
|
||||
} else {
|
||||
// a. Return unfiltered.
|
||||
return unfiltered;
|
||||
}
|
||||
}
|
||||
|
||||
const codeUnitTable = new Map([
|
||||
[0x0008, '\\b'],
|
||||
[0x0009, '\\t'],
|
||||
[0x000A, '\\n'],
|
||||
[0x000C, '\\f'],
|
||||
[0x000D, '\\r'],
|
||||
[0x0022, '\\"'],
|
||||
[0x005C, '\\\\'],
|
||||
]);
|
||||
|
||||
// #sec-serializejsonproperty
|
||||
function SerializeJSONProperty(state, key, holder) {
|
||||
let value = Q(Get(holder, key)); // eslint-disable-line no-shadow
|
||||
if (Type(value) === 'Object' || Type(value) === 'BigInt') {
|
||||
const toJSON = Q(GetV(value, new Value('toJSON')));
|
||||
if (IsCallable(toJSON) === Value.true) {
|
||||
value = Q(Call(toJSON, value, [key]));
|
||||
}
|
||||
}
|
||||
if (state.ReplacerFunction !== Value.undefined) {
|
||||
value = Q(Call(state.ReplacerFunction, holder, [key, value]));
|
||||
}
|
||||
if (Type(value) === 'Object') {
|
||||
if ('NumberData' in value) {
|
||||
value = Q(ToNumber(value));
|
||||
} else if ('StringData' in value) {
|
||||
value = Q(ToString(value));
|
||||
} else if ('BooleanData' in value) {
|
||||
value = value.BooleanData;
|
||||
} else if ('BigIntData' in value) {
|
||||
value = value.BigIntData;
|
||||
}
|
||||
}
|
||||
if (value === Value.null) {
|
||||
return new Value('null');
|
||||
}
|
||||
if (value === Value.true) {
|
||||
return new Value('true');
|
||||
}
|
||||
if (value === Value.false) {
|
||||
return new Value('false');
|
||||
}
|
||||
if (Type(value) === 'String') {
|
||||
return QuoteJSONString(value);
|
||||
}
|
||||
if (Type(value) === 'Number') {
|
||||
if (value.isFinite()) {
|
||||
return X(ToString(value));
|
||||
}
|
||||
return new Value('null');
|
||||
}
|
||||
if (Type(value) === 'BigInt') {
|
||||
return surroundingAgent.Throw('TypeError', 'CannotJSONSerializeBigInt');
|
||||
}
|
||||
if (Type(value) === 'Object' && IsCallable(value) === Value.false) {
|
||||
const isArray = Q(IsArray(value));
|
||||
if (isArray === Value.true) {
|
||||
return Q(SerializeJSONArray(state, value));
|
||||
}
|
||||
return Q(SerializeJSONObject(state, value));
|
||||
}
|
||||
return Value.undefined;
|
||||
}
|
||||
|
||||
function UnicodeEscape(C) {
|
||||
const n = C.charCodeAt(0);
|
||||
Assert(n < 0xFFFF);
|
||||
return `\u005Cu${n.toString(16).padStart(4, '0')}`;
|
||||
}
|
||||
|
||||
function QuoteJSONString(value) { // eslint-disable-line no-shadow
|
||||
let product = '\u0022';
|
||||
const cpList = [...value.stringValue()].map((c) => c.codePointAt(0));
|
||||
for (const C of cpList) {
|
||||
if (codeUnitTable.has(C)) {
|
||||
product = `${product}${codeUnitTable.get(C)}`;
|
||||
} else if (C < 0x0020 || isLeadingSurrogate(C) || isTrailingSurrogate(C)) {
|
||||
const unit = String.fromCodePoint(C);
|
||||
product = `${product}${UnicodeEscape(unit)}`;
|
||||
} else {
|
||||
product = `${product}${String.fromCodePoint(...CodePointToUTF16CodeUnits(C))}`;
|
||||
}
|
||||
}
|
||||
product = `${product}\u0022`;
|
||||
return new Value(product);
|
||||
}
|
||||
|
||||
// #sec-serializejsonobject
|
||||
function SerializeJSONObject(state, value) {
|
||||
if (state.Stack.includes(value)) {
|
||||
return surroundingAgent.Throw('TypeError', 'JSONCircular');
|
||||
}
|
||||
state.Stack.push(value);
|
||||
const stepback = state.Indent;
|
||||
state.Indent = `${state.Indent}${state.Gap}`;
|
||||
let K;
|
||||
if (state.PropertyList !== Value.undefined) {
|
||||
K = state.PropertyList;
|
||||
} else {
|
||||
K = Q(EnumerableOwnPropertyNames(value, 'key'));
|
||||
}
|
||||
const partial = [];
|
||||
for (const P of K) {
|
||||
const strP = Q(SerializeJSONProperty(state, P, value));
|
||||
if (strP !== Value.undefined) {
|
||||
let member = QuoteJSONString(P).stringValue();
|
||||
member = `${member}:`;
|
||||
if (state.Gap !== '') {
|
||||
member = `${member} `;
|
||||
}
|
||||
member = `${member}${strP.stringValue()}`;
|
||||
partial.push(member);
|
||||
}
|
||||
}
|
||||
let final;
|
||||
if (partial.length === 0) {
|
||||
final = new Value('{}');
|
||||
} else {
|
||||
if (state.Gap === '') {
|
||||
const properties = partial.join(',');
|
||||
final = new Value(`{${properties}}`);
|
||||
} else {
|
||||
const separator = `,\u000A${state.Indent}`;
|
||||
const properties = partial.join(separator);
|
||||
final = new Value(`{\u000A${state.Indent}${properties}\u000A${stepback}}`);
|
||||
}
|
||||
}
|
||||
state.Stack.pop();
|
||||
state.Indent = stepback;
|
||||
return final;
|
||||
}
|
||||
|
||||
// #sec-serializejsonarray
|
||||
function SerializeJSONArray(state, value) {
|
||||
if (state.Stack.includes(value)) {
|
||||
return surroundingAgent.Throw('TypeError', 'JSONCircular');
|
||||
}
|
||||
state.Stack.push(value);
|
||||
const stepback = state.Indent;
|
||||
state.Indent = `${state.Indent}${state.Gap}`;
|
||||
const partial = [];
|
||||
const len = Q(LengthOfArrayLike(value)).numberValue();
|
||||
let index = 0;
|
||||
while (index < len) {
|
||||
const indexStr = X(ToString(new Value(index)));
|
||||
const strP = Q(SerializeJSONProperty(state, indexStr, value));
|
||||
if (strP === Value.undefined) {
|
||||
partial.push('null');
|
||||
} else {
|
||||
partial.push(strP.stringValue());
|
||||
}
|
||||
index += 1;
|
||||
}
|
||||
let final;
|
||||
if (partial.length === 0) {
|
||||
final = new Value('[]');
|
||||
} else {
|
||||
if (state.Gap === '') {
|
||||
const properties = partial.join(',');
|
||||
final = new Value(`[${properties}]`);
|
||||
} else {
|
||||
const separator = `,\u000A${state.Indent}`;
|
||||
const properties = partial.join(separator);
|
||||
final = new Value(`[\u000A${state.Indent}${properties}\u000A${stepback}]`);
|
||||
}
|
||||
}
|
||||
state.Stack.pop();
|
||||
state.Indent = stepback;
|
||||
return final;
|
||||
}
|
||||
|
||||
// #sec-json.stringify
|
||||
function JSON_stringify([value = Value.undefined, replacer = Value.undefined, space = Value.undefined]) {
|
||||
const stack = [];
|
||||
const indent = '';
|
||||
let PropertyList = Value.undefined;
|
||||
let ReplacerFunction = Value.undefined;
|
||||
if (Type(replacer) === 'Object') {
|
||||
if (IsCallable(replacer) === Value.true) {
|
||||
ReplacerFunction = replacer;
|
||||
} else {
|
||||
const isArray = Q(IsArray(replacer));
|
||||
if (isArray === Value.true) {
|
||||
PropertyList = new ValueSet();
|
||||
const len = Q(LengthOfArrayLike(replacer)).numberValue();
|
||||
let k = 0;
|
||||
while (k < len) {
|
||||
const vStr = X(ToString(new Value(k)));
|
||||
const v = Q(Get(replacer, vStr));
|
||||
let item = Value.undefined;
|
||||
if (Type(v) === 'String') {
|
||||
item = v;
|
||||
} else if (Type(v) === 'Number') {
|
||||
item = X(ToString(v));
|
||||
} else if (Type(v) === 'Object') {
|
||||
if ('StringData' in v || 'NumberData' in v) {
|
||||
item = Q(ToString(v));
|
||||
}
|
||||
}
|
||||
if (item !== Value.undefined && !PropertyList.has(item)) {
|
||||
PropertyList.add(item);
|
||||
}
|
||||
k += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Type(space) === 'Object') {
|
||||
if ('NumberData' in space) {
|
||||
space = Q(ToNumber(space));
|
||||
} else if ('StringData' in space) {
|
||||
space = Q(ToString(space));
|
||||
}
|
||||
}
|
||||
let gap;
|
||||
if (Type(space) === 'Number') {
|
||||
space = Math.min(10, X(ToInteger(space)).numberValue());
|
||||
if (space < 1) {
|
||||
gap = '';
|
||||
} else {
|
||||
gap = ' '.repeat(space);
|
||||
}
|
||||
} else if (Type(space) === 'String') {
|
||||
if (space.stringValue().length <= 10) {
|
||||
gap = space.stringValue();
|
||||
} else {
|
||||
gap = space.stringValue().slice(0, 10);
|
||||
}
|
||||
} else {
|
||||
gap = '';
|
||||
}
|
||||
const wrapper = OrdinaryObjectCreate(surroundingAgent.intrinsic('%Object.prototype%'));
|
||||
X(CreateDataPropertyOrThrow(wrapper, new Value(''), value));
|
||||
const state = {
|
||||
ReplacerFunction, Stack: stack, Indent: indent, Gap: gap, PropertyList,
|
||||
};
|
||||
return Q(SerializeJSONProperty(state, new Value(''), wrapper));
|
||||
}
|
||||
|
||||
export function BootstrapJSON(realmRec) {
|
||||
const json = BootstrapPrototype(realmRec, [
|
||||
['parse', JSON_parse, 2],
|
||||
['stringify', JSON_stringify, 3],
|
||||
], realmRec.Intrinsics['%Object.prototype%'], 'JSON');
|
||||
|
||||
realmRec.Intrinsics['%JSON%'] = json;
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
import { surroundingAgent } from '../engine.mjs';
|
||||
import {
|
||||
Assert,
|
||||
Call,
|
||||
Get,
|
||||
GetIterator,
|
||||
IsCallable,
|
||||
IteratorClose,
|
||||
IteratorStep,
|
||||
IteratorValue,
|
||||
OrdinaryCreateFromConstructor,
|
||||
} from '../abstract-ops/all.mjs';
|
||||
import {
|
||||
Type,
|
||||
Value,
|
||||
wellKnownSymbols,
|
||||
} from '../value.mjs';
|
||||
import {
|
||||
AbruptCompletion,
|
||||
Q,
|
||||
} from '../completion.mjs';
|
||||
import { BootstrapConstructor } from './Bootstrap.mjs';
|
||||
|
||||
export function AddEntriesFromIterable(target, iterable, adder) {
|
||||
if (IsCallable(adder) === Value.false) {
|
||||
return surroundingAgent.Throw('TypeError', 'NotAFunction', adder);
|
||||
}
|
||||
Assert(iterable !== undefined && iterable !== Value.undefined && iterable !== Value.null);
|
||||
const iteratorRecord = Q(GetIterator(iterable));
|
||||
while (true) {
|
||||
const next = Q(IteratorStep(iteratorRecord));
|
||||
if (next === Value.false) {
|
||||
return target;
|
||||
}
|
||||
const nextItem = Q(IteratorValue(next));
|
||||
if (Type(nextItem) !== 'Object') {
|
||||
const error = surroundingAgent.Throw('TypeError', 'NotAnObject', nextItem);
|
||||
return Q(IteratorClose(iteratorRecord, error));
|
||||
}
|
||||
const k = Get(nextItem, new Value('0'));
|
||||
if (k instanceof AbruptCompletion) {
|
||||
return Q(IteratorClose(iteratorRecord, k));
|
||||
}
|
||||
const v = Get(nextItem, new Value('1'));
|
||||
if (v instanceof AbruptCompletion) {
|
||||
return Q(IteratorClose(iteratorRecord, v));
|
||||
}
|
||||
const status = Call(adder, target, [k.Value, v.Value]);
|
||||
if (status instanceof AbruptCompletion) {
|
||||
return Q(IteratorClose(iteratorRecord, status));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// #sec-map-iterable
|
||||
function MapConstructor([iterable = Value.undefined], { NewTarget }) {
|
||||
// 1. If NewTarget is undefined, throw a TypeError exception.
|
||||
if (NewTarget === Value.undefined) {
|
||||
return surroundingAgent.Throw('TypeError', 'ConstructorNonCallable', this);
|
||||
}
|
||||
// 2. Let map be ? OrdinaryCreateFromConstructor(NewTarget, "%Map.prototype%", « [[MapData]] »).
|
||||
const map = Q(OrdinaryCreateFromConstructor(NewTarget, '%Map.prototype%', ['MapData']));
|
||||
// 3. Set map.[[MapData]] to a new empty List.
|
||||
map.MapData = [];
|
||||
// 4. If iterable is either undefined or null, return map.
|
||||
if (iterable === Value.undefined || iterable === Value.null) {
|
||||
return map;
|
||||
}
|
||||
// 5. Let adder be ? Get(map, "set").
|
||||
const adder = Q(Get(map, new Value('set')));
|
||||
// 6. Return ? AddEntriesFromIterable(map, iterable, adder).
|
||||
return Q(AddEntriesFromIterable(map, iterable, adder));
|
||||
}
|
||||
|
||||
// #sec-get-map-@@species
|
||||
function Map_speciesGetter(args, { thisValue }) {
|
||||
// 1. Return the this value.
|
||||
return thisValue;
|
||||
}
|
||||
|
||||
export function BootstrapMap(realmRec) {
|
||||
const mapConstructor = BootstrapConstructor(realmRec, MapConstructor, 'Map', 0, realmRec.Intrinsics['%Map.prototype%'], [
|
||||
[wellKnownSymbols.species, [Map_speciesGetter]],
|
||||
]);
|
||||
|
||||
realmRec.Intrinsics['%Map%'] = mapConstructor;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user